123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Layout::LineLength

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/line_length.rb

Overview

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.)

  • Layout/ArgumentAlignment

  • Layout/ArrayAlignment

  • Layout/BlockAlignment

  • Layout/BlockEndNewline

  • Layout/ClosingParenthesisIndentation

  • Layout/FirstArgumentIndentation

  • Layout/FirstArrayElementIndentation

  • Layout/FirstHashElementIndentation

  • Layout/FirstParameterIndentation

  • Layout/HashAlignment

  • Layout/IndentationWidth

  • Layout/MultilineArrayLineBreaks

  • Layout/MultilineBlockLayout

  • Layout/MultilineHashBraceLayout

  • Layout/MultilineHashKeyLineBreaks

  • Layout/MultilineMethodArgumentLineBreaks

  • Layout/MultilineMethodParameterLineBreaks

  • Layout/ParameterAlignment

  • Style/BlockDelimiters

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

Examples:

# 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",
}

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

::RuboCop::Cop::RangeHelp - Included

BYTE_ORDER_MARK, NOT_GIVEN

::RuboCop::Cop::Alignment - Included

SPACE

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.

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

Returns a url to view this cops documentation online.

.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::LineLengthHelp - Included

::RuboCop::Cop::Alignment - Included

::RuboCop::Cop::RangeHelp - Included

#add_range, #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

::RuboCop::Cop::AllowedPattern - Included

::RuboCop::Cop::CheckLineBreakable - 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

Actually private methods.

#use_corrector

::RuboCop::Cop::AutocorrectLogic - Included

::RuboCop::Cop::IgnoredNode - Included

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Instance Attribute Details

#allow_heredoc?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 219

def allow_heredoc?
  allowed_heredoc
end

#breakable_range (rw, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 102

attr_accessor :breakable_range

Instance Method Details

#allowed_heredoc (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 223

def allowed_heredoc
  cop_config['AllowHeredoc']
end

#allowed_line?(line, line_index) ⇒ Boolean (private)

[ GitHub ]

  
# 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) (private)

[ GitHub ]

  
# 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) (private)

[ GitHub ]

  
# 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 (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 156

def 
  @breakable_range_by_line_index ||= {}
end

#check_directive_line(line, line_index) (private)

[ GitHub ]

  
# 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) (private)

[ GitHub ]

  
# 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

  [line_index] = range_between(pos, pos + 1)
end

#check_for_breakable_node(node) (private)

[ GitHub ]

  
# 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 = [line_index]
  return if existing

  [line_index] = range
end

#check_for_breakable_semicolons(processed_source) (private)

[ GitHub ]

  
# 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)
    [range.line - 1] = range if range
  end
end

#check_line(line, line_index) (private)

[ GitHub ]

  
# 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) (private)

[ GitHub ]

  
# 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) (private)

[ GitHub ]

  
# 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) (private)

[ GitHub ]

  
# 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 (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 160

def heredocs
  @heredocs ||= extract_heredocs(processed_source.ast)
end

#highlight_start(line) (private)

[ GitHub ]

  
# 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) ⇒ Boolean (private)

[ GitHub ]

  
# 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) ⇒ Boolean (private)

[ GitHub ]

  
# 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 (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 215

def max
  cop_config['Max']
end

#on_array(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 83

alias on_array on_potential_breakable_node

#on_block(node) Also known as: #on_numblock

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 74

def on_block(node)
  check_for_breakable_block(node)
end

#on_def(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 86

alias on_def on_potential_breakable_node

#on_hash(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 84

alias on_hash on_potential_breakable_node

#on_investigation_end

[ GitHub ]

  
# 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

[ GitHub ]

  
# 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.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 78

alias on_numblock on_block

#on_potential_breakable_node(node) Also known as: #on_array, #on_hash, #on_send, #on_def

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 80

def on_potential_breakable_node(node)
  check_for_breakable_node(node)
end

#on_send(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 85

alias on_send on_potential_breakable_node

#register_offense(loc, line, line_index, length: line_length(line)) (private)

[ GitHub ]

  
# 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 = [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) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/line_length.rb', line 189

def shebang?(line, line_index)
  line_index.zero? && line.start_with?('#!')
end