node_mutation 1.19.4 → 1.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +1 -1
- data/lib/node_mutation/action/group_action.rb +29 -0
- data/lib/node_mutation/helper.rb +14 -0
- data/lib/node_mutation/version.rb +1 -1
- data/lib/node_mutation.rb +110 -21
- data/sig/node_mutation/helper.rbs +3 -0
- data/sig/node_mutation.rbs +3 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bac003e717da366c7a0d9e1e0ee939db7173805fa2f9882b9141d089503cf00
|
4
|
+
data.tar.gz: 0e1a8f58718b689cb50bd5177496cc0b782594abff2b90cc532f3770fffce6fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dee5df5d9cc61048b5b16b916527a8a031c80e16ff967bc547d65336a524bfd87e7135ebd72a8ac0aa1f3da6379520949c07d8a7c52baa13ea4a9d2a628c8318
|
7
|
+
data.tar.gz: 3252841fb0ec840591e21bcd28a5ab987271b6900664b7cd25397f7254d93130903d79f328763e67667bd5671101ec449e260a1a54b399939e5fdc9ff90910ff
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# NodeMutation
|
2
2
|
|
3
|
+
## 1.21.0 (2023-09-26)
|
4
|
+
|
5
|
+
* Rename `combine` dsl to `group`
|
6
|
+
|
7
|
+
## 1.20.0 (2023-09-24)
|
8
|
+
|
9
|
+
* Add `CombinedAction` to combine multiple actions.
|
10
|
+
* Add `combine` dsl to combine multiple actions.
|
11
|
+
* Add `NodeMutation::Helper.iterate_actions`
|
12
|
+
|
3
13
|
## 1.19.4 (2023-08-17)
|
4
14
|
|
5
15
|
* Use `NodeMutation.adapter.get_indent`
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# GroupAction is compose of multiple actions.
|
4
|
+
class NodeMutation::GroupAction < NodeMutation::Action
|
5
|
+
DEFAULT_START = 2**30
|
6
|
+
|
7
|
+
attr_accessor :actions
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@actions = []
|
11
|
+
@type = :group
|
12
|
+
end
|
13
|
+
|
14
|
+
def new_code
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Calculate the begin and end positions.
|
21
|
+
def calculate_position
|
22
|
+
@start = DEFAULT_START
|
23
|
+
@end = 0
|
24
|
+
NodeMutation::Helper.iterate_actions(@actions) do |action|
|
25
|
+
@start = [action.start, @start].min
|
26
|
+
@end = [action.end, @end].max
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NodeMutation::Helper
|
4
|
+
# It iterates over all actions, and calls the given block with each action.
|
5
|
+
def self.iterate_actions(actions, &block)
|
6
|
+
actions.each do |action|
|
7
|
+
if action.is_a?(NodeMutation::GroupAction)
|
8
|
+
iterate_actions(action.actions, &block)
|
9
|
+
else
|
10
|
+
block.call(action)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/node_mutation.rb
CHANGED
@@ -11,6 +11,7 @@ class NodeMutation
|
|
11
11
|
autoload :SyntaxTreeAdapter, "node_mutation/adapter/syntax_tree"
|
12
12
|
autoload :Action, 'node_mutation/action'
|
13
13
|
autoload :AppendAction, 'node_mutation/action/append_action'
|
14
|
+
autoload :GroupAction, 'node_mutation/action/group_action'
|
14
15
|
autoload :DeleteAction, 'node_mutation/action/delete_action'
|
15
16
|
autoload :IndentAction, 'node_mutation/action/indent_action'
|
16
17
|
autoload :InsertAction, 'node_mutation/action/insert_action'
|
@@ -22,6 +23,7 @@ class NodeMutation
|
|
22
23
|
autoload :Result, 'node_mutation/result'
|
23
24
|
autoload :Strategy, 'node_mutation/strategy'
|
24
25
|
autoload :Struct, 'node_mutation/struct'
|
26
|
+
autoload :Helper, 'node_mutation/helper'
|
25
27
|
|
26
28
|
# @!attribute [r] actions
|
27
29
|
# @return [Array<NodeMutation::Struct::Action>]
|
@@ -208,21 +210,50 @@ class NodeMutation
|
|
208
210
|
def wrap(node, prefix:, suffix:, newline: false)
|
209
211
|
if newline
|
210
212
|
indentation = NodeMutation.adapter.get_start_loc(node).column
|
211
|
-
|
212
|
-
|
213
|
-
|
213
|
+
group do
|
214
|
+
insert node, prefix + "\n" + (' ' * indentation), at: 'beginning'
|
215
|
+
insert node, "\n" + (' ' * indentation) + suffix, at: 'end'
|
216
|
+
indent node
|
217
|
+
end
|
214
218
|
else
|
215
|
-
|
216
|
-
|
219
|
+
group do
|
220
|
+
insert node, prefix, at: 'beginning'
|
221
|
+
insert node, suffix, at: 'end'
|
222
|
+
end
|
217
223
|
end
|
218
224
|
end
|
219
225
|
|
226
|
+
# Indent source code of the ast node
|
227
|
+
# @param node [Node] ast node
|
228
|
+
# @example
|
229
|
+
# source code of ast node is
|
230
|
+
# class Foobar
|
231
|
+
# end
|
232
|
+
# then we call
|
233
|
+
# indent(node)
|
234
|
+
# the source code will be rewritten to
|
235
|
+
# class Foobar
|
236
|
+
# end
|
237
|
+
def indent(node)
|
238
|
+
@actions << IndentAction.new(node).process
|
239
|
+
end
|
240
|
+
|
220
241
|
# No operation.
|
221
242
|
# @param node [Node] ast node
|
222
243
|
def noop(node)
|
223
244
|
@actions << NoopAction.new(node).process
|
224
245
|
end
|
225
246
|
|
247
|
+
# group multiple actions
|
248
|
+
def group
|
249
|
+
current_actions = @actions
|
250
|
+
group_action = GroupAction.new
|
251
|
+
@actions = group_action.actions
|
252
|
+
yield
|
253
|
+
@actions = current_actions
|
254
|
+
@actions << group_action.process
|
255
|
+
end
|
256
|
+
|
226
257
|
# Process actions and return the new source.
|
227
258
|
#
|
228
259
|
# If there's an action range conflict,
|
@@ -231,23 +262,22 @@ class NodeMutation
|
|
231
262
|
# if strategy is set to KEEP_RUNNING.
|
232
263
|
# @return {NodeMutation::Result}
|
233
264
|
def process
|
265
|
+
@actions = flatten_actions(@actions)
|
234
266
|
if @actions.length == 0
|
235
267
|
return NodeMutation::Result.new(affected: false, conflicted: false)
|
236
268
|
end
|
237
269
|
|
238
270
|
source = +@source
|
239
271
|
@transform_proc.call(@actions) if @transform_proc
|
240
|
-
@actions
|
241
|
-
conflict_actions = get_conflict_actions
|
272
|
+
sort_actions!(@actions)
|
273
|
+
conflict_actions = get_conflict_actions(@actions)
|
242
274
|
if conflict_actions.size > 0 && strategy?(Strategy::THROW_ERROR)
|
243
275
|
raise ConflictActionError, "mutation actions are conflicted"
|
244
276
|
end
|
245
277
|
|
246
|
-
@actions
|
247
|
-
source[action.start...action.end] = action.new_code if action.new_code
|
248
|
-
end
|
278
|
+
new_source = rewrite_source(source, @actions)
|
249
279
|
result = NodeMutation::Result.new(affected: true, conflicted: !conflict_actions.empty?)
|
250
|
-
result.new_source =
|
280
|
+
result.new_source = new_source
|
251
281
|
result
|
252
282
|
end
|
253
283
|
|
@@ -259,13 +289,14 @@ class NodeMutation
|
|
259
289
|
# if strategy is set to KEEP_RUNNING.
|
260
290
|
# @return {NodeMutation::Result}
|
261
291
|
def test
|
292
|
+
@actions = flatten_actions(@actions)
|
262
293
|
if @actions.length == 0
|
263
294
|
return NodeMutation::Result.new(affected: false, conflicted: false)
|
264
295
|
end
|
265
296
|
|
266
297
|
@transform_proc.call(@actions) if @transform_proc
|
267
|
-
@actions
|
268
|
-
conflict_actions = get_conflict_actions
|
298
|
+
sort_actions!(@actions)
|
299
|
+
conflict_actions = get_conflict_actions(@actions)
|
269
300
|
if conflict_actions.size > 0 && strategy?(Strategy::THROW_ERROR)
|
270
301
|
raise ConflictActionError, "mutation actions are conflicted"
|
271
302
|
end
|
@@ -277,27 +308,85 @@ class NodeMutation
|
|
277
308
|
|
278
309
|
private
|
279
310
|
|
311
|
+
# It flattens a series of actions by removing any GroupAction
|
312
|
+
# objects that contain only a single action. This is done recursively.
|
313
|
+
def flatten_actions(actions)
|
314
|
+
new_actions = []
|
315
|
+
actions.each do |action|
|
316
|
+
if action.is_a?(GroupAction)
|
317
|
+
new_actions << flatten_group_action(action)
|
318
|
+
else
|
319
|
+
new_actions << action
|
320
|
+
end
|
321
|
+
end
|
322
|
+
new_actions.compact
|
323
|
+
end
|
324
|
+
|
325
|
+
# It flattens a group action.
|
326
|
+
def flatten_group_action(action)
|
327
|
+
if action.actions.empty?
|
328
|
+
nil
|
329
|
+
elsif action.actions.size == 1
|
330
|
+
if action.actions.first.is_a?(GroupAction)
|
331
|
+
flatten_group_action(action.actions.first)
|
332
|
+
else
|
333
|
+
action.actions.first
|
334
|
+
end
|
335
|
+
else
|
336
|
+
action.actions = flatten_actions(action.actions)
|
337
|
+
action
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
# Sort actions by start position and end position.
|
342
|
+
# @param actions [Array<NodeMutation::Action>]
|
343
|
+
# @return [Array<NodeMutation::Action>] sorted actions
|
344
|
+
def sort_actions!(actions)
|
345
|
+
actions.sort_by! { |action| [action.start, action.end] }
|
346
|
+
actions.each do |action|
|
347
|
+
sort_actions!(action.actions) if action.is_a?(GroupAction)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Rewrite source code with actions.
|
352
|
+
# @param source [String] source code
|
353
|
+
# @param actions [Array<NodeMutation::Action>] actions
|
354
|
+
# @return [String] new source code
|
355
|
+
def rewrite_source(source, actions)
|
356
|
+
actions.reverse_each do |action|
|
357
|
+
if action.is_a?(GroupAction)
|
358
|
+
source = rewrite_source(source, action.actions)
|
359
|
+
else
|
360
|
+
source[action.start...action.end] = action.new_code if action.new_code
|
361
|
+
end
|
362
|
+
end
|
363
|
+
source
|
364
|
+
end
|
365
|
+
|
280
366
|
# It changes source code from bottom to top, and it can change source code twice at the same time,
|
281
367
|
# So if there is an overlap between two actions, it removes the conflict actions and operate them in the next loop.
|
282
|
-
def get_conflict_actions
|
283
|
-
i =
|
368
|
+
def get_conflict_actions(actions)
|
369
|
+
i = actions.length - 1
|
284
370
|
j = i - 1
|
285
371
|
conflict_actions = []
|
286
372
|
return [] if i < 0
|
287
373
|
|
288
|
-
begin_pos =
|
289
|
-
end_pos =
|
374
|
+
begin_pos = actions[i].start
|
375
|
+
end_pos = actions[i].end
|
290
376
|
while j > -1
|
291
377
|
# if we have two actions with overlapped range.
|
292
|
-
if begin_pos <
|
293
|
-
conflict_actions <<
|
378
|
+
if begin_pos < actions[j].end
|
379
|
+
conflict_actions << actions.delete_at(j)
|
294
380
|
else
|
295
381
|
i = j
|
296
|
-
begin_pos =
|
297
|
-
end_pos =
|
382
|
+
begin_pos = actions[i].start
|
383
|
+
end_pos = actions[i].end
|
298
384
|
end
|
299
385
|
j -= 1
|
300
386
|
end
|
387
|
+
actions.each do |action|
|
388
|
+
conflict_actions.concat(get_conflict_actions(action.actions)) if action.is_a?(GroupAction)
|
389
|
+
end
|
301
390
|
conflict_actions
|
302
391
|
end
|
303
392
|
|
data/sig/node_mutation.rbs
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
class NodeMutation[T]
|
2
2
|
VERSION: String
|
3
3
|
|
4
4
|
class MethodNotSupported < StandardError
|
@@ -39,6 +39,8 @@ module NodeMutation[T]
|
|
39
39
|
|
40
40
|
def noop: (node: T) -> void
|
41
41
|
|
42
|
+
def group: () { () -> void } -> void
|
43
|
+
|
42
44
|
def process: () -> NodeMutation::Result
|
43
45
|
|
44
46
|
def test: () -> NodeMutation::Result
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: node_mutation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: ast node mutation apis
|
14
14
|
email:
|
@@ -29,6 +29,7 @@ files:
|
|
29
29
|
- lib/node_mutation/action.rb
|
30
30
|
- lib/node_mutation/action/append_action.rb
|
31
31
|
- lib/node_mutation/action/delete_action.rb
|
32
|
+
- lib/node_mutation/action/group_action.rb
|
32
33
|
- lib/node_mutation/action/indent_action.rb
|
33
34
|
- lib/node_mutation/action/insert_action.rb
|
34
35
|
- lib/node_mutation/action/noop_action.rb
|
@@ -39,6 +40,7 @@ files:
|
|
39
40
|
- lib/node_mutation/adapter.rb
|
40
41
|
- lib/node_mutation/adapter/parser.rb
|
41
42
|
- lib/node_mutation/adapter/syntax_tree.rb
|
43
|
+
- lib/node_mutation/helper.rb
|
42
44
|
- lib/node_mutation/result.rb
|
43
45
|
- lib/node_mutation/strategy.rb
|
44
46
|
- lib/node_mutation/struct.rb
|
@@ -46,6 +48,7 @@ files:
|
|
46
48
|
- node_mutation.gemspec
|
47
49
|
- sig/node_mutation.rbs
|
48
50
|
- sig/node_mutation/adapter.rbs
|
51
|
+
- sig/node_mutation/helper.rbs
|
49
52
|
- sig/node_mutation/result.rbs
|
50
53
|
- sig/node_mutation/strategy.rbs
|
51
54
|
- sig/node_mutation/struct.rbs
|