node_mutation 1.22.1 → 1.22.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1039c5eaf805a6938a0470e3dbafd304b9b7c2a84c3f321fc040f030e0314dea
4
- data.tar.gz: fd7895425980b04494de197902a84bef9dcdb6153255654ccc2a5c28b31badb7
3
+ metadata.gz: 235e331a54f9181d9986ccb6ee62ef172b399a323b697b8090c954e685a117f0
4
+ data.tar.gz: 11e838f63be7a940176ad6880300aeebde419e3bb923016039a821496648b9ca
5
5
  SHA512:
6
- metadata.gz: 0ebb7d1aaf7ffd54e8f99bc1064caaa23c3f76e131260acf58dc43eb23de49a3d3d09482e19754b43ab19c699b313370fdd17ae5dfd02c1e3964470df002ffa8
7
- data.tar.gz: 674438f94176b2ed7e77283e7238ab96edefc49a4423f9bb967904137a964d2d72228fd7f815fdb102b3ed0823820ef4e99fe54a0863b2144a98d3a240a2ccdb
6
+ metadata.gz: 0cd144acfbcc3db98c407332f3408bde088ad14156bd5a9c07bc1d1042872339cc71b25b5896c76ff720d8efe80add83789f6889125e55dd7bcb39cc0cf43512
7
+ data.tar.gz: cdd66a6fa04ba9ad54d788e2f5680045b497a94b48875d4a98efb3a57dfc5a864f9f666a12ab687c16f5a2a59013b39b7d5ec8ead6264e63e22b6faf1edf527a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # NodeMutation
2
2
 
3
+ ## 1.22.3 (2024-01-30)
4
+
5
+ * `child_node_range` supports `arg` `name`
6
+ * Add `action` methods to `GroupAction`
7
+
8
+ ## 1.22.2 (2023-12-04)
9
+
10
+ * `str` child node range does not include quotes
11
+
3
12
  ## 1.22.1 (2023-12-04)
4
13
 
5
14
  * `child_node_range` supports int/float/str/sym `value`
@@ -131,7 +140,7 @@
131
140
 
132
141
  ## 1.13.1 (2023-03-31)
133
142
 
134
- * Remove both whitespaces only when next char is nil
143
+ * Remove both whitespace only when next char is nil
135
144
 
136
145
  ## 1.13.0 (2023-03-31)
