parser 2.5.1.0 → 3.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|