node_mutation 1.22.1 → 1.22.3

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