137
146
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- node_mutation (1.22.1)
4
+ node_mutation (1.22.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -71,6 +71,7 @@ GEM
71
71
  PLATFORMS
72
72
  x86_64-darwin-21
73
73
  x86_64-darwin-22
74
+ x86_64-darwin-23
74
75
  x86_64-linux
75
76
 
76
77
  DEPENDENCIES
@@ -2,18 +2,27 @@
2
2
 
3
3
  # GroupAction is compose of multiple actions.
4
4
  class NodeMutation::GroupAction < NodeMutation::Action
5
+ include NodeMutation::Actionable
6
+
5
7
  DEFAULT_START = 2**30
6
8
 
7
9
  # Initialize a GroupAction.
8
- def initialize
10
+ def initialize(adapter:, &block)
9
11
  @actions = []
10
12
  @type = :group
13
+ @adapter = adapter
14
+ @block = block
11
15
  end
12
16
 
13
17
  def new_code
14
18
  nil
15
19
  end
16
20
 
21
+ def process
22
+ instance_eval(&@block)
23
+ super
24
+ end
25
+
17
26
  private
18
27
 
19
28
  # Calculate the begin and end positions.
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NodeMutation::Actionable
4
+ # Append code to the ast node.
5
+ # @param node [Node] ast node
6
+ # @param code [String] new code to append
7
+ # @example
8
+ # source code of the ast node is
9
+ # def teardown
10
+ # clean_something
11
+ # end
12
+ # then we call
13
+ # mutation.append(node, 'super')
14
+ # the source code will be rewritten to
15
+ # def teardown
16
+ # clean_something
17
+ # super
18
+ # end
19
+ def append(node, code)
20
+ @actions << NodeMutation::AppendAction.new(node, code, adapter: @adapter).process
21
+ end
22
+
23
+ # Delete source code of the child ast node.
24
+ # @param node [Node] ast node
25
+ # @param selectors [Array<Symbol>] selector names of child node.
26
+ # @param and_comma [Boolean] delete extra comma.
27
+ # @example
28
+ # source code of the ast node is
29
+ # FactoryBot.create(...)
30
+ # then we call
31
+ # mutation.delete(node, :receiver, :dot)
32
+ # the source code will be rewritten to
33
+ # create(...)
34
+ def delete(node, *selectors, and_comma: false)
35
+ @actions << NodeMutation::DeleteAction.new(node, *selectors, and_comma: and_comma, adapter: @adapter).process
36
+ end
37
+
38
+ # Insert code to the ast node.
39
+ # @param node [Node] ast node
40
+ # @param code [String] code need to be inserted.
41
+ # @param at [String] insert position, beginning or end
42
+ # @param to [String] where to insert, if it is nil, will insert to current node.
43
+ # @param and_comma [Boolean] insert extra comma.
44
+ # @example
45
+ # source code of the ast node is
46
+ # open('http://test.com')
47
+ # then we call
48
+ # mutation.insert(node, 'URI.', at: 'beginning')
49
+ # the source code will be rewritten to
50
+ # URI.open('http://test.com')
51
+ def insert(node, code, at: 'end', to: nil, and_comma: false)
52
+ @actions << NodeMutation::InsertAction.new(
53
+ node,
54
+ code,
55
+ at: at,
56
+ to: to,
57
+ and_comma: and_comma,
58
+ adapter: @adapter
59
+ ).process
60
+ end
61
+
62
+ # Prepend code to the ast node.
63
+ # @param node [Node] ast node
64
+ # @param code [String] new code to prepend.
65
+ # @example
66
+ # source code of the ast node is
67
+ # def setup
68
+ # do_something
69
+ # end
70
+ # then we call
71
+ # mutation.prepend(node, 'super')
72
+ # the source code will be rewritten to
73
+ # def setup
74
+ # super
75
+ # do_something
76
+ # end
77
+ def prepend(node, code)
78
+ @actions << NodeMutation::PrependAction.new(node, code, adapter: @adapter).process
79
+ end
80
+
81
+ # Remove source code of the ast node.
82
+ # @param node [Node] ast node
83
+ # @param and_comma [Boolean] delete extra comma.
84
+ # @example
85
+ # source code of the ast node is
86
+ # puts "test"
87
+ # then we call
88
+ # mutation.remove(node)
89
+ # the source code will be removed
90
+ def remove(node, and_comma: false)
91
+ @actions << NodeMutation::RemoveAction.new(node, and_comma: and_comma, adapter: @adapter).process
92
+ end
93
+
94
+ # Replace child node of the ast node with new code.
95
+ # @param node [Node] ast node
96
+ # @param selectors [Array<Symbol>] selector names of child node.
97
+ # @param with [String] code need to be replaced with.
98
+ # @example
99
+ # source code of the ast node is
100
+ # assert(object.empty?)
101
+ # then we call
102
+ # mutation.replace(node, :message, with: 'assert_empty')
103
+ # mutation.replace(node, :arguments, with: '{{arguments.first.receiver}}')
104
+ # the source code will be rewritten to
105
+ # assert_empty(object)
106
+ def replace(node, *selectors, with:)
107
+ @actions << NodeMutation::ReplaceAction.new(node, *selectors, with: with, adapter: @adapter).process
108
+ end
109
+
110
+ # Replace source code of the ast node with new code.
111
+ # @param node [Node] ast node
112
+ # @param code [String] code need to be replaced with.
113
+ # @example
114
+ # source code of the ast node is
115
+ # obj.stub(:foo => 1, :bar => 2)
116
+ # then we call
117
+ # replace_with 'allow({{receiver}}).to receive_messages({{arguments}})'
118
+ # the source code will be rewritten to
119
+ # allow(obj).to receive_messages(:foo => 1, :bar => 2)
120
+ def replace_with(node, code)
121
+ @actions << NodeMutation::ReplaceWithAction.new(node, code, adapter: @adapter).process
122
+ end
123
+
124
+ # Wrap source code of the ast node with prefix and suffix code.
125
+ # @param node [Node] ast node
126
+ # @param prefix [String] prefix code need to be wrapped with.
127
+ # @param suffix [String] suffix code need to be wrapped with.
128
+ # @param newline [Boolean] add newline after prefix and before suffix.
129
+ # @example
130
+ # source code of the ast node is
131
+ # class Foobar
132
+ # end
133
+ # then we call
134
+ # wrap(node, prefix: 'module Synvert', suffix: 'end', newline: true)
135
+ # the source code will be rewritten to
136
+ # module Synvert
137
+ # class Foobar
138
+ # end
139
+ # end
140
+ def wrap(node, prefix:, suffix:, newline: false)
141
+ if newline
142
+ indentation = @adapter.get_start_loc(node).column
143
+ group do
144
+ insert node, prefix + "\n" + (' ' * indentation), at: 'beginning'
145
+ insert node, "\n" + (' ' * indentation) + suffix, at: 'end'
146
+ indent node
147
+ end
148
+ else
149
+ group do
150
+ insert node, prefix, at: 'beginning'
151
+ insert node, suffix, at: 'end'
152
+ end
153
+ end
154
+ end
155
+
156
+ # Indent source code of the ast node
157
+ # @param node [Node] ast node
158
+ # @example
159
+ # source code of ast node is
160
+ # class Foobar
161
+ # end
162
+ # then we call
163
+ # indent(node)
164
+ # the source code will be rewritten to
165
+ # class Foobar
166
+ # end
167
+ def indent(node)
168
+ @actions << NodeMutation::IndentAction.new(node, adapter: @adapter).process
169
+ end
170
+
171
+ # No operation.
172
+ # @param node [Node] ast node
173
+ def noop(node)
174
+ @actions << NodeMutation::NoopAction.new(node, adapter: @adapter).process
175
+ end
176
+
177
+ # group multiple actions
178
+ def group(&block)
179
+ @actions << NodeMutation::GroupAction.new(adapter: @adapter, &block).process
180
+ end
181
+ end
@@ -175,7 +175,7 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
175
175
  node.arguments.last.loc.expression.end_pos + 1
176
176
  )
