1
1
# frozen_string_literal: true
2
2
3
+ require_relative "scan_history"
4
+
3
5
module SyntaxSuggest
4
6
# This class is useful for exploring contents before and after
5
7
# a block
@@ -24,22 +26,17 @@ module SyntaxSuggest
24
26
# puts scan.before_index # => 0
25
27
# puts scan.after_index # => 3
26
28
#
27
- # Contents can also be filtered using AroundBlockScan#skip
28
- #
29
- # To grab the next surrounding indentation use AroundBlockScan#scan_adjacent_indent
30
29
class AroundBlockScan
31
30
def initialize ( code_lines :, block :)
32
31
@code_lines = code_lines
33
- @orig_before_index = block . lines . first . index
34
- @orig_after_index = block . lines . last . index
35
32
@orig_indent = block . current_indent
36
- @skip_array = [ ]
37
- @after_array = [ ]
38
- @before_array = [ ]
39
- @stop_after_kw = false
40
33
41
- @force_add_hidden = false
34
+ @stop_after_kw = false
42
35
@force_add_empty = false
36
+ @force_add_hidden = false
37
+ @target_indent = nil
38
+
39
+ @scanner = ScanHistory . new ( code_lines : code_lines , block : block )
43
40
end
44
41
45
42
# When using this flag, `scan_while` will
@@ -89,47 +86,34 @@ def stop_after_kw
89
86
# stopping if we've found a keyword/end mis-match in one direction
90
87
# or the other.
91
88
def scan_while
92
- stop_next = false
93
- kw_count = 0
94
- end_count = 0
95
- index = before_lines . reverse_each . take_while do |line |
96
- next false if stop_next
97
- next true if @force_add_hidden && line . hidden?
98
- next true if @force_add_empty && line . empty?
89
+ stop_next_up = false
90
+ stop_next_down = false
99
91
100
- kw_count += 1 if line . is_kw?
101
- end_count += 1 if line . is_end?
102
- if @stop_after_kw && kw_count > end_count
103
- stop_next = true
104
- end
92
+ @scanner . scan (
93
+ up : -> ( line , kw_count , end_count ) {
94
+ next false if stop_next_up
95
+ next true if @force_add_hidden && line . hidden?
96
+ next true if @force_add_empty && line . empty?
105
97
106
- yield line
107
- end . last &.index
98
+ if @stop_after_kw && kw_count > end_count
99
+ stop_next_up = true
100
+ end
108
101
109
- if index && index < before_index
110
- @before_index = index
111
- end
112
-
113
- stop_next = false
114
- kw_count = 0
115
- end_count = 0
116
- index = after_lines . take_while do |line |
117
- next false if stop_next
118
- next true if @force_add_hidden && line . hidden?
119
- next true if @force_add_empty && line . empty?
102
+ yield line
103
+ } ,
104
+ down : -> ( line , kw_count , end_count ) {
105
+ next false if stop_next_down
106
+ next true if @force_add_hidden && line . hidden?
107
+ next true if @force_add_empty && line . empty?
120
108
121
- kw_count += 1 if line . is_kw?
122
- end_count += 1 if line . is_end?
123
- if @stop_after_kw && end_count > kw_count
124
- stop_next = true
125
- end
109
+ if @stop_after_kw && end_count > kw_count
110
+ stop_next_down = true
111
+ end
126
112
127
- yield line
128
- end . last &.index
113
+ yield line
114
+ }
115
+ )
129
116
130
- if index && index > after_index
131
- @after_index = index
132
- end
133
117
self
134
118
end
135
119
@@ -160,79 +144,104 @@ def scan_while
160
144
# 4 def eat
161
145
# 5 end
162
146
#
163
- def capture_neighbor_context
147
+ def capture_before_after_kws
164
148
lines = [ ]
165
- kw_count = 0
166
- end_count = 0
167
- before_lines . reverse_each do |line |
168
- next if line . empty?
169
- break if line . indent < @orig_indent
170
- next if line . indent != @orig_indent
171
-
172
- kw_count += 1 if line . is_kw?
173
- end_count += 1 if line . is_end?
174
- if kw_count != 0 && kw_count == end_count
175
- lines << line
176
- break
177
- end
178
-
179
- lines << line if line . is_kw? || line . is_end?
180
- end
181
-
182
- lines . reverse!
183
-
184
- kw_count = 0
185
- end_count = 0
186
- after_lines . each do |line |
187
- next if line . empty?
188
- break if line . indent < @orig_indent
189
- next if line . indent != @orig_indent
190
-
191
- kw_count += 1 if line . is_kw?
192
- end_count += 1 if line . is_end?
193
- if kw_count != 0 && kw_count == end_count
194
- lines << line
195
- break
196
- end
197
-
198
- lines << line if line . is_kw? || line . is_end?
199
- end
149
+ up_stop_next = false
150
+ down_stop_next = false
151
+ @scanner . commit_if_changed
200
152
153
+ lines = [ ]
154
+ @scanner . scan (
155
+ up : -> ( line , kw_count , end_count ) {
156
+ break if up_stop_next
157
+ next true if line . empty?
158
+ break if line . indent < @orig_indent
159
+ next true if line . indent != @orig_indent
160
+
161
+ # If we're going up and have one complete kw/end pair, stop
162
+ if kw_count != 0 && kw_count == end_count
163
+ lines << line
164
+ break
165
+ end
166
+
167
+ lines << line if line . is_kw? || line . is_end?
168
+ } ,
169
+ down : -> ( line , kw_count , end_count ) {
170
+ break if down_stop_next
171
+ next true if line . empty?
172
+ break if line . indent < @orig_indent
173
+ next true if line . indent != @orig_indent
174
+
175
+ # if we're going down and have one complete kw/end pair,stop
176
+ if kw_count != 0 && kw_count == end_count
177
+ lines << line
178
+ break
179
+ end
180
+
181
+ lines << line if line . is_kw? || line . is_end?
182
+ }
183
+ )
184
+ @scanner . try_rollback
201
185
lines
202
186
end
203
187
204
188
# Shows the context around code provided by "falling" indentation
205
189
#
206
- # Converts:
207
190
#
191
+ # If this is the original code lines:
192
+ #
193
+ # class OH
194
+ # def hello
208
195
# it "foo" do
196
+ # end
197
+ # end
209
198
#
210
- # into:
199
+ # And this is the line that is captured
200
+ #
201
+ # it "foo" do
202
+ #
203
+ # It will yield its surrounding context:
211
204
#
212
205
# class OH
213
206
# def hello
214
- # it "foo" do
215
207
# end
216
208
# end
217
209
#
210
+ # Example:
211
+ #
212
+ # AroundBlockScan.new(
213
+ # block: block,
214
+ # code_lines: @code_lines
215
+ # ).on_falling_indent do |line|
216
+ # @lines_to_output << line
217
+ # end
218
+ #
218
219
def on_falling_indent
219
- last_indent = @orig_indent
220
- before_lines . reverse_each do |line |
221
- next if line . empty?
222
- if line . indent < last_indent
223
- yield line
224
- last_indent = line . indent
225
- end
226
- end
227
-
228
- last_indent = @orig_indent
229
- after_lines . each do |line |
230
- next if line . empty?
231
- if line . indent < last_indent
232
- yield line
233
- last_indent = line . indent
234
- end
235
- end
220
+ last_indent_up = @orig_indent
221
+ last_indent_down = @orig_indent
222
+
223
+ @scanner . commit_if_changed
224
+ @scanner . scan (
225
+ up : -> ( line , _ , _ ) {
226
+ next true if line . empty?
227
+
228
+ if line . indent < last_indent_up
229
+ yield line
230
+ last_indent_up = line . indent
231
+ end
232
+ true
233
+ } ,
234
+ down : -> ( line , _ , _ ) {
235
+ next true if line . empty?
236
+ if line . indent < last_indent_down
237
+ yield line
238
+ last_indent_down = line . indent
239
+ end
240
+ true
241
+ }
242
+ )
243
+ @scanner . try_rollback
244
+ self
236
245
end
237
246
238
247
# Scanning is intentionally conservative because
@@ -267,38 +276,33 @@ def lookahead_balance_one_line
267
276
return self if kw_count == end_count # nothing to balance
268
277
269
278
# More ends than keywords, check if we can balance expanding up
279
+ next_up = @scanner . next_up
280
+ next_down = @scanner . next_down
270
281
if ( end_count - kw_count ) == 1 && next_up
271
- return self unless next_up . is_kw?
272
- return self unless next_up . indent >= @orig_indent
273
-
274
- @before_index = next_up . index
282
+ if next_up . is_kw? && next_up . indent >= @orig_indent
283
+ @scanner . scan (
284
+ up : -> ( line , _ , _ ) { line == next_up } ,
285
+ down : -> ( line , _ , _ ) { false }
286
+ )
287
+ end
275
288
276
289
# More keywords than ends, check if we can balance by expanding down
277
290
elsif ( kw_count - end_count ) == 1 && next_down
278
- return self unless next_down . is_end?
279
- return self unless next_down . indent >= @orig_indent
280
-
281
- @after_index = next_down . index
291
+ if next_down . is_end? && next_down . indent >= @orig_indent
292
+ @scanner . scan (
293
+ up : -> ( line , _ , _ ) { false } ,
294
+ down : -> ( line ) { line == next_down }
295
+ )
296
+ end
282
297
end
283
298
self
284
299
end
285
300
286
301
# Finds code lines at the same or greater indentation and adds them
287
302
# to the block
288
303
def scan_neighbors_not_empty
289
- scan_while { |line | line . not_empty? && line . indent >= @orig_indent }
290
- end
291
-
292
- # Returns the next line to be scanned above the current block.
293
- # Returns `nil` if at the top of the document already
294
- def next_up
295
- @code_lines [ before_index . pred ]
296
- end
297
-
298
- # Returns the next line to be scanned below the current block.
299
- # Returns `nil` if at the bottom of the document already
300
- def next_down
301
- @code_lines [ after_index . next ]
304
+ @target_indent = @orig_indent
305
+ scan_while { |line | line . not_empty? && line . indent >= @target_indent }
302
306
end
303
307
304
308
# Scan blocks based on indentation of next line above/below block
@@ -310,11 +314,12 @@ def next_down
310
314
# the `def/end` lines surrounding a method.
311
315
def scan_adjacent_indent
312
316
before_after_indent = [ ]
313
- before_after_indent << ( next_up &.indent || 0 )
314
- before_after_indent << ( next_down &.indent || 0 )
315
317
316
- indent = before_after_indent . min
317
- scan_while { |line | line . not_empty? && line . indent >= indent }
318
+ before_after_indent << ( @scanner . next_up &.indent || 0 )
319
+ before_after_indent << ( @scanner . next_down &.indent || 0 )
320
+
321
+ @target_indent = before_after_indent . min
322
+ scan_while { |line | line . not_empty? && line . indent >= @target_indent }
318
323
319
324
self
320
325
end
@@ -331,29 +336,12 @@ def code_block
331
336
# Returns the lines matched by the current scan as an
332
337
# array of CodeLines
333
338
def lines
334
- @code_lines [ before_index ..after_index ]
335
- end
336
-
337
- # Gives the index of the first line currently scanned
338
- def before_index
339
- @before_index ||= @orig_before_index
340
- end
341
-
342
- # Gives the index of the last line currently scanned
343
- def after_index
344
- @after_index ||= @orig_after_index
345
- end
346
-
347
- # Returns an array of all the CodeLines that exist before
348
- # the currently scanned block
349
- private def before_lines
350
- @code_lines [ 0 ...before_index ] || [ ]
339
+ @scanner . lines
351
340
end
352
341
353
- # Returns an array of all the CodeLines that exist after
354
- # the currently scanned block
355
- private def after_lines
356
- @code_lines [ after_index . next ..-1 ] || [ ]
342
+ # Managable rspec errors
343
+ def inspect
344
+ "#<#{ self . class } :0x0000123843lol >"
357
345
end
358
346
end
359
347
end
0 commit comments