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

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

View File

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

View File

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

View File

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

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