177
177
  end
178
- when %i[class name], %i[const name], %i[cvar name], %i[def name], %i[defs name],
178
+ when %i[arg name], %i[class name], %i[const name], %i[cvar name], %i[def name], %i[defs name],
179
179
  %i[gvar name], %i[ivar name], %i[lvar name]
180
180
 
181
181
  NodeMutation::Struct::Range.new(node.loc.name.begin_pos, node.loc.name.end_pos)
@@ -185,7 +185,7 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
185
185
  NodeMutation::Struct::Range.new(node.loc.operator.begin_pos, node.loc.operator.end_pos) if node.loc.operator
186
186
  when %i[defs self]
187
187
  NodeMutation::Struct::Range.new(node.loc.operator.begin_pos - 'self'.length, node.loc.operator.begin_pos)
188
- when %i[float value], %i[int value], %i[rational value], %i[str value], %i[sym value]
188
+ when %i[float value], %i[int value], %i[sym value]
189
189
  NodeMutation::Struct::Range.new(node.loc.expression.begin_pos, node.loc.expression.end_pos)
190
190
  when %i[lvasgn variable], %i[ivasgn variable], %i[cvasgn variable], %i[gvasgn variable]
191
191
  NodeMutation::Struct::Range.new(node.loc.name.begin_pos, node.loc.name.end_pos)
@@ -201,6 +201,8 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
201
201
  if node.loc.begin && node.loc.end
202
202
  NodeMutation::Struct::Range.new(node.loc.begin.begin_pos, node.loc.end.end_pos)
203
203
  end
204
+ when %i[str value]
205
+ NodeMutation::Struct::Range.new(node.loc.expression.begin_pos + 1, node.loc.expression.end_pos - 1)
204
206
  else
205
207
  raise NodeMutation::MethodNotSupported,
206
208
  "#{direct_child_name} is not supported for #{get_source(node)}" unless node.respond_to?(direct_child_name)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class NodeMutation
4
- VERSION = "1.22.1"
4
+ VERSION = "1.22.3"
5
5
  end
data/lib/node_mutation.rb CHANGED
@@ -7,6 +7,7 @@ class NodeMutation
7
7
  class ConflictActionError < StandardError; end
8
8
  class InvalidAdapterError < StandardError; end
9
9
 
10
+ autoload :Actionable, "node_mutation/actionable"
10
11
  autoload :Adapter, "node_mutation/adapter"
11
12
  autoload :ParserAdapter, "node_mutation/adapter/parser"
12
13
  autoload :SyntaxTreeAdapter, "node_mutation/adapter/syntax_tree"
