Manually merge syntax_suggest-1.1.0

This commit is contained in:
Hiroshi SHIBATA 2023-05-23 10:05:27 +09:00
parent c638ffa700
commit a7d7032100
No known key found for this signature in database
GPG Key ID: F9CF13417264FAC2
10 changed files with 404 additions and 179 deletions

View File

@ -117,133 +117,6 @@ module SyntaxSuggest
self self
end 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 # Scanning is intentionally conservative because
# we have no way of rolling back an agressive block (at this time) # 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 return self if kw_count == end_count # nothing to balance
@scanner.commit_if_changed @scanner.commit_if_changed # Rollback point if we don't find anything to optimize
scan_while { |line| line.empty? }
# 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 # More ends than keywords, check if we can balance expanding up
next_up = @scanner.next_up next_up = @scanner.next_up
next_down = @scanner.next_down next_down = @scanner.next_down
if (end_count - kw_count) == 1 && next_up case end_count - kw_count
if next_up.is_kw? && next_up.indent >= @target_indent when 1
if next_up&.is_kw? && next_up.indent >= @target_indent
@scanner.scan( @scanner.scan(
up: ->(line, _, _) { line == next_up }, up: ->(line, _, _) { line == next_up },
down: ->(line, _, _) { false } down: ->(line, _, _) { false }
) )
@scanner.commit_if_changed @scanner.commit_if_changed
end end
when -1
# More keywords than ends, check if we can balance by expanding down if next_down&.is_end? && next_down.indent >= @target_indent
elsif (kw_count - end_count) == 1 && next_down
if next_down.is_end? && next_down.indent >= @target_indent
@scanner.scan( @scanner.scan(
up: ->(line, _, _) { false }, up: ->(line, _, _) { false },
down: ->(line, _, _) { line == next_down } down: ->(line, _, _) { line == next_down }
@ -300,7 +177,7 @@ module SyntaxSuggest
@scanner.commit_if_changed @scanner.commit_if_changed
end end
end end
# Rollback any uncommitted changes
@scanner.stash_changes @scanner.stash_changes
self self

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,13 @@
# frozen_string_literal: true # 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 module SyntaxSuggest
# Turns a "invalid block(s)" into useful context # Turns a "invalid block(s)" into useful context
# #
@ -81,10 +89,10 @@ module SyntaxSuggest
# end # end
# #
def capture_falling_indent(block) def capture_falling_indent(block)
AroundBlockScan.new( Capture::FallingIndentLines.new(
block: block, block: block,
code_lines: @code_lines code_lines: @code_lines
).on_falling_indent do |line| ).call do |line|
@lines_to_output << line @lines_to_output << line
end end
end end
@ -119,8 +127,10 @@ module SyntaxSuggest
def capture_before_after_kws(block) def capture_before_after_kws(block)
return unless block.visible_lines.count == 1 return unless block.visible_lines.count == 1
around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block) around_lines = Capture::BeforeAfterKeywordEnds.new(
.capture_before_after_kws code_lines: @code_lines,
block: block
).call
around_lines -= block.lines around_lines -= block.lines

View File

@ -3,8 +3,21 @@
module SyntaxSuggest module SyntaxSuggest
# Scans up/down from the given block # 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 class ScanHistory
attr_reader :before_index, :after_index attr_reader :before_index, :after_index
@ -25,6 +38,7 @@ module SyntaxSuggest
# Discards any changes that have not been committed # Discards any changes that have not been committed
def stash_changes def stash_changes
refresh_index refresh_index
self
end end
# Discard changes that have not been committed and revert the last commit # Discard changes that have not been committed and revert the last commit

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module SyntaxSuggest module SyntaxSuggest
VERSION = "1.0.4" VERSION = "1.1.0"
end end

View File

@ -4,43 +4,6 @@ require_relative "../spec_helper"
module SyntaxSuggest module SyntaxSuggest
RSpec.describe AroundBlockScan do 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 it "continues scan from last location even if scan is false" do
source = <<~'EOM' source = <<~'EOM'
print 'omg' print 'omg'

View File

@ -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

View File

@ -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

View File

@ -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