parser 2.5.1.0 → 3.0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/parser.rb +4 -0
- data/lib/parser/all.rb +3 -0
- data/lib/parser/ast/processor.rb +49 -1
- data/lib/parser/base.rb +30 -6
- data/lib/parser/builders/default.rb +586 -29
- data/lib/parser/context.rb +17 -0
- data/lib/parser/current.rb +34 -7
- data/lib/parser/current_arg_stack.rb +46 -0
- data/lib/parser/diagnostic.rb +1 -1
- data/lib/parser/diagnostic/engine.rb +1 -2
- data/lib/parser/lexer.rb +23780 -0
- data/lib/parser/lexer/dedenter.rb +52 -49
- data/lib/parser/lexer/literal.rb +4 -0
- data/lib/parser/lexer/stack_state.rb +4 -0
- data/lib/parser/macruby.rb +6149 -0
- data/lib/parser/max_numparam_stack.rb +56 -0
- data/lib/parser/messages.rb +74 -44
- data/lib/parser/meta.rb +13 -3
- data/lib/parser/ruby18.rb +5667 -0
- data/lib/parser/ruby19.rb +6092 -0
- data/lib/parser/ruby20.rb +6527 -0
- data/lib/parser/ruby21.rb +6578 -0
- data/lib/parser/ruby22.rb +6613 -0
- data/lib/parser/ruby23.rb +6624 -0
- data/lib/parser/ruby24.rb +6694 -0
- data/lib/parser/ruby25.rb +6662 -0
- data/lib/parser/ruby26.rb +6676 -0
- data/lib/parser/ruby27.rb +7862 -0
- data/lib/parser/ruby28.rb +8047 -0
- data/lib/parser/ruby30.rb +8060 -0
- data/lib/parser/ruby31.rb +8075 -0
- data/lib/parser/rubymotion.rb +6086 -0
- data/lib/parser/runner.rb +36 -2
- data/lib/parser/runner/ruby_parse.rb +2 -2
- data/lib/parser/runner/ruby_rewrite.rb +2 -2
- data/lib/parser/source/buffer.rb +54 -29
- data/lib/parser/source/comment.rb +18 -5
- data/lib/parser/source/comment/associator.rb +34 -11
- data/lib/parser/source/map.rb +1 -1
- data/lib/parser/source/map/method_definition.rb +25 -0
- data/lib/parser/source/range.rb +20 -4
- data/lib/parser/source/tree_rewriter.rb +146 -16
- data/lib/parser/source/tree_rewriter/action.rb +137 -28
- data/lib/parser/static_environment.rb +14 -0
- data/lib/parser/tree_rewriter.rb +3 -3
- data/lib/parser/variables_stack.rb +36 -0
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +13 -21
- metadata +34 -98
- data/.gitignore +0 -32
- data/.travis.yml +0 -21
- data/.yardopts +0 -21
- data/CHANGELOG.md +0 -909
- data/CONTRIBUTING.md +0 -17
- data/Gemfile +0 -10
- data/README.md +0 -301
- data/Rakefile +0 -165
- data/doc/AST_FORMAT.md +0 -1718
- data/doc/CUSTOMIZATION.md +0 -37
- data/doc/INTERNALS.md +0 -21
- data/doc/css/.gitkeep +0 -0
- data/doc/css/common.css +0 -68
- data/lib/parser/lexer.rl +0 -2376
- data/lib/parser/macruby.y +0 -2198
- data/lib/parser/ruby18.y +0 -1934
- data/lib/parser/ruby19.y +0 -2175
- data/lib/parser/ruby20.y +0 -2353
- data/lib/parser/ruby21.y +0 -2357
- data/lib/parser/ruby22.y +0 -2364
- data/lib/parser/ruby23.y +0 -2370
- data/lib/parser/ruby24.y +0 -2395
- data/lib/parser/ruby25.y +0 -2392
- data/lib/parser/ruby26.y +0 -2392
- data/lib/parser/rubymotion.y +0 -2182
- data/test/bug_163/fixtures/input.rb +0 -5
- data/test/bug_163/fixtures/output.rb +0 -5
- data/test/bug_163/rewriter.rb +0 -20
- data/test/helper.rb +0 -52
- data/test/parse_helper.rb +0 -315
- data/test/racc_coverage_helper.rb +0 -133
- data/test/test_base.rb +0 -31
- data/test/test_current.rb +0 -27
- data/test/test_diagnostic.rb +0 -96
- data/test/test_diagnostic_engine.rb +0 -62
- data/test/test_encoding.rb +0 -99
- data/test/test_lexer.rb +0 -3537
- data/test/test_lexer_stack_state.rb +0 -78
- data/test/test_parse_helper.rb +0 -80
- data/test/test_parser.rb +0 -6968
- data/test/test_runner_rewrite.rb +0 -47
- data/test/test_source_buffer.rb +0 -162
- data/test/test_source_comment.rb +0 -36
- data/test/test_source_comment_associator.rb +0 -367
- data/test/test_source_map.rb +0 -15
- data/test/test_source_range.rb +0 -172
- data/test/test_source_rewriter.rb +0 -541
- data/test/test_source_rewriter_action.rb +0 -46
- data/test/test_source_tree_rewriter.rb +0 -173
- data/test/test_static_environment.rb +0 -45
- data/test/using_tree_rewriter/fixtures/input.rb +0 -3
- data/test/using_tree_rewriter/fixtures/output.rb +0 -3
- data/test/using_tree_rewriter/using_tree_rewriter.rb +0 -9
@@ -117,6 +117,71 @@ module Parser
|
|
117
117
|
@action_root = TreeRewriter::Action.new(all_encompassing_range, @enforcer)
|
118
118
|
end
|
119
119
|
|
120
|
+
##
|
121
|
+
# Returns true iff no (non trivial) update has been recorded
|
122
|
+
#
|
123
|
+
# @return [Boolean]
|
124
|
+
#
|
125
|
+
def empty?
|
126
|
+
@action_root.empty?
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Merges the updates of argument with the receiver.
|
131
|
+
# Policies of the receiver are used.
|
132
|
+
# This action is atomic in that it won't change the receiver
|
133
|
+
# unless it succeeds.
|
134
|
+
#
|
135
|
+
# @param [Rewriter] with
|
136
|
+
# @return [Rewriter] self
|
137
|
+
# @raise [ClobberingError] when clobbering is detected
|
138
|
+
#
|
139
|
+
def merge!(with)
|
140
|
+
raise 'TreeRewriter are not for the same source_buffer' unless
|
141
|
+
source_buffer == with.source_buffer
|
142
|
+
|
143
|
+
@action_root = @action_root.combine(with.action_root)
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Returns a new rewriter that consists of the updates of the received
|
149
|
+
# and the given argument. Policies of the receiver are used.
|
150
|
+
#
|
151
|
+
# @param [Rewriter] with
|
152
|
+
# @return [Rewriter] merge of receiver and argument
|
153
|
+
# @raise [ClobberingError] when clobbering is detected
|
154
|
+
#
|
155
|
+
def merge(with)
|
156
|
+
dup.merge!(with)
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# For special cases where one needs to merge a rewriter attached to a different source_buffer
|
161
|
+
# or that needs to be offset. Policies of the receiver are used.
|
162
|
+
#
|
163
|
+
# @param [TreeRewriter] rewriter from different source_buffer
|
164
|
+
# @param [Integer] offset
|
165
|
+
# @return [Rewriter] self
|
166
|
+
# @raise [IndexError] if action ranges (once offset) don't fit the current buffer
|
167
|
+
#
|
168
|
+
def import!(foreign_rewriter, offset: 0)
|
169
|
+
return self if foreign_rewriter.empty?
|
170
|
+
|
171
|
+
contracted = foreign_rewriter.action_root.contract
|
172
|
+
merge_effective_range = ::Parser::Source::Range.new(
|
173
|
+
@source_buffer,
|
174
|
+
contracted.range.begin_pos + offset,
|
175
|
+
contracted.range.end_pos + offset,
|
176
|
+
)
|
177
|
+
check_range_validity(merge_effective_range)
|
178
|
+
|
179
|
+
merge_with = contracted.moved(@source_buffer, offset)
|
180
|
+
|
181
|
+
@action_root = @action_root.combine(merge_with)
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
120
185
|
##
|
121
186
|
# Replaces the code of the source range `range` with `content`.
|
122
187
|
#
|
@@ -133,8 +198,8 @@ module Parser
|
|
133
198
|
# Inserts the given strings before and after the given range.
|
134
199
|
#
|
135
200
|
# @param [Range] range
|
136
|
-
# @param [String
|
137
|
-
# @param [String
|
201
|
+
# @param [String, nil] insert_before
|
202
|
+
# @param [String, nil] insert_after
|
138
203
|
# @return [Rewriter] self
|
139
204
|
# @raise [ClobberingError] when clobbering is detected
|
140
205
|
#
|
@@ -185,28 +250,62 @@ module Parser
|
|
185
250
|
# @return [String]
|
186
251
|
#
|
187
252
|
def process
|
188
|
-
source = @source_buffer.source
|
189
|
-
adjustment = 0
|
253
|
+
source = @source_buffer.source
|
190
254
|
|
255
|
+
chunks = []
|
256
|
+
last_end = 0
|
191
257
|
@action_root.ordered_replacements.each do |range, replacement|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
source[begin_pos...end_pos] = replacement
|
196
|
-
|
197
|
-
adjustment += replacement.length - range.length
|
258
|
+
chunks << source[last_end...range.begin_pos] << replacement
|
259
|
+
last_end = range.end_pos
|
198
260
|
end
|
261
|
+
chunks << source[last_end...source.length]
|
262
|
+
chunks.join
|
263
|
+
end
|
264
|
+
|
265
|
+
##
|
266
|
+
# Returns a representation of the rewriter as an ordered list of replacements.
|
267
|
+
#
|
268
|
+
# rewriter.as_replacements # => [ [1...1, '('],
|
269
|
+
# [2...4, 'foo'],
|
270
|
+
# [5...6, ''],
|
271
|
+
# [6...6, '!'],
|
272
|
+
# [10...10, ')'],
|
273
|
+
# ]
|
274
|
+
#
|
275
|
+
# This representation is sufficient to recreate the result of `process` but it is
|
276
|
+
# not sufficient to recreate completely the rewriter for further merging/actions.
|
277
|
+
# See `as_nested_actions`
|
278
|
+
#
|
279
|
+
# @return [Array<Range, String>] an ordered list of pairs of range & replacement
|
280
|
+
#
|
281
|
+
def as_replacements
|
282
|
+
@action_root.ordered_replacements
|
283
|
+
end
|
199
284
|
|
200
|
-
|
285
|
+
##
|
286
|
+
# Returns a representation of the rewriter as nested insertions (:wrap) and replacements.
|
287
|
+
#
|
288
|
+
# rewriter.as_actions # =>[ [:wrap, 1...10, '(', ')'],
|
289
|
+
# [:wrap, 2...6, '', '!'], # aka "insert_after"
|
290
|
+
# [:replace, 2...4, 'foo'],
|
291
|
+
# [:replace, 5...6, ''], # aka "removal"
|
292
|
+
# ],
|
293
|
+
#
|
294
|
+
# Contrary to `as_replacements`, this representation is sufficient to recreate exactly
|
295
|
+
# the rewriter.
|
296
|
+
#
|
297
|
+
# @return [Array<(Symbol, Range, String{, String})>]
|
298
|
+
#
|
299
|
+
def as_nested_actions
|
300
|
+
@action_root.nested_actions
|
201
301
|
end
|
202
302
|
|
203
303
|
##
|
204
304
|
# Provides a protected block where a sequence of multiple rewrite actions
|
205
305
|
# are handled atomically. If any of the actions failed by clobbering,
|
206
|
-
# all the actions are rolled back.
|
306
|
+
# all the actions are rolled back. Transactions can be nested.
|
207
307
|
#
|
208
308
|
# @raise [RuntimeError] when no block is passed
|
209
|
-
# @raise [RuntimeError] when already in a transaction
|
210
309
|
#
|
211
310
|
def transaction
|
212
311
|
unless block_given?
|
@@ -231,6 +330,11 @@ module Parser
|
|
231
330
|
@in_transaction
|
232
331
|
end
|
233
332
|
|
333
|
+
# :nodoc:
|
334
|
+
def inspect
|
335
|
+
"#<#{self.class} #{source_buffer.name}: #{action_summary}>"
|
336
|
+
end
|
337
|
+
|
234
338
|
##
|
235
339
|
# @api private
|
236
340
|
# @deprecated Use insert_after or wrap
|
@@ -256,8 +360,34 @@ module Parser
|
|
256
360
|
|
257
361
|
extend Deprecation
|
258
362
|
|
363
|
+
protected
|
364
|
+
|
365
|
+
attr_reader :action_root
|
366
|
+
|
259
367
|
private
|
260
368
|
|
369
|
+
def action_summary
|
370
|
+
replacements = as_replacements
|
371
|
+
case replacements.size
|
372
|
+
when 0 then return 'empty'
|
373
|
+
when 1..3 then #ok
|
374
|
+
else
|
375
|
+
replacements = replacements.first(3)
|
376
|
+
suffix = '…'
|
377
|
+
end
|
378
|
+
parts = replacements.map do |(range, str)|
|
379
|
+
if str.empty? # is this a deletion?
|
380
|
+
"-#{range.to_range}"
|
381
|
+
elsif range.size == 0 # is this an insertion?
|
382
|
+
"+#{str.inspect}@#{range.begin_pos}"
|
383
|
+
else # it is a replacement
|
384
|
+
"^#{str.inspect}@#{range.to_range}"
|
385
|
+
end
|
386
|
+
end
|
387
|
+
parts << suffix if suffix
|
388
|
+
parts.join(', ')
|
389
|
+
end
|
390
|
+
|
261
391
|
ACTIONS = %i[accept warn raise].freeze
|
262
392
|
def check_policy_validity
|
263
393
|
invalid = @policy.values - ACTIONS
|
@@ -266,14 +396,14 @@ module Parser
|
|
266
396
|
|
267
397
|
def combine(range, attributes)
|
268
398
|
range = check_range_validity(range)
|
269
|
-
action = TreeRewriter::Action.new(range, @enforcer, attributes)
|
399
|
+
action = TreeRewriter::Action.new(range, @enforcer, **attributes)
|
270
400
|
@action_root = @action_root.combine(action)
|
271
401
|
self
|
272
402
|
end
|
273
403
|
|
274
404
|
def check_range_validity(range)
|
275
405
|
if range.begin_pos < 0 || range.end_pos > @source_buffer.source.size
|
276
|
-
raise IndexError, "The range #{range} is outside the bounds of the source"
|
406
|
+
raise IndexError, "The range #{range.to_range} is outside the bounds of the source"
|
277
407
|
end
|
278
408
|
range
|
279
409
|
end
|
@@ -281,7 +411,7 @@ module Parser
|
|
281
411
|
def enforce_policy(event)
|
282
412
|
return if @policy[event] == :accept
|
283
413
|
return unless (values = yield)
|
284
|
-
trigger_policy(event, values)
|
414
|
+
trigger_policy(event, **values)
|
285
415
|
end
|
286
416
|
|
287
417
|
POLICY_TO_LEVEL = {warn: :warning, raise: :error}.freeze
|
@@ -7,7 +7,7 @@ module Parser
|
|
7
7
|
#
|
8
8
|
# Actions are arranged in a tree and get combined so that:
|
9
9
|
# children are strictly contained by their parent
|
10
|
-
# sibblings all disjoint from one another
|
10
|
+
# sibblings all disjoint from one another and ordered
|
11
11
|
# only actions with replacement==nil may have children
|
12
12
|
#
|
13
13
|
class TreeRewriter::Action
|
@@ -25,30 +25,77 @@ module Parser
|
|
25
25
|
freeze
|
26
26
|
end
|
27
27
|
|
28
|
-
# Assumes action.children.empty?
|
29
28
|
def combine(action)
|
30
|
-
return self
|
29
|
+
return self if action.empty? # Ignore empty action
|
31
30
|
do_combine(action)
|
32
31
|
end
|
33
32
|
|
33
|
+
def empty?
|
34
|
+
@insert_before.empty? &&
|
35
|
+
@insert_after.empty? &&
|
36
|
+
@children.empty? &&
|
37
|
+
(@replacement == nil || (@replacement.empty? && @range.empty?))
|
38
|
+
end
|
39
|
+
|
34
40
|
def ordered_replacements
|
35
41
|
reps = []
|
36
42
|
reps << [@range.begin, @insert_before] unless @insert_before.empty?
|
37
43
|
reps << [@range, @replacement] if @replacement
|
38
|
-
reps.concat(@children.
|
44
|
+
reps.concat(@children.flat_map(&:ordered_replacements))
|
39
45
|
reps << [@range.end, @insert_after] unless @insert_after.empty?
|
40
46
|
reps
|
41
47
|
end
|
42
48
|
|
49
|
+
def nested_actions
|
50
|
+
actions = []
|
51
|
+
actions << [:wrap, @range, @insert_before, @insert_after] if !@insert_before.empty? ||
|
52
|
+
!@insert_after.empty?
|
53
|
+
actions << [:replace, @range, @replacement] if @replacement
|
54
|
+
actions.concat(@children.flat_map(&:nested_actions))
|
55
|
+
end
|
56
|
+
|
43
57
|
def insertion?
|
44
58
|
!insert_before.empty? || !insert_after.empty? || (replacement && !replacement.empty?)
|
45
59
|
end
|
46
60
|
|
61
|
+
##
|
62
|
+
# A root action has its range set to the whole source range, even
|
63
|
+
# though it typically do not act on that range.
|
64
|
+
# This method returns the action as if it was a child action with
|
65
|
+
# its range contracted.
|
66
|
+
# @return [Action]
|
67
|
+
def contract
|
68
|
+
raise 'Empty actions can not be contracted' if empty?
|
69
|
+
return self if insertion?
|
70
|
+
range = @range.with(
|
71
|
+
begin_pos: children.first.range.begin_pos,
|
72
|
+
end_pos: children.last.range.end_pos,
|
73
|
+
)
|
74
|
+
with(range: range)
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @return [Action] that has been moved to the given source_buffer and with the given offset
|
79
|
+
# No check is done on validity of resulting range.
|
80
|
+
def moved(source_buffer, offset)
|
81
|
+
moved_range = ::Parser::Source::Range.new(
|
82
|
+
source_buffer,
|
83
|
+
@range.begin_pos + offset,
|
84
|
+
@range.end_pos + offset
|
85
|
+
)
|
86
|
+
with(
|
87
|
+
range: moved_range,
|
88
|
+
children: children.map { |child| child.moved(source_buffer, offset) }
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
47
92
|
protected
|
48
93
|
|
49
|
-
|
94
|
+
attr_reader :children
|
95
|
+
|
96
|
+
def with(range: @range, enforcer: @enforcer, children: @children, insert_before: @insert_before, replacement: @replacement, insert_after: @insert_after)
|
50
97
|
children = swallow(children) if replacement
|
51
|
-
self.class.new(range,
|
98
|
+
self.class.new(range, enforcer, children: children, insert_before: insert_before, replacement: replacement, insert_after: insert_after)
|
52
99
|
end
|
53
100
|
|
54
101
|
# Assumes range.contains?(action.range) && action.children.empty?
|
@@ -56,24 +103,32 @@ module Parser
|
|
56
103
|
if action.range == @range
|
57
104
|
merge(action)
|
58
105
|
else
|
59
|
-
|
106
|
+
place_in_hierarchy(action)
|
60
107
|
end
|
61
108
|
end
|
62
109
|
|
63
|
-
def
|
64
|
-
family =
|
110
|
+
def place_in_hierarchy(action)
|
111
|
+
family = analyse_hierarchy(action)
|
65
112
|
|
66
113
|
if family[:fusible]
|
67
|
-
fuse_deletions(action, family[:fusible], [*family[:
|
114
|
+
fuse_deletions(action, family[:fusible], [*family[:sibbling_left], *family[:child], *family[:sibbling_right]])
|
68
115
|
else
|
69
116
|
extra_sibbling = if family[:parent] # action should be a descendant of one of the children
|
70
|
-
family[:parent]
|
117
|
+
family[:parent].do_combine(action)
|
71
118
|
elsif family[:child] # or it should become the parent of some of the children,
|
72
|
-
action.with(children: family[:child])
|
119
|
+
action.with(children: family[:child], enforcer: @enforcer)
|
120
|
+
.combine_children(action.children)
|
73
121
|
else # or else it should become an additional child
|
74
122
|
action
|
75
123
|
end
|
76
|
-
with(children: [*family[:
|
124
|
+
with(children: [*family[:sibbling_left], extra_sibbling, *family[:sibbling_right]])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Assumes `more_children` all contained within `@range`
|
129
|
+
def combine_children(more_children)
|
130
|
+
more_children.inject(self) do |parent, new_child|
|
131
|
+
parent.place_in_hierarchy(new_child)
|
77
132
|
end
|
78
133
|
end
|
79
134
|
|
@@ -84,22 +139,76 @@ module Parser
|
|
84
139
|
without_fusible.do_combine(fused_deletion)
|
85
140
|
end
|
86
141
|
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
def
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
142
|
+
# Similar to @children.bsearch_index || size
|
143
|
+
# except allows for a starting point
|
144
|
+
# and `bsearch_index` is only Ruby 2.3+
|
145
|
+
def bsearch_child_index(from = 0)
|
146
|
+
size = @children.size
|
147
|
+
(from...size).bsearch { |i| yield @children[i] } || size
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns the children in a hierarchy with respect to `action`:
|
151
|
+
# :sibbling_left, sibbling_right (for those that are disjoint from `action`)
|
152
|
+
# :parent (in case one of our children contains `action`)
|
153
|
+
# :child (in case `action` strictly contains some of our children)
|
154
|
+
# :fusible (in case `action` overlaps some children but they can be fused in one deletion)
|
155
|
+
# or raises a `CloberingError`
|
156
|
+
# In case a child has equal range to `action`, it is returned as `:parent`
|
157
|
+
# Reminder: an empty range 1...1 is considered disjoint from 1...10
|
158
|
+
def analyse_hierarchy(action)
|
159
|
+
r = action.range
|
160
|
+
# left_index is the index of the first child that isn't completely to the left of action
|
161
|
+
left_index = bsearch_child_index { |child| child.range.end_pos > r.begin_pos }
|
162
|
+
# right_index is the index of the first child that is completely on the right of action
|
163
|
+
start = left_index == 0 ? 0 : left_index - 1 # See "corner case" below for reason of -1
|
164
|
+
right_index = bsearch_child_index(start) { |child| child.range.begin_pos >= r.end_pos }
|
165
|
+
center = right_index - left_index
|
166
|
+
case center
|
167
|
+
when 0
|
168
|
+
# All children are disjoint from action, nothing else to do
|
169
|
+
when -1
|
170
|
+
# Corner case: if a child has empty range == action's range
|
171
|
+
# then it will appear to be both disjoint and to the left of action,
|
172
|
+
# as well as disjoint and to the right of action.
|
173
|
+
# Since ranges are equal, we return it as parent
|
174
|
+
left_index -= 1 # Fix indices, as otherwise this child would be
|
175
|
+
right_index += 1 # considered as a sibbling (both left and right!)
|
176
|
+
parent = @children[left_index]
|
100
177
|
else
|
101
|
-
@
|
178
|
+
overlap_left = @children[left_index].range.begin_pos <=> r.begin_pos
|
179
|
+
overlap_right = @children[right_index-1].range.end_pos <=> r.end_pos
|
180
|
+
|
181
|
+
# For one child to be the parent of action, we must have:
|
182
|
+
if center == 1 && overlap_left <= 0 && overlap_right >= 0
|
183
|
+
parent = @children[left_index]
|
184
|
+
else
|
185
|
+
# Otherwise consider all non disjoint elements (center) to be contained...
|
186
|
+
contained = @children[left_index...right_index]
|
187
|
+
fusible = check_fusible(action,
|
188
|
+
(contained.shift if overlap_left < 0), # ... but check first and last one
|
189
|
+
(contained.pop if overlap_right > 0) # ... for overlaps
|
190
|
+
)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
{
|
195
|
+
parent: parent,
|
196
|
+
sibbling_left: @children[0...left_index],
|
197
|
+
sibbling_right: @children[right_index...@children.size],
|
198
|
+
fusible: fusible,
|
199
|
+
child: contained,
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
# @param [Array(Action | nil)] fusible
|
204
|
+
def check_fusible(action, *fusible)
|
205
|
+
fusible.compact!
|
206
|
+
return if fusible.empty?
|
207
|
+
fusible.each do |child|
|
208
|
+
kind = action.insertion? || child.insertion? ? :crossing_insertions : :crossing_deletions
|
209
|
+
@enforcer.call(kind) { {range: action.range, conflict: child.range} }
|
102
210
|
end
|
211
|
+
fusible
|
103
212
|
end
|
104
213
|
|
105
214
|
# Assumes action.range == range && action.children.empty?
|
@@ -109,7 +218,7 @@ module Parser
|
|
109
218
|
insert_before: "#{action.insert_before}#{insert_before}",
|
110
219
|
replacement: action.replacement || @replacement,
|
111
220
|
insert_after: "#{insert_after}#{action.insert_after}",
|
112
|
-
)
|
221
|
+
).combine_children(action.children)
|
113
222
|
end
|
114
223
|
|
115
224
|
def call_enforcer_for_merge(action)
|