123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Layout::IndentationWidth

Overview

Checks for indentation that doesn’t use the specified number of spaces.

See also the IndentationConsistency cop which is the companion to this one.

Examples:

# bad
class A
 def test
  puts 'hello'
 end
end

# good
class A
  def test
    puts 'hello'
  end
end

AllowedPatterns: ['^\s*module']

# bad
module A
class B
  def test
  puts 'hello'
  end
end
end

# good
module A
class B
  def test
    puts 'hello'
  end
end
end

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

::RuboCop::Cop::ConfigurableEnforcedStyle - Included

SYMBOL_TO_STRING_CACHE

::RuboCop::Cop::RangeHelp - Included

BYTE_ORDER_MARK, NOT_GIVEN

::RuboCop::Cop::EndKeywordAlignment - Included

MSG

::RuboCop::Cop::Alignment - Included

SPACE

Class Attribute Summary

::RuboCop::Cop::AutoCorrector - Extended

::RuboCop::Cop::Base - Inherited

.gem_requirements, .lint?,
.support_autocorrect?

Returns if class supports autocorrect.

.support_multiple_source?

Override if your cop should be called repeatedly for multiple investigations Between calls to on_new_investigation and on_investigation_end, the result of processed_source will remain constant.

.builtin?

Class Method Summary

::RuboCop::Cop::Base - Inherited

.autocorrect_incompatible_with

List of cops that should not try to autocorrect at the same time as this cop.

.badge

Naming.

.callbacks_needed, .cop_name, .department,
.documentation_url

Cops (other than builtin) are encouraged to implement this.

.exclude_from_registry

Call for abstract Cop classes.

.inherited,
.joining_forces

Override and return the Force class(es) you need to join.

.match?

Returns true if the cop name or the cop namespace matches any of the given names.

.new,
.requires_gem

Register a version requirement for the given gem name.

.restrict_on_send

::RuboCop::ExcludeLimit - Extended

exclude_limit

Sets up a configuration option to have an exclude limit tracked.

transform

Instance Attribute Summary

Instance Method Summary

::RuboCop::Cop::AllowedPattern - Included

::RuboCop::Cop::CheckAssignment - Included

::RuboCop::Cop::Alignment - Included

::RuboCop::Cop::EndKeywordAlignment - Included

::RuboCop::Cop::RangeHelp - Included

#add_range, #column_offset_between,
#contents_range

