diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/syntax_suggest/around_block_scan.rb | 149 | ||||
-rw-r--r-- | lib/syntax_suggest/capture/before_after_keyword_ends.rb | 85 | ||||
-rw-r--r-- | lib/syntax_suggest/capture/falling_indent_lines.rb | 71 | ||||
-rw-r--r-- | lib/syntax_suggest/capture_code_context.rb | 18 | ||||
-rw-r--r-- | lib/syntax_suggest/scan_history.rb | 16 | ||||
-rw-r--r-- | lib/syntax_suggest/version.rb | 2 |
6 files changed, 199 insertions, 142 deletions
diff --git a/lib/syntax_suggest/around_block_scan.rb b/lib/syntax_suggest/around_block_scan.rb index 346a7a99ad..ce00431b3a 100644 --- a/lib/syntax_suggest/around_block_scan.rb +++ b/lib/syntax_suggest/around_block_scan.rb @@ -117,133 +117,6 @@ module SyntaxSuggest self end - # Shows surrounding kw/end pairs - # - # The purpose of showing these extra pairs is due to cases - # of ambiguity when only one visible line is matched. - # - # For example: - # - # 1 class Dog - # 2 def bark - # 4 def eat - # 5 end - # 6 end - # - # In this case either line 2 could be missing an `end` or - # line 4 was an extra line added by mistake (it happens). - # - # When we detect the above problem it shows the issue - # as only being on line 2 - # - # 2 def bark - # - # Showing "neighbor" keyword pairs gives extra context: - # - # 2 def bark - # 4 def eat - # 5 end - # - def capture_before_after_kws - lines = [] - up_stop_next = false - down_stop_next = false - @scanner.commit_if_changed - - lines = [] - @scanner.scan( - up: ->(line, kw_count, end_count) { - break if up_stop_next - next true if line.empty? - break if line.indent < @orig_indent - next true if line.indent != @orig_indent - - # If we're going up and have one complete kw/end pair, stop - if kw_count != 0 && kw_count == end_count - lines << line - break - end - - lines << line if line.is_kw? || line.is_end? - }, - down: ->(line, kw_count, end_count) { - break if down_stop_next - next true if line.empty? - break if line.indent < @orig_indent - next true if line.indent != @orig_indent - - # if we're going down and have one complete kw/end pair,stop - if kw_count != 0 && kw_count == end_count - lines << line - break - end - - lines << line if line.is_kw? || line.is_end? - } - ) - @scanner.stash_changes - lines - end - - # Shows the context around code provided by "falling" indentation - # - # - # If this is the original code lines: - # - # class OH - # def hello - # it "foo" do - # end - # end - # - # And this is the line that is captured - # - # it "foo" do - # - # It will yield its surrounding context: - # - # class OH - # def hello - # end - # end - # - # Example: - # - # AroundBlockScan.new( - # block: block, - # code_lines: @code_lines - # ).on_falling_indent do |line| - # @lines_to_output << line - # end - # - def on_falling_indent - last_indent_up = @orig_indent - last_indent_down = @orig_indent - - @scanner.commit_if_changed - @scanner.scan( - up: ->(line, _, _) { - next true if line.empty? - - if line.indent < last_indent_up - yield line - last_indent_up = line.indent - end - true - }, - down: ->(line, _, _) { - next true if line.empty? - if line.indent < last_indent_down - yield line - last_indent_down = line.indent - end - true - } - ) - @scanner.stash_changes - self - end - # Scanning is intentionally conservative because # we have no way of rolling back an agressive block (at this time) # @@ -275,24 +148,28 @@ module SyntaxSuggest return self if kw_count == end_count # nothing to balance - @scanner.commit_if_changed - scan_while { |line| line.empty? } + @scanner.commit_if_changed # Rollback point if we don't find anything to optimize + + # Try to eat up empty lines + @scanner.scan( + up: ->(line, _, _) { line.hidden? || line.empty? }, + down: ->(line, _, _) { line.hidden? || line.empty? } + ) # More ends than keywords, check if we can balance expanding up next_up = @scanner.next_up next_down = @scanner.next_down - if (end_count - kw_count) == 1 && next_up - if next_up.is_kw? && next_up.indent >= @target_indent + case end_count - kw_count + when 1 + if next_up&.is_kw? && next_up.indent >= @target_indent @scanner.scan( up: ->(line, _, _) { line == next_up }, down: ->(line, _, _) { false } ) @scanner.commit_if_changed end - - # More keywords than ends, check if we can balance by expanding down - elsif (kw_count - end_count) == 1 && next_down - if next_down.is_end? && next_down.indent >= @target_indent + when -1 + if next_down&.is_end? && next_down.indent >= @target_indent @scanner.scan( up: ->(line, _, _) { false }, down: ->(line, _, _) { line == next_down } @@ -300,7 +177,7 @@ module SyntaxSuggest @scanner.commit_if_changed end end - + # Rollback any uncommitted changes @scanner.stash_changes self diff --git a/lib/syntax_suggest/capture/before_after_keyword_ends.rb b/lib/syntax_suggest/capture/before_after_keyword_ends.rb new file mode 100644 index 0000000000..f53c57a4d1 --- /dev/null +++ b/lib/syntax_suggest/capture/before_after_keyword_ends.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module SyntaxSuggest + module Capture + # Shows surrounding kw/end pairs + # + # The purpose of showing these extra pairs is due to cases + # of ambiguity when only one visible line is matched. + # + # For example: + # + # 1 class Dog + # 2 def bark + # 4 def eat + # 5 end + # 6 end + # + # In this case either line 2 could be missing an `end` or + # line 4 was an extra line added by mistake (it happens). + # + # When we detect the above problem it shows the issue + # as only being on line 2 + # + # 2 def bark + # + # Showing "neighbor" keyword pairs gives extra context: + # + # 2 def bark + # 4 def eat + # 5 end + # + # + # Example: + # + # lines = BeforeAfterKeywordEnds.new( + # block: block, + # code_lines: code_lines + # ).call() + # + class BeforeAfterKeywordEnds + def initialize(code_lines:, block:) + @scanner = ScanHistory.new(code_lines: code_lines, block: block) + @original_indent = block.current_indent + end + + def call + lines = [] + + @scanner.scan( + up: ->(line, kw_count, end_count) { + next true if line.empty? + break if line.indent < @original_indent + next true if line.indent != @original_indent + + # If we're going up and have one complete kw/end pair, stop + if kw_count != 0 && kw_count == end_count + lines << line + break + end + + lines << line if line.is_kw? || line.is_end? + true + }, + down: ->(line, kw_count, end_count) { + next true if line.empty? + break if line.indent < @original_indent + next true if line.indent != @original_indent + + # if we're going down and have one complete kw/end pair,stop + if kw_count != 0 && kw_count == end_count + lines << line + break + end + + lines << line if line.is_kw? || line.is_end? + true + } + ) + @scanner.stash_changes + + lines + end + end + end +end diff --git a/lib/syntax_suggest/capture/falling_indent_lines.rb b/lib/syntax_suggest/capture/falling_indent_lines.rb new file mode 100644 index 0000000000..1e046b2ba5 --- /dev/null +++ b/lib/syntax_suggest/capture/falling_indent_lines.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module SyntaxSuggest + module Capture + # Shows the context around code provided by "falling" indentation + # + # If this is the original code lines: + # + # class OH + # def hello + # it "foo" do + # end + # end + # + # And this is the line that is captured + # + # it "foo" do + # + # It will yield its surrounding context: + # + # class OH + # def hello + # end + # end + # + # Example: + # + # FallingIndentLines.new( + # block: block, + # code_lines: @code_lines + # ).call do |line| + # @lines_to_output << line + # end + # + class FallingIndentLines + def initialize(code_lines:, block:) + @lines = nil + @scanner = ScanHistory.new(code_lines: code_lines, block: block) + @original_indent = block.current_indent + end + + def call(&yieldable) + last_indent_up = @original_indent + last_indent_down = @original_indent + + @scanner.commit_if_changed + @scanner.scan( + up: ->(line, _, _) { + next true if line.empty? + + if line.indent < last_indent_up + yieldable.call(line) + last_indent_up = line.indent + end + true + }, + down: ->(line, _, _) { + next true if line.empty? + + if line.indent < last_indent_down + yieldable.call(line) + last_indent_down = line.indent + end + true + } + ) + @scanner.stash_changes + end + end + end +end diff --git a/lib/syntax_suggest/capture_code_context.rb b/lib/syntax_suggest/capture_code_context.rb index 71f5b271b5..6dc7047176 100644 --- a/lib/syntax_suggest/capture_code_context.rb +++ b/lib/syntax_suggest/capture_code_context.rb @@ -1,6 +1,14 @@ # frozen_string_literal: true module SyntaxSuggest + module Capture + end +end + +require_relative "capture/falling_indent_lines" +require_relative "capture/before_after_keyword_ends" + +module SyntaxSuggest # Turns a "invalid block(s)" into useful context # # There are three main phases in the algorithm: @@ -81,10 +89,10 @@ module SyntaxSuggest # end # def capture_falling_indent(block) - AroundBlockScan.new( + Capture::FallingIndentLines.new( block: block, code_lines: @code_lines - ).on_falling_indent do |line| + ).call do |line| @lines_to_output << line end end @@ -119,8 +127,10 @@ module SyntaxSuggest def capture_before_after_kws(block) return unless block.visible_lines.count == 1 - around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block) - .capture_before_after_kws + around_lines = Capture::BeforeAfterKeywordEnds.new( + code_lines: @code_lines, + block: block + ).call around_lines -= block.lines diff --git a/lib/syntax_suggest/scan_history.rb b/lib/syntax_suggest/scan_history.rb index 2be4aaf3c0..d15597c440 100644 --- a/lib/syntax_suggest/scan_history.rb +++ b/lib/syntax_suggest/scan_history.rb @@ -3,8 +3,21 @@ module SyntaxSuggest # Scans up/down from the given block # - # You can snapshot a change by committing it and rolling back. + # You can try out a change, stash it, or commit it to save for later # + # Example: + # + # scanner = ScanHistory.new(code_lines: code_lines, block: block) + # scanner.scan( + # up: ->(_, _, _) { true }, + # down: ->(_, _, _) { true } + # ) + # scanner.changed? # => true + # expect(scanner.lines).to eq(code_lines) + # + # scanner.stash_changes + # + # expect(scanner.lines).to_not eq(code_lines) class ScanHistory attr_reader :before_index, :after_index @@ -25,6 +38,7 @@ module SyntaxSuggest # Discards any changes that have not been committed def stash_changes refresh_index + self end # Discard changes that have not been committed and revert the last commit diff --git a/lib/syntax_suggest/version.rb b/lib/syntax_suggest/version.rb index d816cb96cf..ac8c2f62e5 100644 --- a/lib/syntax_suggest/version.rb +++ b/lib/syntax_suggest/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SyntaxSuggest - VERSION = "1.0.4" + VERSION = "1.1.0" end |