class RuboCop::Cop::Layout::LineLength

Checks the length of lines in the source code. The maximum length is configurable. The tab size is configured in the ‘IndentationWidth` of the `Layout/IndentationStyle` cop. It also ignores a shebang line by default.

This cop has some autocorrection capabilities. It can programmatically shorten certain long lines by inserting line breaks into expressions that can be safely split across lines. These include arrays, hashes, and method calls with argument lists.

If autocorrection is enabled, the following cops are recommended to further format the broken lines. (Many of these are enabled by default.)

Together, these cops will pretty print hashes, arrays, method calls, etc. For example, let’s say the max columns is 25:

@example

# bad
{foo: "0000000000", bar: "0000000000", baz: "0000000000"}

# good
{foo: "0000000000",
bar: "0000000000", baz: "0000000000"}

# good (with recommended cops enabled)
{
  foo: "0000000000",
  bar: "0000000000",
  baz: "0000000000",
}

Constants

MSG

Attributes

breakable_range[RW]

Public Instance Methods

on_array(node)
on_block(node) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 74
def on_block(node)
  check_for_breakable_block(node)
end
Also aliased as: on_numblock
on_def(node)
on_hash(node)
on_investigation_end() click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 94
def on_investigation_end
  processed_source.lines.each_with_index do |line, line_index|
    check_line(line, line_index)
  end
end
on_new_investigation() click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 88
def on_new_investigation
  return unless processed_source.raw_source.include?(';')

  check_for_breakable_semicolons(processed_source)
end
on_numblock(node)
Alias for: on_block
on_potential_breakable_node(node) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 80
def on_potential_breakable_node(node)
  check_for_breakable_node(node)
end
Also aliased as: on_array, on_hash, on_send, on_def
on_send(node)

Private Instance Methods

allow_heredoc?() click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 219
def allow_heredoc?
  allowed_heredoc
end
allowed_heredoc() click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 223
def allowed_heredoc
  cop_config['AllowHeredoc']
end
allowed_line?(line, line_index) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 183
def allowed_line?(line, line_index)
  matches_allowed_pattern?(line) ||
    shebang?(line, line_index) ||
    (heredocs && line_in_permitted_heredoc?(line_index.succ))
end
breakable_block_range(block_node) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 135
def breakable_block_range(block_node)
  if block_node.arguments? && !block_node.lambda?
    block_node.arguments.loc.end
  else
    block_node.braces? ? block_node.loc.begin : block_node.loc.begin.adjust(begin_pos: 1)
  end
end
breakable_range_after_semicolon(semicolon_token) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 143
def breakable_range_after_semicolon(semicolon_token)
  range = semicolon_token.pos
  end_pos = range.end_pos
  next_range = range_between(end_pos, end_pos + 1)
  return nil unless same_line?(next_range, range)

  next_char = next_range.source
  return nil if /[\r\n]/.match?(next_char)
  return nil if next_char == ';'

  next_range
end
breakable_range_by_line_index() click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 156
def breakable_range_by_line_index
  @breakable_range_by_line_index ||= {}
end
check_directive_line(line, line_index) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 250
def check_directive_line(line, line_index)
  length_without_directive = line_length_without_directive(line)
  return if length_without_directive <= max

  range = max..(length_without_directive - 1)
  register_offense(
    source_range(
      processed_source.buffer,
      line_index + 1,
      range
    ),
    line,
    line_index,
    length: length_without_directive
  )
end
check_for_breakable_block(block_node) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 125
def check_for_breakable_block(block_node)
  return unless block_node.single_line?

  line_index = block_node.loc.line - 1
  range = breakable_block_range(block_node)
  pos = range.begin_pos + 1

  breakable_range_by_line_index[line_index] = range_between(pos, pos + 1)
end
check_for_breakable_node(node) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 104
def check_for_breakable_node(node)
  breakable_node = extract_breakable_node(node, max)
  return if breakable_node.nil?

  line_index = breakable_node.first_line - 1
  range = breakable_node.source_range

  existing = breakable_range_by_line_index[line_index]
  return if existing

  breakable_range_by_line_index[line_index] = range
end
check_for_breakable_semicolons(processed_source) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 117
def check_for_breakable_semicolons(processed_source)
  tokens = processed_source.tokens.select { |t| t.type == :tSEMI }
  tokens.reverse_each do |token|
    range = breakable_range_after_semicolon(token)
    breakable_range_by_line_index[range.line - 1] = range if range
  end
end
check_line(line, line_index) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 171
def check_line(line, line_index)
  return if line_length(line) <= max
  return if allowed_line?(line, line_index)

  if ignore_cop_directives? && directive_on_source_line?(line_index)
    return check_directive_line(line, line_index)
  end
  return check_uri_line(line, line_index) if allow_uri?

  register_offense(excess_range(nil, line, line_index), line, line_index)
end
check_uri_line(line, line_index) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 267
def check_uri_line(line, line_index)
  uri_range = find_excessive_uri_range(line)
  return if uri_range && allowed_uri_position?(line, uri_range)

  register_offense(excess_range(uri_range, line, line_index), line, line_index)
end
excess_range(uri_range, line, line_index) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 204
def excess_range(uri_range, line, line_index)
  excessive_position = if uri_range && uri_range.begin < max
                         uri_range.end
                       else
                         highlight_start(line)
                       end

  source_range(processed_source.buffer, line_index + 1,
               excessive_position...(line_length(line)))
end
extract_heredocs(ast) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 227
def extract_heredocs(ast)
  return [] unless ast

  ast.each_node(:str, :dstr, :xstr).select(&:heredoc?).map do |node|
    body = node.location.heredoc_body
    delimiter = node.location.heredoc_end.source.strip
    [body.first_line...body.last_line, delimiter]
  end
end
heredocs() click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 160
def heredocs
  @heredocs ||= extract_heredocs(processed_source.ast)
end
highlight_start(line) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 164
def highlight_start(line)
  # TODO: The max with 0 is a quick fix to avoid crashes when a line
  # begins with many tabs, but getting a correct highlighting range
  # when tabs are used for indentation doesn't work currently.
  [max - indentation_difference(line), 0].max
end
line_in_heredoc?(line_number) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 246
def line_in_heredoc?(line_number)
  heredocs.any? { |range, _delimiter| range.cover?(line_number) }
end
line_in_permitted_heredoc?(line_number) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 237
def line_in_permitted_heredoc?(line_number)
  return false unless allowed_heredoc

  heredocs.any? do |range, delimiter|
    range.cover?(line_number) &&
      (allowed_heredoc == true || allowed_heredoc.include?(delimiter))
  end
end
max() click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 215
def max
  cop_config['Max']
end
register_offense(loc, line, line_index, length: line_length(line)) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 193
def register_offense(loc, line, line_index, length: line_length(line))
  message = format(MSG, length: length, max: max)

  self.breakable_range = breakable_range_by_line_index[line_index]

  add_offense(loc, message: message) do |corrector|
    self.max = line_length(line)
    corrector.insert_before(breakable_range, "\n") unless breakable_range.nil?
  end
end
shebang?(line, line_index) click to toggle source
# File lib/rubocop/cop/layout/line_length.rb, line 189
def shebang?(line, line_index)
  line_index.zero? && line.start_with?('#!')
end