diff options
author | Hiroshi SHIBATA <[email protected]> | 2023-05-23 10:05:27 +0900 |
---|---|---|
committer | Hiroshi SHIBATA <[email protected]> | 2023-05-23 10:05:47 +0900 |
commit | a7d70321005d302d9b5aaa2b83569aa899a5aaa9 (patch) | |
tree | dd890c3152627ad08d1be2a2cbe511e30d701026 | |
parent | c638ffa700b7a9a28ba29a5b88d934efaba0e575 (diff) |
Manually merge syntax_suggest-1.1.0
-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 | ||||
-rw-r--r-- | spec/syntax_suggest/unit/around_block_scan_spec.rb | 37 | ||||
-rw-r--r-- | spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb | 47 | ||||
-rw-r--r-- | spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb | 44 | ||||
-rw-r--r-- | spec/syntax_suggest/unit/scan_history_spec.rb | 114 |
10 files changed, 404 insertions, 179 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 diff --git a/spec/syntax_suggest/unit/around_block_scan_spec.rb b/spec/syntax_suggest/unit/around_block_scan_spec.rb index c9241bc8ee..d6756448bd 100644 --- a/spec/syntax_suggest/unit/around_block_scan_spec.rb +++ b/spec/syntax_suggest/unit/around_block_scan_spec.rb @@ -4,43 +4,6 @@ require_relative "../spec_helper" module SyntaxSuggest RSpec.describe AroundBlockScan do - it "on_falling_indent" do - source = <<~'EOM' - class OH - def lol - print 'lol - end - - def hello - it "foo" do - end - - def yolo - print 'haha' - end - end - EOM - - code_lines = CleanDocument.new(source: source).call.lines - block = CodeBlock.new(lines: code_lines[6]) - - lines = [] - AroundBlockScan.new( - block: block, - code_lines: code_lines - ).on_falling_indent do |line| - lines << line - end - lines.sort! - - expect(lines.join).to eq(<<~'EOM') - class OH - def hello - end - end - EOM - end - it "continues scan from last location even if scan is false" do source = <<~'EOM' print 'omg' diff --git a/spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb b/spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb new file mode 100644 index 0000000000..02d9be4387 --- /dev/null +++ b/spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require_relative "../../spec_helper" + +module SyntaxSuggest + RSpec.describe Capture::BeforeAfterKeywordEnds do + it "before after keyword ends" do + source = <<~'EOM' + def nope + print 'not me' + end + + def lol + print 'lol' + end + + def hello # 8 + + def yolo + print 'haha' + end + + def nada + print 'nope' + end + EOM + + code_lines = CleanDocument.new(source: source).call.lines + block = CodeBlock.new(lines: code_lines[8]) + + expect(block.to_s).to include("def hello") + + lines = Capture::BeforeAfterKeywordEnds.new( + block: block, + code_lines: code_lines + ).call + lines.sort! + + expect(lines.join).to include(<<~'EOM') + def lol + end + def yolo + end + EOM + end + end +end diff --git a/spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb b/spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb new file mode 100644 index 0000000000..61d1642d97 --- /dev/null +++ b/spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require_relative "../../spec_helper" + +module SyntaxSuggest + RSpec.describe Capture::FallingIndentLines do + it "on_falling_indent" do + source = <<~'EOM' + class OH + def lol + print 'lol + end + + def hello + it "foo" do + end + + def yolo + print 'haha' + end + end + EOM + + code_lines = CleanDocument.new(source: source).call.lines + block = CodeBlock.new(lines: code_lines[6]) + + lines = [] + Capture::FallingIndentLines.new( + block: block, + code_lines: code_lines + ).call do |line| + lines << line + end + lines.sort! + + expect(lines.join).to eq(<<~'EOM') + class OH + def hello + end + end + EOM + end + end +end diff --git a/spec/syntax_suggest/unit/scan_history_spec.rb b/spec/syntax_suggest/unit/scan_history_spec.rb new file mode 100644 index 0000000000..0e75ac66ce --- /dev/null +++ b/spec/syntax_suggest/unit/scan_history_spec.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require_relative "../spec_helper" + +module SyntaxSuggest + RSpec.describe ScanHistory do + it "retains commits" do + source = <<~'EOM' + class OH # 0 + def lol # 1 + print 'lol # 2 + end # 3 + + def hello # 5 + it "foo" do # 6 + end # 7 + + def yolo # 8 + print 'haha' # 9 + end # 10 + end + EOM + + code_lines = CleanDocument.new(source: source).call.lines + block = CodeBlock.new(lines: code_lines[6]) + + scanner = ScanHistory.new(code_lines: code_lines, block: block) + scanner.scan(up: ->(_, _, _) { true }, down: ->(_, _, _) { true }) + + expect(scanner.changed?).to be_truthy + scanner.commit_if_changed + expect(scanner.changed?).to be_falsey + + expect(scanner.lines).to eq(code_lines) + + scanner.stash_changes # Assert does nothing if changes are already committed + expect(scanner.lines).to eq(code_lines) + + scanner.revert_last_commit + + expect(scanner.lines.join).to eq(code_lines[6].to_s) + end + + it "is stashable" do + source = <<~'EOM' + class OH # 0 + def lol # 1 + print 'lol # 2 + end # 3 + + def hello # 5 + it "foo" do # 6 + end # 7 + + def yolo # 8 + print 'haha' # 9 + end # 10 + end + EOM + + code_lines = CleanDocument.new(source: source).call.lines + block = CodeBlock.new(lines: code_lines[6]) + + scanner = ScanHistory.new(code_lines: code_lines, block: block) + scanner.scan(up: ->(_, _, _) { true }, down: ->(_, _, _) { true }) + + expect(scanner.lines).to eq(code_lines) + expect(scanner.changed?).to be_truthy + expect(scanner.next_up).to be_falsey + expect(scanner.next_down).to be_falsey + + scanner.stash_changes + + expect(scanner.changed?).to be_falsey + + expect(scanner.next_up).to eq(code_lines[5]) + expect(scanner.lines.join).to eq(code_lines[6].to_s) + expect(scanner.next_down).to eq(code_lines[7]) + end + + it "doesnt change if you dont't change it" do + source = <<~'EOM' + class OH # 0 + def lol # 1 + print 'lol # 2 + end # 3 + + def hello # 5 + it "foo" do # 6 + end # 7 + + def yolo # 8 + print 'haha' # 9 + end # 10 + end + EOM + + code_lines = CleanDocument.new(source: source).call.lines + block = CodeBlock.new(lines: code_lines[6]) + + scanner = ScanHistory.new(code_lines: code_lines, block: block) + + lines = scanner.lines + expect(scanner.changed?).to be_falsey + expect(scanner.next_up).to eq(code_lines[5]) + expect(scanner.next_down).to eq(code_lines[7]) + + expect(scanner.stash_changes.lines).to eq(lines) + expect(scanner.revert_last_commit.lines).to eq(lines) + + expect(scanner.scan(up: ->(_, _, _) { false }, down: ->(_, _, _) { false }).lines).to eq(lines) + end + end +end |