module RuboCop::Cop::AutocorrectAlignment

This module does auto-correction of nodes that should just be moved to the left or to the right, amount being determined by the instance variable @column_delta.

Constants

SPACE

Public Instance Methods

autocorrect(arg) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 49
def autocorrect(arg)
  return unless arg

  heredoc_ranges = heredoc_ranges(arg)
  expr = arg.respond_to?(:loc) ? arg.loc.expression : arg

  # We can't use the instance variable inside the lambda. That would just
  # give each lambda the same reference and they would all get the last
  # value of @column_delta. A local variable fixes the problem.
  column_delta = @column_delta

  return if block_comment_within?(expr)

  lambda do |corrector|
    each_line(expr) do |line_begin_pos|
      autocorrect_line(corrector, line_begin_pos, expr, column_delta,
                       heredoc_ranges)
    end
  end
end
check_alignment(items, base_column = nil) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 29
def check_alignment(items, base_column = nil)
  unless items.empty?
    base_column ||= display_column(items.first.source_range)
  end

  each_bad_alignment(items, base_column) do |current|
    expr = current.source_range
    if offenses.any? { |o| within?(expr, o.location) }
      # If this offense is within a line range that is already being
      # realigned by autocorrect, we report the offense without
      # autocorrecting it. Two rewrites in the same area by the same
      # cop can not be handled. The next iteration will find the
      # offense again and correct it.
      add_offense(nil, location: expr)
    else
      add_offense(current)
    end
  end
end
configured_indentation_width() click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 11
def configured_indentation_width
  cop_config['IndentationWidth'] ||
    config.for_cop('IndentationWidth')['Width']
end
display_column(range) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 24
def display_column(range)
  line = processed_source.lines[range.line - 1]
  Unicode::DisplayWidth.of(line[0, range.column])
end
indentation(node) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 16
def indentation(node)
  offset(node) + (SPACE * configured_indentation_width)
end
offset(node) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 20
def offset(node)
  SPACE * node.loc.column
end

Private Instance Methods

autocorrect_line(corrector, line_begin_pos, expr, column_delta, heredoc_ranges) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 85
def autocorrect_line(corrector, line_begin_pos, expr, column_delta,
                     heredoc_ranges)
  range = calculate_range(expr, line_begin_pos, column_delta)
  # We must not change indentation of heredoc strings.
  return if heredoc_ranges.any? { |h| within?(range, h) }

  if column_delta > 0
    unless range.source == "\n"
      corrector.insert_before(range, ' ' * column_delta)
    end
  elsif range.source =~ /\A[ \t]+\z/
    remove(range, corrector)
  end
end
block_comment_within?(expr) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 108
def block_comment_within?(expr)
  processed_source.comments.select(&:document?).any? do |c|
    within?(c.loc.expression, expr)
  end
end
calculate_range(expr, line_begin_pos, column_delta) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 118
def calculate_range(expr, line_begin_pos, column_delta)
  starts_with_space = expr.source_buffer.source[line_begin_pos] =~ / /
  pos_to_remove = if column_delta > 0 || starts_with_space
                    line_begin_pos
                  else
                    line_begin_pos - column_delta.abs
                  end

  range_between(pos_to_remove, pos_to_remove + column_delta.abs)
end
each_bad_alignment(items, base_column) { |current| ... } click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 72
def each_bad_alignment(items, base_column)
  prev_line = -1
  items.each do |current|
    if current.loc.line > prev_line &&
       begins_its_line?(current.source_range)
      @column_delta = base_column - display_column(current.source_range)

      yield current if @column_delta.nonzero?
    end
    prev_line = current.loc.line
  end
end
each_line(expr) { |line_begin_pos| ... } click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 140
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
heredoc_ranges(arg) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 100
def heredoc_ranges(arg)
  return [] unless arg.is_a?(Parser::AST::Node)

  arg.each_node(:dstr)
     .select { |n| n.loc.respond_to?(:heredoc_body) }
     .map { |n| n.loc.heredoc_body.join(n.loc.heredoc_end) }
end
remove(range, corrector) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 129
def remove(range, corrector)
  original_stderr = $stderr
  $stderr = StringIO.new # Avoid error messages on console
  corrector.remove(range)
rescue RuntimeError
  range = range_between(range.begin_pos + 1, range.end_pos + 1)
  retry if range.source =~ /^ +$/
ensure
  $stderr = original_stderr
end
within?(inner, outer) click to toggle source
# File lib/rubocop/cop/mixin/autocorrect_alignment.rb, line 114
def within?(inner, outer)
  inner.begin_pos >= outer.begin_pos && inner.end_pos <= outer.end_pos
end