123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Layout::MultilineMethodCallIndentation

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, ::RuboCop::Cop::AutoCorrector, ::RuboCop::Cop::Base, ::RuboCop::ExcludeLimit, NodePattern::Macros, RuboCop::AST::Sexp
Instance Chain:
Inherits: RuboCop::Cop::Base
Defined in: lib/rubocop/cop/layout/multiline_method_call_indentation.rb

Overview

Checks the indentation of the method name part in method calls that span more than one line.

Examples:

EnforcedStyle: aligned (default)

# bad
while myvariable
.b
  # do something
end

# good
while myvariable
      .b
  # do something
end

# good
Thing.a
     .b
     .c

EnforcedStyle: indented

# good
while myvariable
  .b

  # do something
end

EnforcedStyle: indented_relative_to_receiver

# good
while myvariable
        .a
        .b

  # do something
end

# good
myvariable = Thing
               .a
               .b
               .c

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

::RuboCop::Cop::ConfigurableEnforcedStyle - Included

SYMBOL_TO_STRING_CACHE

::RuboCop::Cop::Alignment - Included

SPACE

::RuboCop::Cop::MultilineExpressionIndentation - Included

ASSIGNMENT_MESSAGE_TAIL, DEFAULT_MESSAGE_TAIL, KEYWORD_ANCESTOR_TYPES, KEYWORD_MESSAGE_TAIL, UNALIGNED_RHS_TYPES

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::MultilineExpressionIndentation - Included

#on_csend
#on_send,
#argument_in_method_call

rubocop:todo Metrics/CyclomaticComplexity.

#assignment_rhs, #check,
#correct_indentation

The correct indentation of node is usually IndentationWidth, with one exception: prefix keywords.

#disqualified_rhs?, #grouped_expression?, #incorrect_style_detected, #indentation, #indented_keyword_expression, #inside_arg_list_parentheses?, #keyword_message_tail, #kw_node_with_special_indentation,
#left_hand_side

In a chain of method calls, we regard the top call node as the base for indentation of all lines following the first.

#not_for_this_cop?, #operation_description, #part_of_assignment_rhs, #part_of_block_body?,
#postfix_conditional?

Returns true if node is a conditional whose body and condition begin on the same line.

#valid_method_rhs_candidate?

The []= operator and setters (a.b = c) are parsed as :send nodes.

#valid_rhs?, #valid_rhs_candidate?, #within_node?

::RuboCop::Cop::Alignment - Included

::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

#should_align_with_base?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 127

def should_align_with_base?
  @base && style != :indented_relative_to_receiver
end

#should_indent_relative_to_receiver?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 123

def should_indent_relative_to_receiver?
  @base && style == :indented_relative_to_receiver
end

Instance Method Details

#align_with_base_message(rhs) (private)

[ GitHub ]

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

def align_with_base_message(rhs)
  "Align `#{rhs.source}` with `#{base_source}` on line #{@base.line}."
end

#alignment_base(node, rhs, given_style) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 152

def alignment_base(node, rhs, given_style)
  case given_style
  when :aligned
    semantic_alignment_base(node, rhs) || syntactic_alignment_base(node, rhs)
  when :indented
    nil
  when :indented_relative_to_receiver
    receiver_alignment_base(node)
  end
end

#autocorrect(corrector, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 67

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

#base_source (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 140

def base_source
  @base.source[/[^\n]*/]
end

#extra_indentation(given_style, parent) (private)

[ GitHub ]

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

def extra_indentation(given_style, parent)
  if given_style == :indented_relative_to_receiver
    if parent && (parent.splat_type? || parent.kwsplat_type?)
      configured_indentation_width - parent.loc.operator.length
    else
      configured_indentation_width
    end
  else
    0
  end
end

#find_multiline_block_chain_node(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 226

def find_multiline_block_chain_node(node)
  return unless (block_node = node.each_descendant(:block, :numblock).first)
  return unless block_node.multiline? && block_node.parent.call_type?

  if node.receiver.call_type?
    node.receiver
  else
    block_node.parent
  end
end

#first_call_has_a_dot(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 237

def first_call_has_a_dot(node)
  # descend to root of method chain
  node = node.receiver while node.receiver
  # ascend to first call which has a dot
  node = node.parent
  node = node.parent until node.loc.respond_to?(:dot) && node.loc.dot

  node
end

#get_dot_right_above(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 217