@@ -26,6 +27,8 @@ class NodeMutation
26
27
  autoload :Struct, 'node_mutation/struct'
27
28
  autoload :Helper, 'node_mutation/helper'
28
29
 
30
+ include Actionable
31
+
29
32
  # @!attribute [r] actions
30
33
  # @return [Array<NodeMutation::Struct::Action>]
31
34
  attr_reader :actions, :adapter
@@ -71,182 +74,6 @@ class NodeMutation
71
74
  @adapter = get_adapter_instance(adapter)
72
75
  end
73
76
 
74
- # Append code to the ast node.
75
- # @param node [Node] ast node
76
- # @param code [String] new code to append
77
- # @example
78
- # source code of the ast node is
79
- # def teardown
80
- # clean_something
81
- # end
82
- # then we call
83
- # mutation.append(node, 'super')
84
- # the source code will be rewritten to
85
- # def teardown
86
- # clean_something
87
- # super
88
- # end
89
- def append(node, code)
90
- @actions << AppendAction.new(node, code, adapter: @adapter).process
91
- end
92
-
93
- # Delete source code of the child ast node.
94
- # @param node [Node] ast node
95
- # @param selectors [Array<Symbol>] selector names of child node.
96
- # @param and_comma [Boolean] delete extra comma.
97
- # @example
98
- # source code of the ast node is
99
- # FactoryBot.create(...)
100
- # then we call
101
- # mutation.delete(node, :receiver, :dot)
102
- # the source code will be rewritten to
103
- # create(...)
104
- def delete(node, *selectors, and_comma: false)
105
- @actions << DeleteAction.new(node, *selectors, and_comma: and_comma, adapter: @adapter).process
106
- end
107
-
108
- # Insert code to the ast node.
109
- # @param node [Node] ast node
110
- # @param code [String] code need to be inserted.
111
- # @param at [String] insert position, beginning or end
112
- # @param to [String] where to insert, if it is nil, will insert to current node.
113
- # @param and_comma [Boolean] insert extra comma.
114
- # @example
115
- # source code of the ast node is
116
- # open('http://test.com')
117
- # then we call
118
- # mutation.insert(node, 'URI.', at: 'beginning')
119
- # the source code will be rewritten to
120
- # URI.open('http://test.com')
121
- def insert(node, code, at: 'end', to: nil, and_comma: false)
122
- @actions << InsertAction.new(node, code, at: at, to: to, and_comma: and_comma, adapter: @adapter).process
123
- end
124
-
125
- # Prepend code to the ast node.
126
- # @param node [Node] ast node
127
- # @param code [String] new code to prepend.
128
- # @example
129
- # source code of the ast node is
130
- # def setup
131
- # do_something
132
- # end
133
- # then we call
134
- # mutation.prepend(node, 'super')
135
- # the source code will be rewritten to
136
- # def setup
137
- # super
138
- # do_something
139
- # end
140
- def prepend(node, code)
141
- @actions << PrependAction.new(node, code, adapter: @adapter).process
142
- end
143
-
144
- # Remove source code of the ast node.
145
- # @param node [Node] ast node
146
- # @param and_comma [Boolean] delete extra comma.
147
- # @example
148
- # source code of the ast node is
149
- # puts "test"
150
- # then we call
151
- # mutation.remove(node)
152
- # the source code will be removed
153
- def remove(node, and_comma: false)
154
- @actions << RemoveAction.new(node, and_comma: and_comma, adapter: @adapter).process
155
- end
156
-
157
- # Replace child node of the ast node with new code.
158
- # @param node [Node] ast node
159
- # @param selectors [Array<Symbol>] selector names of child node.
160
- # @param with [String] code need to be replaced with.
161
- # @example
162
- # source code of the ast node is
163
- # assert(object.empty?)
164
- # then we call
165
- # mutation.replace(node, :message, with: 'assert_empty')
166
- # mutation.replace(node, :arguments, with: '{{arguments.first.receiver}}')
167
- # the source code will be rewritten to
168
- # assert_empty(object)
169
- def replace(node, *selectors, with:)
170
- @actions << ReplaceAction.new(node, *selectors, with: with, adapter: @adapter).process
171
- end
172
-
173
- # Replace source code of the ast node with new code.
174
- # @param node [Node] ast node
175
- # @param code [String] code need to be replaced with.
176
- # @example
177
- # source code of the ast node is
178
- # obj.stub(:foo => 1, :bar => 2)
179
- # then we call
180
- # replace_with 'allow({{receiver}}).to receive_messages({{arguments}})'
181
- # the source code will be rewritten to
182
- # allow(obj).to receive_messages(:foo => 1, :bar => 2)
183
- def replace_with(node, code)
184
- @actions << ReplaceWithAction.new(node, code, adapter: @adapter).process
185
- end
186
-
187
- # Wrap source code of the ast node with prefix and suffix code.
188
- # @param node [Node] ast node
189
- # @param prefix [String] prefix code need to be wrapped with.
190
- # @param suffix [String] suffix code need to be wrapped with.
191
- # @param newline [Boolean] add newline after prefix and before suffix.
192
- # @example
193
- # source code of the ast node is
194
- # class Foobar
195
- # end
196
- # then we call
197
- # wrap(node, prefix: 'module Synvert', suffix: 'end', newline: true)
198
- # the source code will be rewritten to
199
- # module Synvert
200
- # class Foobar
201
- # end
202
- # end
203
- def wrap(node, prefix:, suffix:, newline: false)
204
- if newline
205
- indentation = @adapter.get_start_loc(node).column
206
- group do
207
- insert node, prefix + "\n" + (' ' * indentation), at: 'beginning'
208
- insert node, "\n" + (' ' * indentation) + suffix, at: 'end'
209
- indent node
210
- end
211
- else
212
- group do
213
- insert node, prefix, at: 'beginning'
214
- insert node, suffix, at: 'end'
215
- end
216
- end
217
- end
218
-
219
- # Indent source code of the ast node
220
- # @param node [Node] ast node
221
- # @example
222
- # source code of ast node is
223
- # class Foobar
224
- # end
225
- # then we call
226
- # indent(node)
227
- # the source code will be rewritten to
228
- # class Foobar
229
- # end
230
- def indent(node)
231
- @actions << IndentAction.new(node, adapter: @adapter).process
232
- end
233
-
234
- # No operation.
235
- # @param node [Node] ast node
236
- def noop(node)
237
- @actions << NoopAction.new(node, adapter: @adapter).process
238
- end
239
-
240
- # group multiple actions
241
- def group
242
- current_actions = @actions
243
- group_action = GroupAction.new
244
- @actions = group_action.actions
245
- yield
246
- @actions = current_actions
247
- @actions << group_action.process
248
- end
249
-
250
77
  # Process actions and return the new source.
