123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::AlignmentCorrector

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Inherits: Object
Defined in: lib/rubocop/cop/correctors/alignment_corrector.rb

Overview

This class does autocorrection of nodes that should just be moved to the left or to the right, amount being determined by the instance variable column_delta.

Class Attribute Summary

Class Method Summary

Alignment - Extended

RangeHelp - Extended

add_range,
arguments_range

A range containing the first to the last argument of a method call or method definition.

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

Class Attribute Details

.processed_source (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 13

attr_reader :processed_source

.using_tabs?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 186

def using_tabs?
  config = processed_source.config
  indentation_style = config.for_cop('Layout/IndentationStyle')['EnforcedStyle']
  indentation_style == 'tabs'
end

Class Method Details

.align_end(corrector, processed_source, node, align_to)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 40

def align_end(corrector, processed_source, node, align_to)
  @processed_source = processed_source
  whitespace = whitespace_range(node)
  column = alignment_column(align_to)
  indentation = indentation_string(column)

  if whitespace.source.strip.empty?
    corrector.replace(whitespace, indentation)
  else
    corrector.insert_after(whitespace, "\n#{indentation}")
  end
end

.alignment_column(align_to) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 168

def alignment_column(align_to)
  if !align_to
    0
  elsif align_to.respond_to?(:loc)
    align_to.source_range.column
  else
    align_to.column
  end
end

.autocorrect_line(corrector, line_begin_pos, expr, column_delta, taboo_ranges) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 55

def autocorrect_line(corrector, line_begin_pos, expr, column_delta,
                     taboo_ranges)
  range = calculate_range(expr, line_begin_pos, column_delta)
  # We must not change indentation of heredoc strings or inside other
  # string literals
  return if taboo_ranges.any? { |t| within?(range, t) }

  if column_delta.positive? && range.resize(1).source != "\n"
    corrector.insert_before(range, ' ' * column_delta)
  elsif /\A[ \t]+\z/.match?(range.source)
    corrector.remove(range)
  end
end

.block_comment_within?(expr) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 136

def block_comment_within?(expr)
  processed_source.comments.select(&:document?).any? do |c|
    within?(c.source_range, expr)
  end
end

.calculate_range(expr, line_begin_pos, column_delta) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 142

def calculate_range(expr, line_begin_pos, column_delta)
  return range_between(line_begin_pos, line_begin_pos) if column_delta.positive?

  starts_with_space = expr.source_buffer.source[line_begin_pos].start_with?(' ')

  if starts_with_space
    range_between(line_begin_pos, line_begin_pos + column_delta.abs)
  else
    range_between(line_begin_pos - column_delta.abs, line_begin_pos)
  end
end

.configured_indentation_width (private)

The number of columns a single tab spans. This must match the width Layout/IndentationWidth uses to compute column_delta, otherwise the division below would not land on a tab boundary. Layout/IndentationStyle has its own IndentationWidth, but that only governs how that cop replaces tabs with spaces, so it must not be consulted here.

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 107

def configured_indentation_width
  processed_source.config.for_cop('Layout/IndentationWidth')['Width'] || 2
end

.correct(corrector, processed_source, node, column_delta, tab_indentation: false)

tab_indentation may only be set by callers whose column_delta represents whole indentation levels (e.g. Layout/IndentationWidth). Alignment to an arbitrary column cannot be expressed with tabs, so those callers leave tab correction disabled to avoid producing a never-converging correction.

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 19

def correct(corrector, processed_source, node, column_delta, tab_indentation: false)
  return unless node

  @processed_source = processed_source

  expr = node.respond_to?(:loc) ? node.source_range : node
  return if block_comment_within?(expr)

  taboo_ranges = inside_string_ranges(node)

  if using_tabs?
    return unless tab_indentation

    correct_tab_indentation(corrector, expr, column_delta, taboo_ranges)
  else
    each_line(expr) do |line_begin_pos|
      autocorrect_line(corrector, line_begin_pos, expr, column_delta, taboo_ranges)
    end
  end
end

.correct_tab_indentation(corrector, expr, column_delta, taboo_ranges) (private)

Tab indentation is corrected by rewriting each line’s leading whitespace to the target number of tabs. Working in whole tabs rather than applying a column delta keeps the result idempotent, which avoids the infinite loops that delta-based correction caused for tabs.

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 73

def correct_tab_indentation(corrector, expr, column_delta, taboo_ranges)
  buffer = expr.source_buffer

  each_line(expr) do |line_begin_pos|
    line_range = buffer.line_range(buffer.line_for_position(line_begin_pos))
    correct_tab_line(corrector, line_range, column_delta, taboo_ranges)
  end
end

.correct_tab_line(corrector, line_range, column_delta, taboo_ranges) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 82

def correct_tab_line(corrector, line_range, column_delta, taboo_ranges)
  leading = line_range.source[/\A[ \t]*/]
  return if leading.length == line_range.source.length # blank line

  leading_range = range_between(line_range.begin_pos, line_range.begin_pos + leading.length)
  return if taboo_ranges.any? { |t| within?(leading_range, t) }

  target = target_tab_indentation(leading, column_delta)
  corrector.replace(leading_range, target) unless leading == target
end

.delimited_string_literal?(node) ⇒ Boolean (private)

Some special kinds of string literals are not composed of literal characters between two delimiters: - The source map of ?a responds to :begin and :end but its end is nil. - The source map of FILE responds to neither :begin nor :end.

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 132

def delimited_string_literal?(node)
  node.loc?(:begin) && node.loc?(:end)
end

.each_line(expr) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 154

def each_line(expr)
  line_begin_pos = expr.begin_pos
  expr.source.each_line do |line|
    yield line_begin_pos
    line_begin_pos += line.length
  end
end

.indentation_string(column) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 178

def indentation_string(column)
  if using_tabs?
    "\t" * column
  else
    ' ' * column
  end
end

.inside_string_range(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 117

def inside_string_range(node)
  loc = node.location

  if node.heredoc?
    loc.heredoc_body.join(loc.heredoc_end)
  elsif delimited_string_literal?(node)
    loc.begin.end.join(loc.end.begin)
  end
end

.inside_string_ranges(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 111

def inside_string_ranges(node)
  return [] unless node.is_a?(Parser::AST::Node)

  node.each_node(:any_str).filter_map { |n| inside_string_range(n) }
end

.target_tab_indentation(leading, column_delta) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 93

def target_tab_indentation(leading, column_delta)
  width = configured_indentation_width
  return leading unless width.positive?

  visual_width = leading.chars.sum { |char| char == "\t" ? width : 1 }

  "\t" * ([visual_width + column_delta, 0].max / width)
end

.whitespace_range(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/alignment_corrector.rb', line 162

def whitespace_range(node)
  begin_pos = node.loc.end.begin_pos

  range_between(begin_pos - node.loc.end.column, begin_pos)
end