123456789_123456789_123456789_123456789_123456789_

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

Overview

Checks for indentation that doesn’t use the specified number of spaces. The indentation width can be configured using the Width setting. The default width is 2.

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

Examples:

Width: 2 (default)

# 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.

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

Returns an url to view this cops documentation online.

.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

Actually private methods.

#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 218

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 56

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 226

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 164

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 234

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 254

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 267

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 168

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 192

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 198

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 340

def check_rescue?(rescue_node)
  rescue_node.body
end

#configured_indentation_width (private)

[ GitHub ]

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

def configured_indentation_width
  cop_config['Width']
end

#each_member(members) (private)

[ GitHub ]

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

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 230

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 322

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 380

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 301

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 359

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 277

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 80

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 137

def on_case(case_node)
  case_node.when_branches.each 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 145

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 94

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 118

alias on_csend on_send

#on_def(node) Also known as: #on_defs

[ GitHub ]

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

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 125

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 65

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 70

alias on_for     on_ensure

#on_if(node, base = node)

[ GitHub ]

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

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 72

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 101

alias on_module on_class

#on_numblock(node)

Alias for #on_block.

[ GitHub ]

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

alias on_numblock on_block

#on_resbody(node)

Alias for #on_ensure.

[ GitHub ]

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

alias on_resbody on_ensure

#on_rescue(node)

[ GitHub ]

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

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 100

alias on_sclass on_class

#on_send(node) Also known as: #on_csend

[ GitHub ]

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

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 135

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 127

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 312

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 180

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 344

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 222

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 367

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