class RuboCop::Cop::Layout::RedundantLineBreak

Checks whether certain expressions, e.g. method calls, that could fit completely on a single line, are broken up into multiple lines unnecessarily.

@example any configuration

# bad
foo(
  a,
  b
)

puts 'string that fits on ' \
     'a single line'

things
  .select { |thing| thing.cond? }
  .join('-')

# good
foo(a, b)

puts 'string that fits on a single line'

things.select { |thing| thing.cond? }.join('-')

@example InspectBlocks: false (default)

# good
foo(a) do |x|
  puts x
end

@example InspectBlocks: true

# bad
foo(a) do |x|
  puts x
end

# good
foo(a) { |x| puts x }

Constants

MSG

Public Instance Methods

on_csend(node)
Alias for: on_send
on_lvasgn(node) click to toggle source
Calls superclass method RuboCop::Cop::CheckAssignment#on_lvasgn
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 51
def on_lvasgn(node)
  super unless end_with_percent_blank_string?(processed_source)
end
on_send(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 55
def on_send(node)
  # Include "the whole expression".
  node = node.parent while node.parent&.send_type? ||
                           convertible_block?(node) ||
                           node.parent.is_a?(RuboCop::AST::BinaryOperatorNode)

  return unless offense?(node) && !part_of_ignored_node?(node)

  register_offense(node)
end
Also aliased as: on_csend

Private Instance Methods

check_assignment(node, _rhs) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 73
def check_assignment(node, _rhs)
  return unless offense?(node)

  register_offense(node)
end
comment_within?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 133
def comment_within?(node)
  comment_line_numbers = processed_source.comments.map { |comment| comment.loc.line }

  comment_line_numbers.any? do |comment_line_number|
    comment_line_number >= node.first_line && comment_line_number <= node.last_line
  end
end
configured_to_not_be_inspected?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 103
def configured_to_not_be_inspected?(node)
  return true if other_cop_takes_precedence?(node)

  !cop_config['InspectBlocks'] && (node.block_type? ||
                                   any_descendant?(node, :block, &:multiline?))
end
convertible_block?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 127
def convertible_block?(node)
  parent = node.parent
  parent&.block_type? && node == parent.send_node &&
    (node.parenthesized? || !node.arguments?)
end
end_with_percent_blank_string?(processed_source) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 69
def end_with_percent_blank_string?(processed_source)
  processed_source.buffer.source.end_with?("%\n\n")
end
index_access_call_chained?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 97
def index_access_call_chained?(node)
  return false unless node.send_type? && node.method?(:[])

  node.children.first.send_type? && node.children.first.method?(:[])
end
max_line_length() click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 155
def max_line_length
  config.for_cop('Layout/LineLength')['Max']
end
offense?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 86
def offense?(node)
  return false if !node.multiline? || too_long?(node) || !suitable_as_single_line?(node)
  return require_backslash?(node) if node.and_type? || node.or_type?

  !index_access_call_chained?(node) && !configured_to_not_be_inspected?(node)
end
other_cop_takes_precedence?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 110
def other_cop_takes_precedence?(node)
  single_line_block_chain_enabled? && any_descendant?(node, :block) do |block_node|
    block_node.parent.send_type? && block_node.parent.loc.dot && !block_node.multiline?
  end
end
register_offense(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 79
def register_offense(node)
  add_offense(node) do |corrector|
    corrector.replace(node, to_single_line(node.source).strip)
  end
  ignore_node(node)
end
require_backslash?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 93
def require_backslash?(node)
  processed_source.lines[node.loc.operator.line - 1].end_with?('\\')
end
single_line_block_chain_enabled?() click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 116
def single_line_block_chain_enabled?
  @config.for_cop('Layout/SingleLineBlockChain')['Enabled']
end
suitable_as_single_line?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 120
def suitable_as_single_line?(node)
  !comment_within?(node) &&
    node.each_descendant(:if, :case, :kwbegin, :def, :defs).none? &&
    node.each_descendant(:dstr, :str).none? { |n| n.heredoc? || n.value.include?("\n") } &&
    node.each_descendant(:begin, :sym).none? { |b| !b.single_line? }
end
to_single_line(source) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 146
def to_single_line(source)
  source
    .gsub(/" *\\\n\s*'/, %q(" + ')) # Double quote, backslash, and then single quote
    .gsub(/' *\\\n\s*"/, %q(' + ")) # Single quote, backslash, and then double quote
    .gsub(/(["']) *\\\n\s*\1/, '')  # Double or single quote, backslash, then same quote
    .gsub(/\n\s*(?=(&)?\.\w)/, '')  # Extra space within method chaining which includes `&.`
    .gsub(/\s*\\?\n\s*/, ' ')       # Any other line break, with or without backslash
end
too_long?(node) click to toggle source
# File lib/rubocop/cop/layout/redundant_line_break.rb, line 141
def too_long?(node)
  lines = processed_source.lines[(node.first_line - 1)...node.last_line]
  to_single_line(lines.join("\n")).length > max_line_length
end