A range containing only the contents of a literal with delimiters (e.g.

#directions,
#effective_column

Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value.

#final_pos, #move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range

::RuboCop::Cop::ConfigurableEnforcedStyle - Included

::RuboCop::Cop::Base - Inherited

#add_global_offense

Adds an offense that has no particular location.

#add_offense

Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense.

#begin_investigation

Called before any investigation.

#callbacks_needed,
#cop_config

Configuration Helpers.

#cop_name, #excluded_file?,
#external_dependency_checksum

This method should be overridden when a cop’s behavior depends on state that lives outside of these locations:

#inspect,
#message

Gets called if no message is specified when calling add_offense or add_global_offense Cops are discouraged to override this; instead pass your message directly.

#name

Alias for Base#cop_name.

#offenses,
#on_investigation_end

Called after all on_…​

#on_new_investigation

Called before all on_…​

#on_other_file

Called instead of all on_…​

#parse

There should be very limited reasons for a Cop to do it’s own parsing.

#parser_engine,
#ready

Called between investigations.

#relevant_file?, #target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction,
#callback_argument

Reserved for Cop::Cop.

#complete_investigation

Called to complete an investigation.

#correct, #current_corrector,
#current_offense_locations

Reserved for Commissioner:

#current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #range_for_original, #range_from_node_or_range, #reset_investigation, #use_corrector

::RuboCop::Cop::AutocorrectLogic - Included

::RuboCop::Cop::IgnoredNode - Included

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Instance Attribute Details

#indented_internal_methods_style?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 219

def indented_internal_methods_style?
  indentation_consistency_style == 'indented_internal_methods'
end

Instance Method Details

#access_modifier?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 57

def_node_matcher :access_modifier?, <<~PATTERN
  [(send ...) access_modifier?]
PATTERN

#access_modifier_indentation_style (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 227

def access_modifier_indentation_style
  config.for_cop('Layout/AccessModifierIndentation')['EnforcedStyle']
end

#autocorrect(corrector, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 165

def autocorrect(corrector, node)
  AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
end

#check_assignment(node, rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 235

def check_assignment(node, rhs)
  # If there are method calls chained to the right hand side of the
  # assignment, we let rhs be the receiver of those method calls before
  # we check its indentation.
  rhs = first_part_of_call_chain(rhs)
  return unless rhs

  end_config = config.for_cop('Layout/EndAlignment')
  style = end_config['EnforcedStyleAlignWith'] || 'keyword'
  base = variable_alignment?(node.loc, rhs, style.to_sym) ? node : rhs

  case rhs.type
  when :if            then on_if(rhs, base)
  when :while, :until then on_while(rhs, base)
  else                     return
  end

  ignore_node(rhs)
end

#check_if(node, body, else_clause, base_loc) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 255

def check_if(node, body, else_clause, base_loc)
  return if node.ternary?

  check_indentation(base_loc, body)
  return unless else_clause

  # If the else clause is an elsif, it will get its own on_if call so
  # we don't need to process it here.
  return if else_clause.if_type? && else_clause.elsif?

  check_indentation(node.loc.else, else_clause)
end

#check_indentation(base_loc, body_node, style = 'normal') (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 268

def check_indentation(base_loc, body_node, style = 'normal')
  return unless indentation_to_check?(base_loc, body_node)

  indentation = column_offset_between(body_node.loc, base_loc)
  @column_delta = configured_indentation_width - indentation
  return if @column_delta.zero?

  offense(body_node, indentation, style)
end

#check_members(base, members) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 169

def check_members(base, members)
  check_indentation(base, select_check_member(members.first))

  return unless members.any? && members.first.begin_type?

  if indentation_consistency_style == 'indented_internal_methods'
    check_members_for_indented_internal_methods_style(members)
  else
    check_members_for_normal_style(base, members)
  end
end

#check_members_for_indented_internal_methods_style(members) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 193

def check_members_for_indented_internal_methods_style(members)
  each_member(members) do |member, previous_modifier|
    check_indentation(previous_modifier, member, indentation_consistency_style)
  end
end

#check_members_for_normal_style(base, members) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 199

def check_members_for_normal_style(base, members)
  members.first.children.each do |member|
    next if member.send_type? && member.access_modifier?

    check_indentation(base, member)
  end
end

#check_rescue?(rescue_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 341

def check_rescue?(rescue_node)
  rescue_node.body
end

#configured_indentation_width (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 377

def configured_indentation_width
  cop_config['Width']
end

#each_member(members) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 207

def each_member(members)
  previous_modifier = nil
  members.first.children.each do |member|
    if member.send_type? && member.special_modifier?
      previous_modifier = member
    elsif previous_modifier
      yield member, previous_modifier.source_range
      previous_modifier = nil
    end
  end
end

#indentation_consistency_style (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 231

def indentation_consistency_style
  config.for_cop('Layout/IndentationConsistency')['EnforcedStyle']
end

#indentation_to_check?(base_loc, body_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 323

def indentation_to_check?(base_loc, body_node)
  return false if skip_check?(base_loc, body_node)

  if body_node.rescue_type?
    check_rescue?(body_node)
  elsif body_node.ensure_type?
    block_body, = *body_node

    if block_body&.rescue_type?
      check_rescue?(block_body)
    else
      !block_body.nil?
    end
  else
    true
  end
end

#leftmost_modifier_of(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 381

def leftmost_modifier_of(node)
  return node unless node.parent&.send_type?

  leftmost_modifier_of(node.parent)
end

#message(configured_indentation_width, indentation, name) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 302

def message(configured_indentation_width, indentation, name)
  format(
    MSG,
    configured_indentation_width: configured_indentation_width,
    indentation: indentation,
    name: name
  )
end

#offending_range(body_node, indentation) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 360

def offending_range(body_node, indentation)
  expr = body_node.source_range
  begin_pos = expr.begin_pos
  ind = expr.begin_pos - indentation
  pos = indentation >= 0 ? ind..begin_pos : begin_pos..ind
  range_between(pos.begin, pos.end)
end

#offense(body_node, indentation, style) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 278

def offense(body_node, indentation, style)
  # This cop only autocorrects the first statement in a def body, for
  # example.
  body_node = body_node.children.first if body_node.begin_type? && !parentheses?(body_node)

  # Since autocorrect changes a number of lines, and not only the line
  # where the reported offending range is, we avoid autocorrection if
  # this cop has already found other offenses is the same
  # range. Otherwise, two corrections can interfere with each other,
  # resulting in corrupted code.
  node = if autocorrect? && other_offense_in_same_range?(body_node)
           nil
         else
           body_node
         end

  name = style == 'normal' ? '' : " #{style}"
  message = message(configured_indentation_width, indentation, name)

  add_offense(offending_range(body_node, indentation), message: message) do |corrector|
    autocorrect(corrector, node)
  end
end

#on_block(node) Also known as: #on_numblock

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 81

def on_block(node)
  end_loc = node.loc.end

  return unless begins_its_line?(end_loc)

  check_indentation(end_loc, node.body)

  return unless indented_internal_methods_style?

  check_members(end_loc, [node.body])
end

#on_case(case_node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 138

def on_case(case_node)
  case_node.each_when do |when_node|
    check_indentation(when_node.loc.keyword, when_node.body)
  end

  check_indentation(case_node.when_branches.last.loc.keyword, case_node.else_branch)
end

#on_case_match(case_match)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 146

def on_case_match(case_match)
  case_match.each_in_pattern do |in_pattern_node|
    check_indentation(in_pattern_node.loc.keyword, in_pattern_node.body)
  end

  else_branch = case_match.else_branch&.empty_else_type? ? nil : case_match.else_branch

  check_indentation(case_match.in_pattern_branches.last.loc.keyword, else_branch)
end

#on_class(node) Also known as: #on_sclass, #on_module

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 95

def on_class(node)
  base = node.loc.keyword
  return if same_line?(base, node.body)

  check_members(base, [node.body])
end

#on_csend(node)

Alias for #on_send.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 119

alias on_csend on_send

#on_def(node) Also known as: #on_defs

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 121

def on_def(node)
  return if ignored_node?(node)

  check_indentation(node.loc.keyword, node.body)
end

#on_defs(node)

Alias for #on_def.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 126

alias on_defs on_def

#on_ensure(node) Also known as: #on_resbody, #on_for

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 66

def on_ensure(node)
  check_indentation(node.loc.keyword, node.body)
end

#on_for(node)

Alias for #on_ensure.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 71

alias on_for     on_ensure

#on_if(node, base = node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 156

def on_if(node, base = node)
  return if ignored_node?(node)
  return if node.ternary? || node.modifier_form?

  check_if(node, node.body, node.else_branch, base.loc)
end

#on_kwbegin(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 73

def on_kwbegin(node)
  # Check indentation against end keyword but only if it's first on its
  # line.
  return unless begins_its_line?(node.loc.end)

  check_indentation(node.loc.end, node.children.first)
end

#on_module(node)

Alias for #on_class.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 102

alias on_module on_class

#on_numblock(node)

Alias for #on_block.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 93

alias on_numblock on_block

#on_resbody(node)

Alias for #on_ensure.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 70

alias on_resbody on_ensure

#on_rescue(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 61

def on_rescue(node)
  _begin_node, *_rescue_nodes, else_node = *node
  check_indentation(node.loc.else, else_node)
end

#on_sclass(node)

Alias for #on_class.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 101

alias on_sclass on_class

#on_send(node) Also known as: #on_csend

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 104

def on_send(node)
  super
  return unless node.adjacent_def_modifier?

  def_end_config = config.for_cop('Layout/DefEndAlignment')
  style = def_end_config['EnforcedStyleAlignWith'] || 'start_of_line'
  base = if style == 'def'
           node.first_argument
         else
           leftmost_modifier_of(node) || node
         end

  check_indentation(base.source_range, node.first_argument.body)
  ignore_node(node.first_argument)
end

#on_until(node, base = node)

Alias for #on_while.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 136

alias on_until on_while

#on_while(node, base = node) Also known as: #on_until

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 128

def on_while(node, base = node)
  return if ignored_node?(node)

  return unless node.single_line_condition?

  check_indentation(base.loc, node.body)
end

#other_offense_in_same_range?(node) ⇒ Boolean (private)

Returns true if the given node is within another node that has already been marked for autocorrection by this cop.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 313

def other_offense_in_same_range?(node)
  expr = node.source_range
  @offense_ranges ||= []

  return true if @offense_ranges.any? { |r| within?(expr, r) }

  @offense_ranges << expr
  false
end

#select_check_member(member) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 181

def select_check_member(member)
  return unless member

  if access_modifier?(member.children.first)
    return if access_modifier_indentation_style == 'outdent'

    member.children.first
  else
    member
  end
end

#skip_check?(base_loc, body_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 345

def skip_check?(base_loc, body_node)
  return true if allowed_line?(base_loc)
  return true unless body_node

  # Don't check if expression is on same line as "then" keyword, etc.
  return true if same_line?(body_node, base_loc)

  return true if starts_with_access_modifier?(body_node)

  # Don't check indentation if the line doesn't start with the body.
  # For example, lines like "else do_something".
  first_char_pos_on_line = body_node.source_range.source_line =~ /\S/
  body_node.loc.column != first_char_pos_on_line
end

#special_modifier?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 223

def special_modifier?(node)
  node.bare_access_modifier? && SPECIAL_MODIFIERS.include?(node.source)
end

#starts_with_access_modifier?(body_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/indentation_width.rb', line 368

def starts_with_access_modifier?(body_node)
  return false unless body_node.begin_type?

  starting_node = body_node.children.first
  return false unless starting_node

  starting_node.send_type? && starting_node.bare_access_modifier?
end