Skip to content

Commit

Permalink
[Fix rubocop#7977] Add Layout/RedundantLineBreak cop
Browse files Browse the repository at this point in the history
Support method calls and assignments.

Disable the cop by default, but enable it for RuboCop sources.
  • Loading branch information
jonas054 committed Apr 6, 2021
1 parent ae1df5b commit 161c054
Show file tree
Hide file tree
Showing 8 changed files with 454 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Expand Up @@ -47,6 +47,9 @@ Layout/EndOfLine:
Layout/ClassStructure:
Enabled: true

Layout/RedundantLineBreak:
Enabled: true

Layout/TrailingWhitespace:
AllowInHeredoc: false

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,10 @@

## master (unreleased)

### New features

* [#7977](https://github.com/rubocop/rubocop/issues/7977): Add `Layout/RedundantLineBreak` cop. ([@jonas054][])

## 1.12.1 (2021-04-04)

### Bug fixes
Expand Down
7 changes: 7 additions & 0 deletions config/default.yml
Expand Up @@ -1101,6 +1101,13 @@ Layout/ParameterAlignment:
# But it can be overridden by setting this parameter
IndentationWidth: ~

Layout/RedundantLineBreak:
Description: >-
Do not break up an expression into multiple lines when it fits
on a single line.
Enabled: false
VersionAdded: '<<next>>'

Layout/RescueEnsureAlignment:
Description: 'Align rescues and ensures correctly.'
Enabled: true
Expand Down
Empty file added legacy-docs/cops.md
Empty file.
Empty file added legacy-docs/cops_layout.md
Empty file.
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -221,6 +221,7 @@
require_relative 'rubocop/cop/layout/multiline_method_definition_brace_layout'
require_relative 'rubocop/cop/layout/multiline_operation_indentation'
require_relative 'rubocop/cop/layout/parameter_alignment'
require_relative 'rubocop/cop/layout/redundant_line_break'
require_relative 'rubocop/cop/layout/rescue_ensure_alignment'
require_relative 'rubocop/cop/layout/space_after_colon'
require_relative 'rubocop/cop/layout/space_after_comma'
Expand Down
95 changes: 95 additions & 0 deletions lib/rubocop/cop/layout/redundant_line_break.rb
@@ -0,0 +1,95 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Layout
# This cop checks whether certain expressions, e.g. method calls, that could fit
# completely on a single line, are broken up into multiple lines unnecessarily.
#
# @example
# # bad
# foo(
# a,
# b
# )
#
# # good
# foo(a, b)
class RedundantLineBreak < Cop
include CheckAssignment

MSG = 'Redundant line break detected.'

def on_send(node) # rubocop:todo Metrics/CyclomaticComplexity
# Include "the whole expression".
node = node.parent while convertible_block?(node) ||
node.parent.is_a?(RuboCop::AST::BinaryOperatorNode) ||
node.parent&.send_type?

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

add_offense(node)
ignore_node(node)
end

def check_assignment(node, _rhs)
return unless offense?(node)

add_offense(node)
ignore_node(node)
end

def autocorrect(node)
->(corrector) { corrector.replace(node.source_range, to_single_line(node.source).strip) }
end

private

def offense?(node)
!single_line?(node) && !too_long?(node) && suitable_as_single_line?(node)
end

def suitable_as_single_line?(node)
!comment_within?(node) &&
node.each_descendant(:if, :case, :kwbegin, :def).none? &&
node.each_descendant(:dstr, :str).none?(&:heredoc?) &&
node.each_descendant(:begin).none? { |b| b.first_line != b.last_line }
end

def convertible_block?(node)
return false unless node.parent&.block_type?

send_node = node.parent&.send_node
send_node.parenthesized? || !send_node.arguments?
end

def comment_within?(node)
processed_source.comments.map(&:loc).map(&:line).any? do |comment_line_number|
comment_line_number >= node.first_line && comment_line_number <= node.last_line
end
end

def single_line?(node)
node.first_line == node.last_line
end

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

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(/\s*\\?\n\s*/, ' ') # Any other line break, with or without backslash
end

def max_line_length
config.for_cop('Layout/LineLength')['Max']
end
end
end
end
end

0 comments on commit 161c054

Please sign in to comment.