diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b49269..bff4b5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## HEAD (unreleased) -- Replace some references in tests with foobar style code (https://github.com/zombocom/dead_end/pull/38) +- Fix bug where empty lines were interpreted to have a zero indentation (https://github.com/zombocom/dead_end/pull/39) ## 1.0.1 diff --git a/lib/dead_end/code_block.rb b/lib/dead_end/code_block.rb index 786fb9f..ee7df64 100644 --- a/lib/dead_end/code_block.rb +++ b/lib/dead_end/code_block.rb @@ -17,10 +17,12 @@ module DeadEnd # # class CodeBlock + UNSET = Object.new.freeze attr_reader :lines def initialize(lines: []) @lines = Array(lines) + @valid = UNSET end def visible_lines @@ -68,7 +70,8 @@ def invalid? end def valid? - DeadEnd.valid?(self.to_s) + return @valid if @valid != UNSET + @valid = DeadEnd.valid?(self.to_s) end def to_s diff --git a/lib/dead_end/code_frontier.rb b/lib/dead_end/code_frontier.rb index 1ba6291..4634159 100644 --- a/lib/dead_end/code_frontier.rb +++ b/lib/dead_end/code_frontier.rb @@ -10,20 +10,20 @@ module DeadEnd # sorted and then the frontier can be filtered. Large blocks that totally contain a # smaller block will cause the smaller block to be evicted. # - # CodeFrontier#<< - # CodeFrontier#pop + # CodeFrontier#<<(block) # Adds block to frontier + # CodeFrontier#pop # Removes block from frontier # # ## Knowing where we can go # - # Internally it keeps track of an "indent hash" which is exposed via `next_indent_line` + # Internally it keeps track of "unvisited" lines which is exposed via `next_indent_line` # when called this will return a line of code with the most indentation. # - # This line of code can be used to build a CodeBlock via and then when that code block - # is added back to the frontier, then the lines in the code block are removed from the - # indent hash so we don't double-create the same block. + # This line of code can be used to build a CodeBlock and then when that code block + # is added back to the frontier, then the lines are removed from the + # "unvisited" so we don't double-create the same block. # - # CodeFrontier#next_indent_line - # CodeFrontier#register_indent_block + # CodeFrontier#next_indent_line # Shows next line + # CodeFrontier#register_indent_block(block) # Removes lines from unvisited # # ## Knowing when to stop # @@ -42,13 +42,7 @@ class CodeFrontier def initialize(code_lines: ) @code_lines = code_lines @frontier = [] - @indent_hash = {} - code_lines.each do |line| - next if line.empty? - - @indent_hash[line.indent] ||= [] - @indent_hash[line.indent] << line - end + @unvisited_lines = @code_lines.sort_by(&:indent_index) end def count @@ -75,38 +69,31 @@ def pop return @frontier.pop end - def indent_hash_indent - @indent_hash.keys.sort.last - end - def next_indent_line - indent = @indent_hash.keys.sort.last - @indent_hash[indent]&.first + @unvisited_lines.last end def expand? return false if @frontier.empty? - return true if @indent_hash.empty? + return true if @unvisited_lines.empty? frontier_indent = @frontier.last.current_indent - hash_indent = @indent_hash.keys.sort.last + unvisited_indent= next_indent_line.indent if ENV["DEBUG"] puts "```" puts @frontier.last.to_s puts "```" puts " @frontier indent: #{frontier_indent}" - puts " @hash indent: #{hash_indent}" + puts " @unvisited indent: #{unvisited_indent}" end - frontier_indent >= hash_indent + # Expand all blocks before moving to unvisited lines + frontier_indent >= unvisited_indent end def register_indent_block(block) - block.lines.each do |line| - @indent_hash[line.indent]&.delete(line) - end - @indent_hash.select! {|k, v| !v.empty?} + @unvisited_lines -= block.lines self end diff --git a/lib/dead_end/code_line.rb b/lib/dead_end/code_line.rb index 5c874b7..86148f7 100644 --- a/lib/dead_end/code_line.rb +++ b/lib/dead_end/code_line.rb @@ -36,9 +36,14 @@ class CodeLine def initialize(line: , index:) @original_line = line.freeze @line = @original_line - @empty = line.strip.empty? + if line.strip.empty? + @empty = true + @indent = 0 + else + @empty = false + @indent = SpaceCount.indent(line) + end @index = index - @indent = SpaceCount.indent(line) @status = nil # valid, invalid, unknown @invalid = false @@ -72,6 +77,10 @@ def trailing_slash? @is_trailing_slash end + def indent_index + @indent_index ||= [indent, index] + end + def <=>(b) self.index <=> b.index end @@ -92,15 +101,6 @@ def is_end? @is_end end - def mark_invalid - @invalid = true - self - end - - def marked_invalid? - @invalid - end - def mark_invisible @line = "" self diff --git a/lib/dead_end/code_search.rb b/lib/dead_end/code_search.rb index 7fc97e1..8c357b7 100644 --- a/lib/dead_end/code_search.rb +++ b/lib/dead_end/code_search.rb @@ -75,7 +75,7 @@ def push(block, name: ) record(block: block, name: name) if block.valid? - block.lines.each(&:mark_invisible) + block.mark_invisible frontier << block else frontier << block @@ -92,7 +92,7 @@ def sweep(block:, name: ) # Parses the most indented lines into blocks that are marked # and added to the frontier - def add_invalid_blocks + def visit_new_blocks max_indent = frontier.next_indent_line&.indent while (line = frontier.next_indent_line) && (line.indent == max_indent) @@ -145,7 +145,7 @@ def call if frontier.expand? expand_invalid_block else - add_invalid_blocks + visit_new_blocks end end diff --git a/spec/unit/code_line_spec.rb b/spec/unit/code_line_spec.rb index 1ba22f3..92e7bd5 100644 --- a/spec/unit/code_line_spec.rb +++ b/spec/unit/code_line_spec.rb @@ -41,20 +41,6 @@ module DeadEnd expect(line.is_kw?).to be_truthy end - it "can be marked as invalid or valid" do - code_lines = code_line_array(<<~EOM) - def foo - Array(value) |x| - end - end - EOM - - expect(code_lines[0].marked_invalid?).to be_falsey - code_lines[0].mark_invalid - expect(code_lines[0].marked_invalid?).to be_truthy - - end - it "ignores marked lines" do code_lines = code_line_array(<<~EOM) def foo @@ -110,5 +96,14 @@ def foo expect(code_lines.map(&:indent)).to eq([0, 2, 4, 2, 0]) end + + it "doesn't count empty lines as having an indentation" do + code_lines = code_line_array(<<~EOM) + + + EOM + + expect(code_lines.map(&:indent)).to eq([0, 0]) + end end end diff --git a/spec/unit/code_search_spec.rb b/spec/unit/code_search_spec.rb index 6e1fac1..9fec231 100644 --- a/spec/unit/code_search_spec.rb +++ b/spec/unit/code_search_spec.rb @@ -277,7 +277,7 @@ def foo it "stacked ends 2" do search = CodeSearch.new(<<~EOM) - def lol + def cat blerg end @@ -285,7 +285,7 @@ def lol end # one end # two - def lol + def dog end EOM search.call @@ -445,7 +445,7 @@ def foo puts 'lol' end EOM - search.add_invalid_blocks + search.visit_new_blocks expect(search.code_lines.join).to eq(<<~EOM) def foo