def get_dot_right_above(node)
  node.each_ancestor.find do |a|
    dot = a.loc.respond_to?(:dot) && a.loc.dot
    next unless dot

    dot.line == node.loc.dot.line - 1 && dot.column == node.loc.dot.column
  end
end

#message(node, lhs, rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 113

def message(node, lhs, rhs)
  if should_indent_relative_to_receiver?
    relative_to_receiver_message(rhs)
  elsif should_align_with_base?
    align_with_base_message(rhs)
  else
    no_base_message(lhs, rhs, node)
  end
end

#no_base_message(lhs, rhs, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 144

def no_base_message(lhs, rhs, node)
  used_indentation = rhs.column - indentation(lhs)
  what = operation_description(node, rhs)

  "Use #{correct_indentation(node)} (not #{used_indentation}) " \
    "spaces for indenting #{what} spanning multiple lines."
end

#offending_range(node, lhs, rhs, given_style) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 87

def offending_range(node, lhs, rhs, given_style)
  return false unless begins_its_line?(rhs)
  return false if not_for_this_cop?(node)

  @base = alignment_base(node, rhs, given_style)
  correct_column = if @base
                     @base.column + extra_indentation(given_style, node.parent)
                   else
                     indentation(lhs) + correct_indentation(node)
                   end
  @column_delta = correct_column - rhs.column
  rhs if @column_delta.nonzero?
end

#operation_rhs(node) {|operation_rhs.first_argument| ... } (private)

Yields:

  • (operation_rhs.first_argument)
[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 247

def operation_rhs(node)
  operation_rhs = node.receiver.each_ancestor(:send).find do |rhs|
    operator_rhs?(rhs, node.receiver)
  end

  return unless operation_rhs

  yield operation_rhs.first_argument
end

#operator_rhs?(node, receiver) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 257

def operator_rhs?(node, receiver)
  node.operator_method? && node.arguments? && within_node?(receiver, node.first_argument)
end

#receiver_alignment_base(node) (private)

a .b .c

[ GitHub ]

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

def receiver_alignment_base(node)
  node = node.receiver while node.receiver
  node = node.parent
  node = node.parent until node.loc.dot

  node&.receiver&.source_range
end

#relative_to_receiver_message(rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 131

def relative_to_receiver_message(rhs)
  "Indent `#{rhs.source}` #{configured_indentation_width} spaces " \
    "more than `#{base_source}` on line #{@base.line}."
end

#relevant_node?(send_node) ⇒ Boolean (private)

[ GitHub ]

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

def relevant_node?(send_node)
  send_node.loc.dot # Only check method calls with dot operator
end

#right_hand_side(send_node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 75

def right_hand_side(send_node)
  dot = send_node.loc.dot
  selector = send_node.loc.selector
  if (send_node.dot? || send_node.safe_navigation?) && selector && same_line?(dot, selector)
    dot.join(selector)
  elsif selector
    selector
  elsif send_node.implicit_call?
    dot.join(send_node.loc.begin)
  end
end

#semantic_alignment_base(node, rhs) (private)

a.b .c

[ GitHub ]

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

def semantic_alignment_base(node, rhs)
  return unless rhs.source.start_with?('.', '&.')

  node = semantic_alignment_node(node)
  return unless node&.loc&.selector && node.loc.dot

  node.loc.dot.join(node.loc.selector)
end

#semantic_alignment_node(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 201

def semantic_alignment_node(node)
  return if argument_in_method_call(node, :with_parentheses)

  dot_right_above = get_dot_right_above(node)
  return dot_right_above if dot_right_above

  if (multiline_block_chain_node = find_multiline_block_chain_node(node))
    return multiline_block_chain_node
  end

  node = first_call_has_a_dot(node)
  return if node.loc.dot.line != node.first_line

  node
end

#syntactic_alignment_base(lhs, rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 163

def syntactic_alignment_base(lhs, rhs)
  # a if b
  #      .c
  kw_node_with_special_indentation(lhs) do |base|
    return indented_keyword_expression(base).source_range
  end

  # a = b
  #     .c
  part_of_assignment_rhs(lhs, rhs) { |base| return assignment_rhs(base).source_range }

  # a + b
  #     .c
  operation_rhs(lhs) { |base| return base.source_range }
end

#validate_config

Raises:

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 55

def validate_config
  return unless style == :aligned && cop_config['IndentationWidth']

  raise ValidationError,
        'The `Layout/MultilineMethodCallIndentation` ' \
        'cop only accepts an `IndentationWidth` ' \
        'configuration parameter when ' \
        '`EnforcedStyle` is `indented`.'
end