Skip to content

Commit cecd122

Browse files
committed
Add comments and refactor AroundBlockScan methods
1 parent 13739c6 commit cecd122

File tree

5 files changed

+127
-73
lines changed

5 files changed

+127
-73
lines changed

lib/syntax_suggest/around_block_scan.rb

Lines changed: 121 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,35 +38,64 @@ def initialize(code_lines:, block:)
3838
@before_array = []
3939
@stop_after_kw = false
4040

41-
@skip_hidden = false
42-
@skip_empty = false
41+
@force_add_hidden = false
42+
@force_add_empty = false
4343
end
4444

45-
def skip(name)
46-
case name
47-
when :hidden?
48-
@skip_hidden = true
49-
when :empty?
50-
@skip_empty = true
51-
else
52-
raise "Unsupported skip #{name}"
53-
end
45+
# When using this flag, `scan_while` will
46+
# bypass the block it's given and always add a
47+
# line that responds truthy to `CodeLine#hidden?`
48+
#
49+
# Lines are hidden when they've been evaluated by
50+
# the parser as part of a block and found to contain
51+
# valid code.
52+
def force_add_hidden
53+
@force_add_hidden = true
54+
self
55+
end
56+
57+
# When using this flag, `scan_while` will
58+
# bypass the block it's given and always add a
59+
# line that responds truthy to `CodeLine#empty?`
60+
#
61+
# Empty lines contain no code, only whitespace such
62+
# as leading spaces a newline.
63+
def force_add_empty
64+
@force_add_empty = true
5465
self
5566
end
5667

68+
# Tells `scan_while` to look for mismatched keyword/end-s
69+
#
70+
# When scanning up, if we see more keywords then end-s it will
71+
# stop. This might happen when scanning outside of a method body.
72+
# the first scan line up would be a keyword and this setting would
73+
# trigger a stop.
74+
#
75+
# When scanning down, stop if there are more end-s than keywords.
5776
def stop_after_kw
5877
@stop_after_kw = true
5978
self
6079
end
6180

81+
# Main work method
82+
#
83+
# The scan_while method takes a block that yields lines above and
84+
# below the block. If the yield returns true, the @before_index
85+
# or @after_index are modified to include the matched line.
86+
#
87+
# In addition to yielding individual lines, the internals of this
88+
# object give a mini DSL to handle common situations such as
89+
# stopping if we've found a keyword/end mis-match in one direction
90+
# or the other.
6291
def scan_while
6392
stop_next = false
6493
kw_count = 0
6594
end_count = 0
6695
index = before_lines.reverse_each.take_while do |line|
6796
next false if stop_next
68-
next true if @skip_hidden && line.hidden?
69-
next true if @skip_empty && line.empty?
97+
next true if @force_add_hidden && line.hidden?
98+
next true if @force_add_empty && line.empty?
7099

71100
kw_count += 1 if line.is_kw?
72101
end_count += 1 if line.is_end?
@@ -86,8 +115,8 @@ def scan_while
86115
end_count = 0
87116
index = after_lines.take_while do |line|
88117
next false if stop_next
89-
next true if @skip_hidden && line.hidden?
90-
next true if @skip_empty && line.empty?
118+
next true if @force_add_hidden && line.hidden?
119+
next true if @force_add_empty && line.empty?
91120

92121
kw_count += 1 if line.is_kw?
93122
end_count += 1 if line.is_end?
@@ -104,6 +133,33 @@ def scan_while
104133
self
105134
end
106135

136+
# Shows surrounding kw/end pairs
137+
#
138+
# The purpose of showing these extra pairs is due to cases
139+
# of ambiguity when only one visible line is matched.
140+
#
141+
# For example:
142+
#
143+
# 1 class Dog
144+
# 2 def bark
145+
# 4 def eat
146+
# 5 end
147+
# 6 end
148+
#
149+
# In this case either line 2 could be missing an `end` or
150+
# line 4 was an extra line added by mistake (it happens).
151+
#
152+
# When we detect the above problem it shows the issue
153+
# as only being on line 2
154+
#
155+
# 2 def bark
156+
#
157+
# Showing "neighbor" keyword pairs gives extra context:
158+
#
159+
# 2 def bark
160+
# 4 def eat
161+
# 5 end
162+
#
107163
def capture_neighbor_context
108164
lines = []
109165
kw_count = 0
@@ -145,6 +201,20 @@ def capture_neighbor_context
145201
lines
146202
end
147203

204+
# Shows the context around code provided by "falling" indentation
205+
#
206+
# Converts:
207+
#
208+
# it "foo" do
209+
#
210+
# into:
211+
#
212+
# class OH
213+
# def hello
214+
# it "foo" do
215+
# end
216+
# end
217+
#
148218
def on_falling_indent
149219
last_indent = @orig_indent
150220
before_lines.reverse_each do |line|
@@ -213,18 +283,31 @@ def lookahead_balance_one_line
213283
self
214284
end
215285

286+
# Finds code lines at the same or greater indentation and adds them
287+
# to the block
216288
def scan_neighbors_not_empty
217289
scan_while { |line| line.not_empty? && line.indent >= @orig_indent }
218290
end
219291

292+
# Returns the next line to be scanned above the current block.
293+
# Returns `nil` if at the top of the document already
220294
def next_up
221295
@code_lines[before_index.pred]
222296
end
223297

298+
# Returns the next line to be scanned below the current block.
299+
# Returns `nil` if at the bottom of the document already
224300
def next_down
225301
@code_lines[after_index.next]
226302
end
227303

