Skip to content

Commit

Permalink
Unify empty line cop logic
Browse files Browse the repository at this point in the history
- DRYs things up a bit.
- Ensures each cop correctly handles heredocs.
- Unifies highlighting format.
  * Note: It was very hard to find some sort of unified way to highlight
    the different possible node endings, so I just went with
    highlighting the lowest offending line starting right of any
    whitespace.
- Ensures each cop allows comments immediately following the offending
  line, notably to provide support for rubocop:disable directives
  (especially disable/enable pairs that might surround the block).
  • Loading branch information
dgollahon committed Jun 14, 2018
1 parent 1bf3acd commit 0289cd8
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 78 deletions.
1 change: 1 addition & 0 deletions lib/rubocop-rspec.rb
Expand Up @@ -21,6 +21,7 @@
require_relative 'rubocop/rspec/capybara'
require_relative 'rubocop/rspec/factory_bot'
require_relative 'rubocop/rspec/final_end_location'
require_relative 'rubocop/rspec/blank_line_separation'

RuboCop::RSpec::Inject.defaults!

Expand Down
27 changes: 9 additions & 18 deletions lib/rubocop/cop/rspec/empty_line_after_example_group.rb
Expand Up @@ -24,6 +24,8 @@ module RSpec
# end
#
class EmptyLineAfterExampleGroup < Cop
include RuboCop::RSpec::BlankLineSeparation

MSG = 'Add an empty line after `%<example_group>s`.'.freeze

def_node_matcher :example_group, ExampleGroups::ALL.block_pattern
Expand All @@ -32,24 +34,13 @@ def on_block(node)
return unless example_group(node)
return if node.parent && node.equal?(node.parent.children.last)

return if next_line(node).blank?

add_offense(
node,
location: node.loc.end,
message: format(MSG, example_group: node.method_name)
)
end

def autocorrect(node)
->(corrector) { corrector.insert_after(node.loc.end, "\n") }
end

private

def next_line(node)
send_line = node.loc.end.line
processed_source[send_line]
missing_separating_line(node) do |location|
add_offense(
node,
location: location,
message: format(MSG, example_group: node.method_name)
)
end
end
end
end
Expand Down
34 changes: 2 additions & 32 deletions lib/rubocop/cop/rspec/empty_line_after_final_let.rb
Expand Up @@ -17,8 +17,7 @@ module RSpec
#
# it { does_something }
class EmptyLineAfterFinalLet < Cop
include RangeHelp
include RuboCop::RSpec::FinalEndLocation
include RuboCop::RSpec::BlankLineSeparation

MSG = 'Add an empty line after the last `let` block.'.freeze

Expand All @@ -32,39 +31,10 @@ def on_block(node)
return if latest_let.nil?
return if latest_let.equal?(node.body.children.last)

no_new_line_after(latest_let) do |location|
missing_separating_line(latest_let) do |location|
add_offense(latest_let, location: location)
end
end

def autocorrect(node)
lambda do |corrector|
no_new_line_after(node) do |location|
corrector.insert_after(location.end, "\n")
end
end
end

private

def no_new_line_after(node)
line = final_end_location(node).line
line += 1 while comment_line?(processed_source[line])

return if processed_source[line].blank?
yield offending_loc(node, line)
end

def offending_loc(node, last_line)
offending_line = processed_source[last_line - 1]
if comment_line?(offending_line)
start = offending_line.index('#')
length = offending_line.length - start
source_range(processed_source.buffer, last_line, start, length)
else
node.loc.expression
end
end
end
end
end
Expand Down
27 changes: 9 additions & 18 deletions lib/rubocop/cop/rspec/empty_line_after_hook.rb
Expand Up @@ -34,6 +34,8 @@ module RSpec
# it { does_something }
#
class EmptyLineAfterHook < Cop
include RuboCop::RSpec::BlankLineSeparation

MSG = 'Add an empty line after `%<hook>s`.'.freeze

def_node_matcher :hook?, Hooks::ALL.block_pattern
Expand All @@ -42,24 +44,13 @@ def on_block(node)
return unless hook?(node)
return if node.equal?(node.parent.children.last)

return if next_line(node).blank?

add_offense(
node,
location: :expression,
message: format(MSG, hook: node.method_name)
)
end

