class RuboCop::Cop::Layout::LineLength

rubocop:disable Metrics/ClassLength
}
baz: “0000000000”,
bar: “0000000000”,
foo: “0000000000”,
{
# good (with recommended cops enabled)
bar: “0000000000”, baz: “0000000000”}
{foo: “0000000000”,
# good
{foo: “0000000000”, bar: “0000000000”, baz: “0000000000”}
# bad
@example
is 25:
method calls, etc. For example, let’s say the max columns
Together, these cops will pretty print hashes, arrays,
* ‘Style/BlockDelimiters`
* `Layout/ParameterAlignment`
* `Layout/MultilineMethodParameterLineBreaks`
* `Layout/MultilineMethodArgumentLineBreaks`
* `Layout/MultilineHashKeyLineBreaks`
* `Layout/MultilineHashBraceLayout`
* `Layout/MultilineBlockLayout`
* `Layout/MultilineArrayLineBreaks`
* `Layout/IndentationWidth`
* `Layout/HashAlignment`
* `Layout/FirstParameterIndentation`
* `Layout/FirstHashElementIndentation`
* `Layout/FirstArrayElementIndentation`
* `Layout/FirstArgumentIndentation`
* `Layout/ClosingParenthesisIndentation`
* `Layout/BlockEndNewline`
* `Layout/BlockAlignment`
* `Layout/ArrayAlignment`
* `Layout/ArgumentAlignment`
(Many of these are enabled by default.)
are recommended to further format the broken lines.
If autocorrection is enabled, the following cops
method calls with argument lists.
split across lines. These include arrays, hashes, and
inserting line breaks into expressions that can be safely
It can programmatically shorten certain long lines by
This cop has some autocorrection capabilities.
It also ignores a shebang line by default.
of the `Layout/IndentationStyle` cop.
The tab size is configured in the `IndentationWidth`
The maximum length is configurable.
Checks the length of lines in the source code.

def allow_heredoc?

def allow_heredoc?
  allowed_heredoc
end

def allow_string_split?

def allow_string_split?
  cop_config['SplitStrings']
end

def allowed_combination?(line, uri_range, qualified_name_range)

def allowed_combination?(line, uri_range, qualified_name_range)
  if uri_range && qualified_name_range
    allowed_position?(line, uri_range) && allowed_position?(line, qualified_name_range)
  elsif uri_range
    allowed_position?(line, uri_range)
  elsif qualified_name_range
    allowed_position?(line, qualified_name_range)
  else
    false
  end
end

def allowed_heredoc

def allowed_heredoc
  cop_config['AllowHeredoc']
end

def allowed_line?(line, line_index)

def allowed_line?(line, line_index)
  matches_allowed_pattern?(line) ||
    shebang?(line, line_index) ||
    (heredocs && line_in_permitted_heredoc?(line_index.succ))
end

def breakable_block_range(block_node)

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

def breakable_dstr?(node)

def breakable_dstr?(node)
  # If the `dstr` only contains one child, it cannot be broken
  breakable_string?(node) && !node.child_nodes.one?
end

def breakable_dstr_begin_position(node)

def breakable_dstr_begin_position(node)
  source_range = node.source_range
  source_range.begin_pos if source_range.column < max && source_range.last_column >= max
end

def breakable_range_after_semicolon(semicolon_token)

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

def breakable_range_by_line_index

def breakable_range_by_line_index
  @breakable_range_by_line_index ||= {}
end

def breakable_string?(node)

def breakable_string?(node)
  allow_string_split? &&
    node.single_line? &&
    !node.heredoc? &&
    # TODO: strings inside hashes, kwargs and arrays are currently ignored,
    # but could be considered in the future
    !node.parent&.type?(:pair, :kwoptarg, :array)
end

def breakable_string_delimiters

def breakable_string_delimiters
  @breakable_string_delimiters ||= {}
end

def breakable_string_position(node)

def breakable_string_position(node)
  source_range = node.source_range
  return if source_range.last_column < max
  return unless (pos = breakable_string_range(node))
  pos.end_pos unless pos.end_pos == source_range.begin_pos
end

def breakable_string_range(node)

otherwise, the string will be broken at the line length limit.
If the string contains spaces, use them to determine a place for a clean break;
are not bisected.
Locate where to break a string that is too long, ensuring that escape characters
def breakable_string_range(node)
  source_range = node.source_range
  relevant_substr = largest_possible_string(node)
  if (space_pos = relevant_substr.rindex(/\s/))
    source_range.resize(space_pos + 1)
  elsif (escape_pos = relevant_substr.rindex(/\\(u[\da-f]{0,4}|x[\da-f]{0,2})?\z/))
    source_range.resize(escape_pos)
  else
    adjustment = max - source_range.last_column - 3
    return if adjustment.abs > source_range.size
    source_range.adjust(end_pos: adjustment)
  end
end

def check_directive_line(line, line_index)

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

def check_for_breakable_block(block_node)

def check_for_breakable_block(block_node)
  return unless block_node.single_line?
  return if receiver_contains_heredoc?(block_node)
  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

def check_for_breakable_dstr(node) # rubocop:disable Metrics/AbcSize

rubocop:disable Metrics/AbcSize
def check_for_breakable_dstr(node) # rubocop:disable Metrics/AbcSize
  line_index = node.loc.line - 1
  return if breakable_range_by_line_index[line_index]
  return unless breakable_dstr?(node)
  return unless (delimiter = string_delimiter(node))
  node.each_child_node(:begin).detect do |begin_node|
    next unless (pos = breakable_dstr_begin_position(begin_node))
    breakable_range_by_line_index[line_index] = range_between(pos, pos + 1)
    breakable_string_delimiters[line_index] = delimiter
  end
end

def check_for_breakable_node(node)

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

def check_for_breakable_semicolons(processed_source)

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

def check_for_breakable_str(node)

def check_for_breakable_str(node)
  line_index = node.loc.line - 1
  return if breakable_range_by_line_index[line_index]
  return unless breakable_string?(node)
  return unless (delimiter = string_delimiter(node))
  return unless (pos = breakable_string_position(node))
  breakable_range_by_line_index[line_index] = range_between(pos, pos + 1)
  breakable_string_delimiters[line_index] = delimiter
end

def check_line(line, line_index)

rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
def check_line(line, line_index)
  return if line_length(line) <= max
  return if allowed_line?(line, line_index)
  if allow_rbs_inline_annotation? && rbs_inline_annotation_on_source_line?(line_index)
    return
  end
  if allow_cop_directives? && directive_on_source_line?(line_index)
    return check_directive_line(line, line_index)
  end
  return check_line_for_exemptions(line, line_index) if allow_uri? || allow_qualified_name?
  register_offense(excess_range(nil, line, line_index), line, line_index)
end

def check_line_for_exemptions(line, line_index)

def check_line_for_exemptions(line, line_index)
  uri_range            = range_if_applicable(line, :uri)
  qualified_name_range = range_if_applicable(line, :qualified_name)
  return if allowed_combination?(line, uri_range, qualified_name_range)
  range = uri_range || qualified_name_range
  register_offense(excess_range(range, line, line_index), line, line_index)
end

def excess_range(uri_range, line, line_index)

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

def extract_heredocs(ast)

def extract_heredocs(ast)
  return [] unless ast
  ast.each_node(:any_str).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

def heredocs

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

def highlight_start(line)

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

def largest_possible_string(node)

Find the largest possible substring of a string node to retain before a break
def largest_possible_string(node)
  # The maximum allowed length of a string value is:
  # `Max` - end delimiter (quote) - continuation characters (space and slash)
  max_length = max - 3
  # Offset by the string's starting column so the broken line actually fits
  # within `Max`. When on the same line as its parent, use the column difference;
  # otherwise the string is indented on its own line, so subtract that indentation.
  # (Without this, an indented string under a multi-line parent never shortens
  # below `Max` and the autocorrect loops, inserting empty `"" \` fragments.)
  max_length -= if same_line?(node, node.parent)
                  column_offset_between(node.loc, node.parent.loc)
                else
                  node.loc.column
                end
  node.source[0...(max_length)]
end

def line_in_heredoc?(line_number)

def line_in_heredoc?(line_number)
  heredocs.any? { |range, _delimiter| range.cover?(line_number) }
end

def line_in_permitted_heredoc?(line_number)

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

def max

def max
  cop_config['Max']
end

def on_block(node)

def on_block(node)
  check_for_breakable_block(node)
end

def on_dstr(node)

def on_dstr(node)
  check_for_breakable_dstr(node)
end

def on_investigation_end

def on_investigation_end
  processed_source.lines.each_with_index do |line, line_index|
    check_line(line, line_index)
  end
end

def on_new_investigation

def on_new_investigation
  return unless processed_source.raw_source.include?(';')
  check_for_breakable_semicolons(processed_source)
end

def on_potential_breakable_node(node)

def on_potential_breakable_node(node)
  check_for_breakable_node(node)
end

def on_str(node)

def on_str(node)
  check_for_breakable_str(node)
end

def range_if_applicable(line, type)

def range_if_applicable(line, type)
  return unless type == :uri ? allow_uri? : allow_qualified_name?
  find_excessive_range(line, type)
end

def receiver_contains_heredoc?(node)

def receiver_contains_heredoc?(node)
  return false unless (receiver = node.receiver)
  return true if receiver.any_str_type? && receiver.heredoc?
  receiver.each_descendant(:any_str).any?(&:heredoc?)
end

def register_offense(loc, line, line_index, length: line_length(line))

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)
    insertion = if (delimiter = breakable_string_delimiters[line_index])
                  [delimiter, " \\\n", delimiter].join
                else
                  "\n"
                end
    corrector.insert_before(breakable_range, insertion) unless breakable_range.nil?
  end
end

def shebang?(line, line_index)

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

def string_delimiter(node)

def string_delimiter(node)
  delimiter = if node.loc?(:begin)
                node.loc.begin
              elsif node.parent&.dstr_type? && node.parent.loc?(:begin)
                node.parent.loc.begin
              end&.source
  delimiter if %w[' "].include?(delimiter)
end