summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2023-05-23 10:05:27 +0900
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2023-05-23 10:05:47 +0900
commita7d70321005d302d9b5aaa2b83569aa899a5aaa9 (patch)
treedd890c3152627ad08d1be2a2cbe511e30d701026 /lib
parentc638ffa700b7a9a28ba29a5b88d934efaba0e575 (diff)
Manually merge syntax_suggest-1.1.0
Diffstat (limited to 'lib')
-rw-r--r--lib/syntax_suggest/around_block_scan.rb149
-rw-r--r--lib/syntax_suggest/capture/before_after_keyword_ends.rb85
-rw-r--r--lib/syntax_suggest/capture/falling_indent_lines.rb71
-rw-r--r--lib/syntax_suggest/capture_code_context.rb18
-rw-r--r--lib/syntax_suggest/scan_history.rb16
-rw-r--r--lib/syntax_suggest/version.rb2
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