def autocorrect(node)
->(corrector) { corrector.insert_after(node.loc.end, "\n") }
end

private

def next_line(node)
send_line = node.loc.end.line
processed_source[send_line]
missing_separating_line(node) do |location|
add_offense(
node,
location: location,
message: format(MSG, hook: node.method_name)
)
end
end
end
end
Expand Down
14 changes: 5 additions & 9 deletions lib/rubocop/cop/rspec/empty_line_after_subject.rb
Expand Up @@ -15,6 +15,8 @@ module RSpec
#
# let(:foo) { bar }
class EmptyLineAfterSubject < Cop
include RuboCop::RSpec::BlankLineSeparation

MSG = 'Add empty line after `subject`.'.freeze

def_node_matcher :subject?, Subject::ALL.block_pattern
Expand All @@ -23,15 +25,9 @@ def on_block(node)
return unless subject?(node) && !in_spec_block?(node)
return if node.equal?(node.parent.children.last)

send_line = node.loc.end.line
next_line = processed_source[send_line]
return if next_line.blank?

add_offense(node, location: :expression, message: MSG)
end

def autocorrect(node)
->(corrector) { corrector.insert_after(node.loc.end, "\n") }
missing_separating_line(node) do |location|
add_offense(node, location: location, message: MSG)
end
end

private
Expand Down
37 changes: 37 additions & 0 deletions lib/rubocop/rspec/blank_line_separation.rb
@@ -0,0 +1,37 @@
module RuboCop
module RSpec
# Helps determine the offending location if there is not a blank line
# following the node. Allows comments to follow directly after.
module BlankLineSeparation
include FinalEndLocation
include RuboCop::Cop::RangeHelp

def missing_separating_line(node)
line = final_end_location(node).line

line += 1 while comment_line?(processed_source[line])

return if processed_source[line].blank?

yield offending_loc(line)
end

def offending_loc(last_line)
offending_line = processed_source[last_line - 1]

content_length = offending_line.lstrip.length
start = offending_line.length - content_length

source_range(processed_source.buffer, last_line, start, content_length)
end

def autocorrect(node)
lambda do |corrector|
missing_separating_line(node) do |location|
corrector.insert_after(location.end, "\n")
end
end
end
end
end
end
11 changes: 11 additions & 0 deletions spec/rubocop/cop/rspec/empty_line_after_example_group_spec.rb
Expand Up @@ -15,6 +15,17 @@
RUBY
end

it 'highlights single line formulations correctly' do
expect_offense(<<-RUBY)
RSpec.describe Foo do
describe('#bar') { }
^^^^^^^^^^^^^^^^^^^^ Add an empty line after `describe`.
describe '#baz' do
end
end
RUBY
end

it 'checks for empty line after context' do
expect_offense(<<-RUBY)
RSpec.context 'foo' do
Expand Down
42 changes: 41 additions & 1 deletion spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb
Expand Up @@ -19,9 +19,9 @@
RSpec.describe User do
let(:a) { a }
let!(:b) do
^^^^^^^^^^^ Add an empty line after the last `let` block.
b
end
^^^ Add an empty line after the last `let` block.
it { expect(a).to eq(b) }
end
RUBY
Expand Down Expand Up @@ -143,6 +143,21 @@
RUBY
end

it 'handles silly HEREDOC offense' do
expect_offense(<<-RUBY)
RSpec.describe 'silly heredoc syntax' do
let(:foo) { <<-BAR }
hello
world
BAR
^^^ Add an empty line after the last `let` block.
it 'has tricky syntax' do
expect(foo).to eql(" hello\n world\n")
end
end
RUBY
end

bad_example = <<-RUBY
RSpec.describe User do
let(:params) { foo }
Expand Down Expand Up @@ -185,6 +200,31 @@
end
RUBY

include_examples 'autocorrect',
bad_example,
good_example

bad_example = <<-RUBY
RSpec.describe User do
let(:params) { <<-DOC }
I'm super annoying!
DOC
it 'has a new line' do
end
end
RUBY

good_example = <<-RUBY
RSpec.describe User do
let(:params) { <<-DOC }
I'm super annoying!
DOC
it 'has a new line' do
end
end
RUBY

include_examples 'autocorrect',
bad_example,
good_example
Expand Down

0 comments on commit 0289cd8

Please sign in to comment.