Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## HEAD (unreleased)

- Fix bug where empty lines were interpreted to have a zero indentation (https://github.com/zombocom/dead_end/pull/39)
- Better results when missing "end" comes at the end of a capturing block (such as a class or module definition) (https://github.com/zombocom/dead_end/issues/32)

## 1.0.1

Expand Down
82 changes: 68 additions & 14 deletions lib/dead_end/capture_code_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,9 @@ def initialize(blocks: , code_lines:)

def call
@blocks.each do |block|
around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
.start_at_next_line
.capture_neighbor_context

around_lines -= block.lines

@lines_to_output.concat(around_lines)

AroundBlockScan.new(
block: block,
code_lines: @code_lines,
).on_falling_indent do |line|
@lines_to_output << line
end
capture_last_end_same_indent(block)
capture_before_after_kws(block)
capture_falling_indent(block)
end

@lines_to_output.select!(&:not_empty?)
Expand All @@ -58,5 +47,70 @@ def call

return @lines_to_output
end

def capture_falling_indent(block)
AroundBlockScan.new(
block: block,
code_lines: @code_lines,
).on_falling_indent do |line|
@lines_to_output << line
end
end

def capture_before_after_kws(block)
around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
.start_at_next_line
.capture_neighbor_context

around_lines -= block.lines

@lines_to_output.concat(around_lines)
end

# Problems heredocs are back in play
def capture_last_end_same_indent(block)
start_index = block.visible_lines.first.index
lines = @code_lines[start_index..block.lines.last.index]
kw_end_lines = lines.select {|line| line.indent == block.current_indent && (line.is_end? || line.is_kw?) }


# TODO handle case of heredocs showing up here
#
# Due to https://github.com/zombocom/dead_end/issues/32
# There's a special case where a keyword right before the last
# end of a valid block accidentally ends up identifying that the problem
# was with the block instead of before it. To handle that
# special case, we can re-parse back through the internals of blocks
# and if they have mis-matched keywords and ends show the last one
end_lines = kw_end_lines.select(&:is_end?)
end_lines.each_with_index do |end_line, i|
start_index = i.zero? ? 0 : end_lines[i-1].index
end_index = end_line.index - 1
lines = @code_lines[start_index..end_index]

stop_next = false
kw_count = 0
end_count = 0
lines = lines.reverse.take_while do |line|
next false if stop_next

end_count += 1 if line.is_end?
kw_count += 1 if line.is_kw?

stop_next = true if !kw_count.zero? && kw_count >= end_count
true
end.reverse

next unless kw_count > end_count

lines = lines.select {|line| line.is_kw? || line.is_end? }

next if lines.empty?

@lines_to_output << end_line
@lines_to_output << lines.first
@lines_to_output << lines.last
end
end
end
end
3 changes: 2 additions & 1 deletion lib/dead_end/code_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ def initialize(line: , index:)
end
end

@is_comment = lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
return if @is_comment
@is_kw = (kw_count - end_count) > 0
@is_end = (end_count - kw_count) > 0
@is_comment = lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
@is_trailing_slash = lex.last.token == TRAILING_SLASH
end

Expand Down
149 changes: 149 additions & 0 deletions spec/unit/capture_code_context_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# frozen_string_literal: true

require_relative "../spec_helper.rb"

module DeadEnd
RSpec.describe CaptureCodeContext do
it "shows ends of captured block" do
lines = fixtures_dir.join("rexe.rb.txt").read.lines
lines.delete_at(148 - 1)
source = lines.join

search = CodeSearch.new(source)
search.call

# expect(search.invalid_blocks.join.strip).to eq('class Dog')
display = CaptureCodeContext.new(
blocks: search.invalid_blocks,
code_lines: search.code_lines
)
lines = display.call

lines = lines.sort.map(&:original)
expect(lines.join).to eq(<<~EOM)
class Rexe
VERSION = '1.5.1'
PROJECT_URL = 'https://github.com/keithrbennett/rexe'
class Lookups
def format_requires
end
class CommandLineParser
end
end
EOM
end

it "shows ends of captured block" do
source = <<~'EOM'
class Dog
def bark
puts "woof"
end
EOM
search = CodeSearch.new(source)
search.call

expect(search.invalid_blocks.join.strip).to eq('class Dog')
display = CaptureCodeContext.new(
blocks: search.invalid_blocks,
code_lines: search.code_lines
)
lines = display.call.sort.map(&:original)
expect(lines.join).to eq(<<~EOM)
class Dog
def bark
end
EOM
end

it "captures surrounding context on falling indent" do
syntax_string = <<~EOM
class Blerg
end

class OH

def hello
it "foo" do
end
end

class Zerg
end
EOM

search = CodeSearch.new(syntax_string)
search.call

expect(search.invalid_blocks.join.strip).to eq('it "foo" do')

display = CaptureCodeContext.new(
blocks: search.invalid_blocks,
code_lines: search.code_lines
)
lines = display.call.sort.map(&:original)
expect(lines.join).to eq(<<~EOM)
class OH
def hello
it "foo" do
end
end
EOM
end

it "captures surrounding context on same indent" do
syntax_string = <<~EOM
class Blerg
end
class OH

def nope
end

def lol
end

it "foo"
puts "here"
end

def haha
end

def nope
end
end

class Zerg
end
EOM

search = CodeSearch.new(syntax_string)
search.call

code_context = CaptureCodeContext.new(
blocks: search.invalid_blocks,
code_lines: search.code_lines
)

# Finds lines previously hidden
lines = code_context.call
# expect(lines.select(&:hidden?).map(&:line_number)).to eq([11, 12])

out = DisplayCodeWithLineNumbers.new(
lines: lines,
).call

expect(out).to eq(<<~EOM.indent(2))
3 class OH
8 def lol
9 end
11 it "foo"
13 end
15 def haha
16 end
20 end
EOM
end
end
end
90 changes: 0 additions & 90 deletions spec/unit/display_invalid_blocks_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,96 +67,6 @@ def meow
expect(display.banner).to include("DeadEnd: Missing `end` detected")
end

it "captures surrounding context on same indent" do
syntax_string = <<~EOM
class Blerg
end
class OH

def nope
end

def lol
end

it "foo"
puts "here"
end

def haha
end

def nope
end
end

class Zerg
end
EOM

search = CodeSearch.new(syntax_string)
search.call

code_context = CaptureCodeContext.new(
blocks: search.invalid_blocks,
code_lines: search.code_lines
)

# Finds lines previously hidden
lines = code_context.call
# expect(lines.select(&:hidden?).map(&:line_number)).to eq([11, 12])

out = DisplayCodeWithLineNumbers.new(
lines: lines,
).call

expect(out).to eq(<<~EOM.indent(2))
3 class OH
8 def lol
9 end
11 it "foo"
13 end
15 def haha
16 end
20 end
EOM
end

it "captures surrounding context on falling indent" do
syntax_string = <<~EOM
class Blerg
end

class OH

def hello
it "foo" do
end
end

class Zerg
end
EOM

search = CodeSearch.new(syntax_string)
search.call

expect(search.invalid_blocks.join.strip).to eq('it "foo" do')

display = CaptureCodeContext.new(
blocks: search.invalid_blocks,
code_lines: search.code_lines
)
lines = display.call.sort
expect(lines.join).to eq(<<~EOM)
class OH
def hello
it "foo" do
end
end
EOM
end

it "works with valid code" do
syntax_string = <<~EOM
class OH
Expand Down
Loading