304+
# Scan blocks based on indentation of next line above/below block
305+
#
306+
# Determines indentaion of the next line above/below the current block.
307+
#
308+
# Normally this is called when a block has expanded to capture all "neighbors"
309+
# at the same (or greater) indentation and needs to expand out. For example
310+
# the `def/end` lines surrounding a method.
228311
def scan_adjacent_indent
229312
before_after_indent = []
230313
before_after_indent << (next_up&.indent || 0)
@@ -236,6 +319,16 @@ def scan_adjacent_indent
236319
self
237320
end
238321

322+
# TODO: Doc or delete
323+
#
324+
# I don't remember why this is needed, but it's called in code_context.
325+
# It's related to the implementation of `capture_neighbor_context` somehow
326+
# and that display improvement is only triggered when there's one visible line
327+
#
328+
# I think the primary purpose is to not include the current line in the
329+
# logic evaluation of `capture_neighbor_context`. If that's true, then
330+
# we should fix that method to handle this logic instead of only using
331+
# it in one place and together.
239332
def start_at_next_line
240333
before_index
241334
after_index
@@ -244,26 +337,39 @@ def start_at_next_line
244337
self
245338
end
246339

340+
# Return the currently matched lines as a `CodeBlock`
341+
#
342+
# When a `CodeBlock` is created it will gather metadata about
343+
# itself, so this is not a free conversion. Avoid allocating
344+
# more CodeBlock's than needed
247345
def code_block
248346
CodeBlock.new(lines: lines)
249347
end
250348

349+
# Returns the lines matched by the current scan as an
350+
# array of CodeLines
251351
def lines
252352
@code_lines[before_index..after_index]
253353
end
254354

355+
# Gives the index of the first line currently scanned
255356
def before_index
256357
@before_index ||= @orig_before_index
257358
end
258359

360+
# Gives the index of the last line currently scanned
259361
def after_index
260362
@after_index ||= @orig_after_index
261363
end
262364

365+
# Returns an array of all the CodeLines that exist before
366+
# the currently scanned block
263367
private def before_lines
264368
@code_lines[0...before_index] || []
265369
end
266370

371+
# Returns an array of all the CodeLines that exist after
372+
# the currently scanned block
267373
private def after_lines
268374
@code_lines[after_index.next..-1] || []
269375
end

lib/syntax_suggest/block_expand.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def call(block)
6262
# as there's no undo (currently).
6363
def expand_indent(block)
6464
AroundBlockScan.new(code_lines: @code_lines, block: block)
65-
.skip(:hidden?)
65+
.force_add_hidden
6666
.stop_after_kw
6767
.scan_adjacent_indent
6868
.code_block
@@ -126,7 +126,7 @@ def expand_indent(block)
126126
# We try to resolve this edge case with `lookahead_balance_one_line` below.
127127
def expand_neighbors(block)
128128
neighbors = AroundBlockScan.new(code_lines: @code_lines, block: block)
129-
.skip(:hidden?)
129+
.force_add_hidden
130130
.stop_after_kw
131131
.scan_neighbors_not_empty
132132

lib/syntax_suggest/parse_blocks_from_indent_line.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ def initialize(code_lines:)
3636
# Builds blocks from bottom up
3737
def each_neighbor_block(target_line)
3838
scan = AroundBlockScan.new(code_lines: code_lines, block: CodeBlock.new(lines: target_line))
39-
.skip(:empty?)
40-
.skip(:hidden?)
39+
.force_add_empty
40+
.force_add_hidden
4141
.scan_while { |line| line.indent >= target_line.indent }
4242

4343
neighbors = scan.code_block.lines

spec/integration/syntax_suggest_spec.rb

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -207,57 +207,5 @@ def bark
207207
> 4 end
208208
EOM
209209
end
210-
211-
it "comment inside of a method" do
212-
source = <<~'EOM'
213-
class Dog
214-
def bark
215-
# todo
216-
end
217-
218-
def sit
219-
print "sit"
220-
end
221-
end
222-
end # extra end
223-
EOM
224-
225-
io = StringIO.new
226-
SyntaxSuggest.call(
227-
io: io,
228-
source: source
229-
)
230-
out = io.string
231-
expect(out).to include(<<~EOM)
232-
> 1 class Dog
233-
> 9 end
234-
> 10 end # extra end
235-
EOM
236-
end
237-
238-
it "space inside of a method" do
239-
source = <<~'EOM'
240-
class Dog # 1
241-
def bark # 2
242-
243-
end # 4
244-
245-
def sit # 6
246-
print "sit" # 7
247-
end # 8
248-
end # 9
249-
end # extra end
250-
EOM
251-
252-
io = StringIO.new
253-
SyntaxSuggest.call(
254-
io: io,
255-
source: source
256-
)
257-
out = io.string
258-
expect(out).to include(<<~EOM)
259-
> 10 end # extra end
260-
EOM
261-
end
262210
end
263211
end

spec/unit/around_block_scan_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ def foo
149149

150150
block = CodeBlock.new(lines: code_lines[3])
151151
expand = AroundBlockScan.new(code_lines: code_lines, block: block)
152-
expand.skip(:empty?)
153-
expand.skip(:hidden?)
152+
expand.force_add_empty
153+
expand.force_add_hidden
154154
expand.scan_neighbors_not_empty
155155

156156
expect(expand.code_block.to_s).to eq(<<~EOM.indent(4))

0 commit comments

Comments
 (0)