251
78
  #
252
79
  # If there's an action range conflict,
@@ -262,7 +89,6 @@ class NodeMutation
262
89
  return NodeMutation::Result.new(affected: false, conflicted: false)
263
90
  end
264
91
 
265
- source = +@source
266
92
  @transform_proc.call(@actions) if @transform_proc
267
93
  sorted_actions = sort_flatten_actions(flatten_actions)
268
94
  conflict_actions = get_conflict_actions(sorted_actions)
@@ -271,7 +97,7 @@ class NodeMutation
271
97
  end
272
98
 
273
99
  actions = sort_flatten_actions(flat_actions(get_filter_actions(conflict_actions)))
274
- new_source = rewrite_source(source, actions)
100
+ new_source = rewrite_source(+@source, actions)
275
101
  result = NodeMutation::Result.new(affected: true, conflicted: !conflict_actions.empty?)
276
102
  result.new_source = new_source
277
103
  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.22.1
4
+ version: 1.22.3
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-12-04 00:00:00.000000000 Z
11
+ date: 2024-01-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: ast node mutation apis
14
14
  email:
@@ -37,6 +37,7 @@ files:
37
37
  - lib/node_mutation/action/remove_action.rb
38
38
  - lib/node_mutation/action/replace_action.rb
39
39
  - lib/node_mutation/action/replace_with_action.rb
40
+ - lib/node_mutation/actionable.rb
40
41
  - lib/node_mutation/adapter.rb
41
42
  - lib/node_mutation/adapter/parser.rb
42
43
  - lib/node_mutation/adapter/syntax_tree.rb
@@ -73,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
74
  - !ruby/object:Gem::Version
74
75
  version: '0'
75
76
  requirements: []
76
- rubygems_version: 3.4.20
77
+ rubygems_version: 3.5.3
77
78
  signing_key:
78
79
  specification_version: 4
79
80
  summary: ast node mutation apis