class RuboCop::Cop::Metrics::LineLength

This cop 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/Tab` cop.

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 Layout cops are recommended to further format the broken lines.

- AlignArray
- AlignHash
- AlignParameters
- ClosingParenthesisIndentation
- IndentFirstArgument
- IndentFirstArrayElement
- IndentFirstHashElement
- IndentFirstParameter
- MultilineArrayLineBreaks
- MultilineHashBraceLayout
- MultilineHashKeyLineBreaks
- MultilineMethodArgumentLineBreaks

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

Public Instance Methods

autocorrect(range) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 76
def autocorrect(range)
  return if range.nil?

  lambda do |corrector|
    corrector.insert_before(range, "\n")
  end
end
investigate_post_walk(processed_source) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 70
def investigate_post_walk(processed_source)
  processed_source.lines.each_with_index do |line, line_index|
    check_line(line, line_index)
  end
end
on_array(node)
on_hash(node)
on_potential_breakable_node(node) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 63
def on_potential_breakable_node(node)
  check_for_breakable_node(node)
end
Also aliased as: on_array, on_hash, on_send
on_send(node)

Private Instance Methods

allow_heredoc?() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 189
def allow_heredoc?
  allowed_heredoc
end
allow_uri?() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 222
def allow_uri?
  cop_config['AllowURI']
end
allowed_heredoc() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 193
def allowed_heredoc
  cop_config['AllowHeredoc']
end
allowed_uri_position?(line, uri_range) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 230
def allowed_uri_position?(line, uri_range)
  uri_range.begin < max &&
    (uri_range.end == line_length(line) ||
     uri_range.end == line_length(line) - 1)
end
breakable_nodes_by_line_index() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 94
def breakable_nodes_by_line_index
  @breakable_nodes_by_line_index ||= {}
end
breakable_range(line, line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 153
def breakable_range(line, line_index)
  return if line_in_heredoc?(line_index + 1)

  semicolon_range = breakable_semicolon_range(line, line_index)
  return semicolon_range if semicolon_range

  breakable_node = breakable_nodes_by_line_index[line_index]
  return breakable_node.source_range if breakable_node
end
breakable_semicolon_range(line, line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 163
def breakable_semicolon_range(line, line_index)
  semicolon_separated_parts = line.split(';')
  return if semicolon_separated_parts.length <= 1

  column = semicolon_separated_parts.first.length + 1
  range = source_range(processed_source.buffer, line_index, column, 1)
  return if processed_source.commented?(range)

  range
end
check_directive_line(line, line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 269
def check_directive_line(line, line_index)
  return if line_length_without_directive(line) <= max

  range = max..(line_length_without_directive(line) - 1)
  register_offense(
    source_range(
      processed_source.buffer,
      line_index + 1,
      range
    ),
    line,
    line_index
  )
end
check_for_breakable_node(node) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 86
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
  breakable_nodes_by_line_index[line_index] = breakable_node
end
check_line(line, line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 120
def check_line(line, line_index)
  return if line_length(line) <= max
  return if ignored_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(
    source_range(
      processed_source.buffer, line_index,
      highlight_start(line)...line_length(line)
    ),
    line,
    line_index
  )
end
check_uri_line(line, line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 301
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
directive_on_source_line?(line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 284
def directive_on_source_line?(line_index)
  source_line_number = line_index + processed_source.buffer.first_line
  comment =
    processed_source
    .comments
    .detect { |e| e.location.line == source_line_number }

  return false unless comment

  comment.text.match(CommentConfig::COMMENT_DIRECTIVE_REGEXP)
end
excess_range(uri_range, line, line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 174
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/metrics/line_length.rb, line 197
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
find_excessive_uri_range(line) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 236
def find_excessive_uri_range(line)
  last_uri_match = match_uris(line).last
  return nil unless last_uri_match

  begin_position, end_position =
    last_uri_match.offset(0).map do |pos|
      pos + indentation_difference(line)
    end
  return nil if begin_position < max && end_position < max

  begin_position...end_position
end
heredocs() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 98
def heredocs
  @heredocs ||= extract_heredocs(processed_source.ast)
end
highlight_start(line) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 116
def highlight_start(line)
  max - indentation_difference(line)
end
ignore_cop_directives?() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 226
def ignore_cop_directives?
  cop_config['IgnoreCopDirectives']
end
ignored_line?(line, line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 139
def ignored_line?(line, line_index)
  matches_ignored_pattern?(line) ||
    heredocs && line_in_permitted_heredoc?(line_index.succ)
end
indentation_difference(line) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 106
def indentation_difference(line)
  return 0 unless tab_indentation_width

  line.match(/^\t*/)[0].size * (tab_indentation_width - 1)
end
line_in_heredoc?(line_number) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 216
def line_in_heredoc?(line_number)
  heredocs.any? do |range, _delimiter|
    range.cover?(line_number)
  end
end
line_in_permitted_heredoc?(line_number) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 207
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
line_length(line) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 112
def line_length(line)
  line.length + indentation_difference(line)
end
line_length_without_directive(line) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 296
def line_length_without_directive(line)
  before_comment, = line.split(CommentConfig::COMMENT_DIRECTIVE_REGEXP)
  before_comment.rstrip.length
end
match_uris(string) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 249
def match_uris(string)
  matches = []
  string.scan(uri_regexp) do
    matches << $LAST_MATCH_INFO if valid_uri?($LAST_MATCH_INFO[0])
  end
  matches
end
max() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 185
def max
  cop_config['Max']
end
register_offense(loc, line, line_index) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 144
def register_offense(loc, line, line_index)
  message = format(MSG, length: line_length(line), max: max)

  breakable_range = breakable_range(line, line_index)
  add_offense(breakable_range, location: loc, message: message) do
    self.max = line_length(line)
  end
end
tab_indentation_width() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 102
def tab_indentation_width
  config.for_cop('Layout/Tab')['IndentationWidth']
end
uri_regexp() click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 264
def uri_regexp
  @uri_regexp ||=
    URI::DEFAULT_PARSER.make_regexp(cop_config['URISchemes'])
end
valid_uri?(uri_ish_string) click to toggle source
# File lib/rubocop/cop/metrics/line_length.rb, line 257
def valid_uri?(uri_ish_string)
  URI.parse(uri_ish_string)
  true
rescue URI::InvalidURIError, NoMethodError
  false
end