Manually merge syntax_suggest-1.1.0
This commit is contained in:
parent
c638ffa700
commit
a7d7032100
@ -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
|
||||
|
85
lib/syntax_suggest/capture/before_after_keyword_ends.rb
Normal file
85
lib/syntax_suggest/capture/before_after_keyword_ends.rb
Normal 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
|
71
lib/syntax_suggest/capture/falling_indent_lines.rb
Normal file
71
lib/syntax_suggest/capture/falling_indent_lines.rb
Normal 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
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SyntaxSuggest
|
||||
VERSION = "1.0.4"
|
||||
VERSION = "1.1.0"
|
||||
end
|
||||
|
@ -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'
|
||||
|
@ -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
|
@ -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
|
114
spec/syntax_suggest/unit/scan_history_spec.rb
Normal file
114
spec/syntax_suggest/unit/scan_history_spec.rb
Normal 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
|
Loading…
x
Reference in New Issue
Block a user