node_mutation 1.10.0 → 1.11.0

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: da3db727f8adbdcb8e54c74e8bcb2625a3d3969af1c166734d644aeb00c33d51
4
- data.tar.gz: ad17f467d8cd3edfd1b9ba59656a2b457cfb5c9ebcb4f4df3a10c35bef93b0d7
3
+ metadata.gz: d41515d4be39619d95802e531496159773a1202de61c8fa3c3d269d2a72926da
4
+ data.tar.gz: 2bd0415eec90cb45cdc2dcfacce1cf6529fcdf9f12e6fcd44054aa273e4c4b0b
5
5
  SHA512:
6
- metadata.gz: 3848188fda27a5d4f9663e664ac82e70d55bc643ddf89468925e01c29449d4015ca459b27203c0891e8052ce6ea4a134b70d2438d8f6d41c4fb89ed357bec24d
7
- data.tar.gz: 5198f02dae89f72183a3863f2cf6f1ffa9738bdc931d8a2bd3c45d6d982f98840b047998fd2e89dc9afce00c535c98bc60a850c616d551b9e6f8a309777ea278
6
+ metadata.gz: 656c00012eac7a7b6d2229933fb7e08fd7f0f9a986e90f7db69cc5129097702a2850cdf5494c6f1dae3d6f7da9d0ba642ffd7eb4362517a5c0f465a0d49a52d3
7
+ data.tar.gz: 4faced52fd71462a10c54970afb4412858c355c96bf8713e132f886535d3a1309aa792bc1f4e946c0549492c351c9750fa3ab1d05c3ecd0fda0d58f84c98427f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # NodeMutation
2
2
 
3
+ ## 1.11.0 (2023-03-20)
4
+
5
+ * Calculate position properly for `add_comma`
6
+ * Add `and_comma` param to `insert` dsl
7
+
8
+ ## 1.10.1 (2023-03-13)
9
+
10
+ * Remove `OpenStruct`, use `Struct` instead
11
+
3
12
  ## 1.10.0 (2023-03-01)
4
13
 
5
14
  * Support `variable` of `lvasgn`, `ivasgn`, `cvasgn`, and `gvasgn` node in `child_node_range`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- node_mutation (1.10.0)
4
+ node_mutation (1.11.0)
5
5
  erubis
6
6
 
7
7
  GEM
@@ -23,10 +23,10 @@ class NodeMutation::DeleteAction < NodeMutation::Action
23
23
  # Calculate the begin and end positions.
24
24
  def calculate_position
25
25
  @start = @selectors.map { |selector| NodeMutation.adapter.child_node_range(@node, selector) }
26
- .compact.map(&:start).min
26
+ .compact.map(&:start).min
27
27
  @end = @selectors.map { |selector| NodeMutation.adapter.child_node_range(@node, selector) }
28
- .compact.map(&:end).max
29
- squeeze_spaces
28
+ .compact.map(&:end).max
30
29
  remove_comma if @and_comma
30
+ remove_whitespace
31
31
  end
32
32
  end
@@ -8,17 +8,23 @@ class NodeMutation::InsertAction < NodeMutation::Action
8
8
  # @param code [String] to be inserted
9
9
  # @param at [String] position to insert, beginning or end
10
10
  # @param to [<nil|String>] name of child node
11
- def initialize(node, code, at: 'end', to: nil)
11
+ # @param and_comma [Boolean] insert extra comma.
12
+ def initialize(node, code, at: 'end', to: nil, and_comma: false)
12
13
  super(node, code)
13
14
  @at = at
14
15
  @to = to
16
+ @and_comma = and_comma
15
17
  end
16
18
 
17
19
  # The rewritten source code.
18
20
  #
19
21
  # @return [String] rewritten code.
20
22
  def new_code
21
- rewritten_source
23
+ if @and_comma
24
+ @at == 'end' ? ", #{rewritten_source}" : "#{rewritten_source}, "
25
+ else
26
+ rewritten_source
27
+ end
22
28
  end
23
29
 
24
30
  private
@@ -21,15 +21,13 @@ class NodeMutation::RemoveAction < NodeMutation::Action
21
21
 
22
22
  # Calculate the begin the end positions.
23
23
  def calculate_position
24
+ @start = NodeMutation.adapter.get_start(@node)
25
+ @end = NodeMutation.adapter.get_end(@node)
26
+ remove_comma if @and_comma
27
+ remove_whitespace
24
28
  if take_whole_line?
25
- @start = start_index
26
- @end = end_index
29
+ remove_newline
27
30
  squeeze_lines
28
- else
29
- @start = NodeMutation.adapter.get_start(@node)
30
- @end = NodeMutation.adapter.get_end(@node)
31
- squeeze_spaces
32
- remove_comma if @and_command
33
31
  end
34
32
  end
35
33
 
@@ -37,18 +35,50 @@ class NodeMutation::RemoveAction < NodeMutation::Action
37
35
  #
38
36
  # @return [Boolean]
39
37
  def take_whole_line?
40
- NodeMutation.adapter.get_source(@node) == file_source[start_index...end_index].strip
38
+ NodeMutation.adapter.get_source(@node) == file_source[@start...@end].strip.chomp(',')
41
39
  end
42
40
 
43
- # Get the start position of the line
44
- def start_index
45
- index = file_source[0..NodeMutation.adapter.get_start(@node)].rindex("\n")
46
- index ? index + "\n".length : NodeMutation.adapter.get_start(@node)
41
+ def remove_newline
42
+ leading_count = 1
43
+ loop do
44
+ if file_source[@start - leading_count] == "\n"
45
+ break
46
+ elsif ["\t", ' '].include?(file_source[@start - leading_count])
47
+ leading_count += 1
48
+ else
49
+ break
50
+ end
51
+ end
52
+
53
+ trailing_count = 0
54
+ loop do
55
+ if file_source[@end + trailing_count] == "\n"
56
+ break
57
+ elsif ["\t", ' '].include?(file_source[@end + trailing_count])
58
+ trailing_count += 1
59
+ else
60
+ break
61
+ end
62
+ end
63
+
64
+ if file_source[@end + trailing_count] == "\n"
65
+ @end += trailing_count + 1
66
+ end
67
+
68
+ if file_source[@start - leading_count] == "\n"
69
+ @start -= leading_count - 1
70
+ end
47
71
  end
48
72
 
49
- # Get the end position of the line
50
- def end_index
51
- index = file_source[NodeMutation.adapter.get_end(@node)..-1].index("\n")
52
- index ? NodeMutation.adapter.get_end(@node) + index + "\n".length : NodeMutation.adapter.get_end(@node)
73
+ def squeeze_lines
74
+ lines = file_source.split("\n")
75
+ begin_line = NodeMutation.adapter.get_start_loc(@node).line
76
+ end_line = NodeMutation.adapter.get_end_loc(@node).line
77
+ before_line_is_blank = begin_line == 1 || lines[begin_line - 2] == ''
78
+ after_line_is_blank = lines[end_line] == ''
79
+
80
+ if lines.length > 1 && before_line_is_blank && after_line_is_blank
81
+ @end += "\n".length
82
+ end
53
83
  end
54
84
  end
@@ -23,7 +23,9 @@ class NodeMutation::ReplaceAction < NodeMutation::Action
23
23
 
24
24
  # Calculate the begin the end positions.
25
25
  def calculate_position
26
- @start = @selectors.map { |selector| NodeMutation.adapter.child_node_range(@node, selector).start }.min
27
- @end = @selectors.map { |selector| NodeMutation.adapter.child_node_range(@node, selector).end }.max
26
+ @start = @selectors.map { |selector| NodeMutation.adapter.child_node_range(@node, selector).start }
27
+ .min
28
+ @end = @selectors.map { |selector| NodeMutation.adapter.child_node_range(@node, selector).end }
29
+ .max
28
30
  end
29
31
  end
@@ -20,7 +20,7 @@ class NodeMutation::WrapAction < NodeMutation::Action
20
20
  def new_code
21
21
  "#{@code}\n#{' ' * @indent}" +
22
22
  NodeMutation.adapter.get_source(@node).split("\n").map { |line| " #{line}" }
23
- .join("\n") +
23
+ .join("\n") +
24
24
  "\n#{' ' * @indent}end"
25
25
  end
26
26
 
@@ -30,7 +30,8 @@ class NodeMutation::Action
30
30
  # @return [String] rewritten code.
31
31
  def new_code
32
32
  if rewritten_source.split("\n").length > 1
33
- "\n\n" + rewritten_source.split("\n").map { |line| indent(@node) + line }.join("\n")
33
+ "\n\n" + rewritten_source.split("\n").map { |line| indent(@node) + line }
34
+ .join("\n")
34
35
  else
35
36
  "\n" + indent(@node) + rewritten_source
36
37
  end
@@ -52,23 +53,14 @@ class NodeMutation::Action
52
53
  @rewritten_source ||= NodeMutation.adapter.rewritten_source(@node, @code)
53
54
  end
54
55
 
55
- # Squeeze spaces from source code.
56
- def squeeze_spaces
57
- if file_source[@start - 1] == ' ' && [' ', "\n", ';'].include?(file_source[@end])
58
- @start -= 1
59
- end
60
- end
61
-
62
- # Squeeze empty lines from source code.
63
- def squeeze_lines
64
- lines = file_source.split("\n")
65
- begin_line = NodeMutation.adapter.get_start_loc(@node).line
66
- end_line = NodeMutation.adapter.get_end_loc(@node).line
67
- before_line_is_blank = begin_line == 1 || lines[begin_line - 2] == ''
68
- after_line_is_blank = lines[end_line] == ''
56
+ # remove unused whitespace.
57
+ # e.g. `foobar(foo, bar)`, if we remove `foo`, the whitespace should also be removed,
58
+ # the code should be changed to `foobar(bar)`.
59
+ def remove_whitespace
60
+ return unless [' ', '('].include?(file_source[@start - 1])
69
61
 
70
- if lines.length > 1 && before_line_is_blank && after_line_is_blank
71
- @end += "\n".length
62
+ while file_source[@end] == ' '
63
+ @end += 1
72
64
  end
73
65
  end
74
66
 
@@ -76,14 +68,28 @@ class NodeMutation::Action
76
68
  # e.g. `foobar(foo, bar)`, if we remove `foo`, the comma should also be removed,
77
69
  # the code should be changed to `foobar(bar)`.
78
70
  def remove_comma
79
- if ',' == file_source[@start - 1]
80
- @start -= 1
81
- elsif ', ' == file_source[@start - 2, 2]
82
- @start -= 2
83
- elsif ', ' == file_source[@end, 2]
84
- @end += 2
85
- elsif ',' == file_source[@end]
86
- @end += 1
71
+ leading_count = 1
72
+ loop do
73
+ if file_source[@start - leading_count] == ','
74
+ @start -= leading_count
75
+ return
76
+ elsif ["\n", "\r", "\t", ' '].include?(file_source[@start - leading_count])
77
+ leading_count += 1
78
+ else
79
+ break
80
+ end
81
+ end
82
+
83
+ trailing_count = 0
84
+ loop do
85
+ if file_source[@end + trailing_count] == ','
86
+ @end += trailing_count + 1
87
+ return
88
+ elsif file_source[@end + trailing_count] == ' '
89
+ trailing_count += 1
90
+ else
91
+ break
92
+ end
87
93
  end
88
94
  end
89
95
 
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ NodeMutation::Location = Struct.new(:line, :column)
@@ -5,6 +5,8 @@ INDEX_REGEXP = /\A-?\d+\z/
5
5
  class NodeMutation::ParserAdapter < NodeMutation::Adapter
6
6
  def get_source(node)
7
7
  if node.is_a?(Array)
8
+ return "" if node.empty?
9
+
8
10
  source = file_content(node.first)
9
11
  source[node.first.loc.expression.begin_pos...node.last.loc.expression.end_pos]
10
12
  else
@@ -64,53 +66,50 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
64
66
  if node.is_a?(Array)
65
67
  if direct_child_name =~ INDEX_REGEXP
66
68
  child_node = node[direct_child_name.to_i]
67
- raise NodeMutation::MethodNotSupported, "#{direct_child_name} is not supported for #{get_source(node)}" unless child_node
69
+ raise NodeMutation::MethodNotSupported,
70
+ "#{direct_child_name} is not supported for #{get_source(node)}" unless child_node
68
71
  return child_node_range(child_node, nested_child_name) if nested_child_name
69
- return OpenStruct.new(start: child_node.loc.expression.begin_pos, end: child_node.loc.expression.end_pos)
72
+
73
+ return NodeMutation::Range.new(child_node.loc.expression.begin_pos, child_node.loc.expression.end_pos)
70
74
  end
71
75
 
72
- raise NodeMutation::MethodNotSupported, "#{direct_child_name} is not supported for #{get_source(node)}" unless node.respond_to?(direct_child_name)
76
+ raise NodeMutation::MethodNotSupported,
77
+ "#{direct_child_name} is not supported for #{get_source(node)}" unless node.respond_to?(direct_child_name)
78
+
73
79
  child_node = node.send(direct_child_name)
74
80
  return child_node_range(child_node, nested_child_name) if nested_child_name
75
- return OpenStruct.new(
76
- start: child_node.loc.expression.begin_pos,
77
- end: child_node.loc.expression.end_pos
78
- )
81
+
82
+ return NodeMutation::Range.new(child_node.loc.expression.begin_pos, child_node.loc.expression.end_pos)
79
83
  end
80
84
 
81
85
  case [node.type, child_name.to_sym]
82
86
  when %i[block pipes], %i[def parentheses], %i[defs parentheses]
83
- OpenStruct.new(
84
- start: node.arguments.first.loc.expression.begin_pos - 1,
85
- end: node.arguments.last.loc.expression.end_pos + 1
86
- )
87
+ NodeMutation::Range.new(node.arguments.first.loc.expression.begin_pos - 1, node.arguments.last.loc.expression.end_pos + 1)
87
88
  when %i[block arguments], %i[def arguments], %i[defs arguments]
88
- OpenStruct.new(
89
- start: node.arguments.first.loc.expression.begin_pos,
90
- end: node.arguments.last.loc.expression.end_pos
91
- )
89
+ NodeMutation::Range.new(node.arguments.first.loc.expression.begin_pos, node.arguments.last.loc.expression.end_pos)
92
90
  when %i[class name], %i[const name], %i[def name], %i[defs name]
93
- OpenStruct.new(start: node.loc.name.begin_pos, end: node.loc.name.end_pos)
91
+ NodeMutation::Range.new(node.loc.name.begin_pos, node.loc.name.end_pos)
94
92
  when %i[defs dot]
95
- OpenStruct.new(start: node.loc.operator.begin_pos, end: node.loc.operator.end_pos) if node.loc.operator
93
+ NodeMutation::Range.new(node.loc.operator.begin_pos, node.loc.operator.end_pos) if node.loc.operator
96
94
  when %i[defs self]
97
- OpenStruct.new(start: node.loc.operator.begin_pos - 'self'.length, end: node.loc.operator.begin_pos)
95
+ NodeMutation::Range.new(node.loc.operator.begin_pos - 'self'.length, node.loc.operator.begin_pos)
98
96
  when %i[lvasgn variable], %i[ivasgn variable], %i[cvasgn variable], %i[gvasgn variable]
99
- OpenStruct.new(start: node.loc.name.begin_pos, end: node.loc.name.end_pos)
97
+ NodeMutation::Range.new(node.loc.name.begin_pos, node.loc.name.end_pos)
100
98
  when %i[send dot], %i[csend dot]
101
- OpenStruct.new(start: node.loc.dot.begin_pos, end: node.loc.dot.end_pos) if node.loc.dot
99
+ NodeMutation::Range.new(node.loc.dot.begin_pos, node.loc.dot.end_pos) if node.loc.dot
102
100
  when %i[send message], %i[csend message]
103
101
  if node.loc.operator
104
- OpenStruct.new(start: node.loc.selector.begin_pos, end: node.loc.operator.end_pos)
102
+ NodeMutation::Range.new(node.loc.selector.begin_pos, node.loc.operator.end_pos)
105
103
  else
106
- OpenStruct.new(start: node.loc.selector.begin_pos, end: node.loc.selector.end_pos)
104
+ NodeMutation::Range.new(node.loc.selector.begin_pos, node.loc.selector.end_pos)
107
105
  end
108
106
  when %i[send parentheses], %i[csend parentheses]
109
107
  if node.loc.begin && node.loc.end
110
- OpenStruct.new(start: node.loc.begin.begin_pos, end: node.loc.end.end_pos)
108
+ NodeMutation::Range.new(node.loc.begin.begin_pos, node.loc.end.end_pos)
111
109
  end
112
110
  else
113
- raise NodeMutation::MethodNotSupported, "#{direct_child_name} is not supported for #{get_source(node)}" unless node.respond_to?(direct_child_name)
111
+ raise NodeMutation::MethodNotSupported,
112
+ "#{direct_child_name} is not supported for #{get_source(node)}" unless node.respond_to?(direct_child_name)
114
113
 
115
114
  child_node = node.send(direct_child_name)
116
115
 
@@ -120,10 +119,7 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
120
119
 
121
120
  if child_node.is_a?(Parser::AST::Node)
122
121
  return(
123
- OpenStruct.new(
124
- start: child_node.loc.expression.begin_pos,
125
- end: child_node.loc.expression.end_pos
126
- )
122
+ NodeMutation::Range.new(child_node.loc.expression.begin_pos, child_node.loc.expression.end_pos)
127
123
  )
128
124
  end
129
125
 
@@ -131,10 +127,7 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
131
127
  return nil if child_node.empty?
132
128
 
133
129
  return(
134
- OpenStruct.new(
135
- start: child_node.first.loc.expression.begin_pos,
136
- end: child_node.last.loc.expression.end_pos
137
- )
130
+ NodeMutation::Range.new(child_node.first.loc.expression.begin_pos, child_node.last.loc.expression.end_pos)
138
131
  )
139
132
  end
140
133
  end
@@ -149,12 +142,12 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
149
142
 
150
143
  def get_start_loc(node)
151
144
  begin_loc = node.loc.expression.begin
152
- OpenStruct.new(line: begin_loc.line, column: begin_loc.column)
145
+ NodeMutation::Location.new(begin_loc.line, begin_loc.column)
153
146
  end
154
147
 
155
148
  def get_end_loc(node)
156
149
  end_loc = node.loc.expression.end
157
- OpenStruct.new(line: end_loc.line, column: end_loc.column)
150
+ NodeMutation::Location.new(end_loc.line, end_loc.column)
158
151
  end
159
152
 
160
153
  def get_indent(node)
@@ -169,21 +162,26 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
169
162
  if node.is_a?(Array)
170
163
  if direct_child_name =~ INDEX_REGEXP
171
164
  child_node = node[direct_child_name.to_i]
172
- raise NodeMutation::MethodNotSupported, "#{direct_child_name} is not supported for #{get_source(node)}" unless child_node
165
+ raise NodeMutation::MethodNotSupported,
166
+ "#{direct_child_name} is not supported for #{get_source(node)}" unless child_node
173
167
  return child_node_by_name(child_node, nested_child_name) if nested_child_name
168
+
174
169
  return child_node
175
170
  end
176
171
 
177
- raise NodeMutation::MethodNotSupported, "#{direct_child_name} is not supported for #{get_source(node)}" unless node.respond_to?(direct_child_name)
172
+ raise NodeMutation::MethodNotSupported,
173
+ "#{direct_child_name} is not supported for #{get_source(node)}" unless node.respond_to?(direct_child_name)
174
+
178
175
  child_node = node.send(direct_child_name)
179
176
  return child_node_by_name(child_node, nested_child_name) if nested_child_name
177
+
180
178
  return child_node
181
179
  end
182
180
 
183
181
  if node.respond_to?(direct_child_name)
184
182
  child_node = node.send(direct_child_name)
185
183
  elsif direct_child_name.include?('(') && direct_child_name.include?(')')
186
- child_node = eval("node.#{direct_child_name}")
184
+ child_node = node.instance_eval(direct_child_name)
187
185
  else
188
186
  raise NodeMutation::MethodNotSupported, "#{direct_child_name} is not supported for #{get_source(node)}"
189
187
  end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ NodeMutation::Range = Struct.new(:start, :end)
@@ -30,8 +30,12 @@ class NodeMutation::Result
30
30
  def to_hash
31
31
  hash = { file_path: file_path }
32
32
  @options.each do |key, value|
33
- hash[key] = value.is_a?(Array) ? value.map { |action| action.to_h } : value
33
+ if key == :actions
34
+ hash[:actions] = value.map { |action| { start: action.start, end: action.end, new_code: action.new_code } }
35
+ else
36
+ hash[key] = value
37
+ end
34
38
  end
35
39
  hash
36
40
  end
37
- end
41
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class NodeMutation
4
- VERSION = "1.10.0"
4
+ VERSION = "1.11.0"
5
5
  end
data/lib/node_mutation.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ostruct'
4
-
5
3
  require_relative "node_mutation/version"
6
4
 
7
5
  class NodeMutation
@@ -20,12 +18,14 @@ class NodeMutation
20
18
  autoload :ReplaceWithAction, 'node_mutation/action/replace_with_action'
21
19
  autoload :WrapAction, 'node_mutation/action/wrap_action'
22
20
  autoload :NoopAction, 'node_mutation/action/noop_action'
21
+ autoload :Range, 'node_mutation/range'
22
+ autoload :Location, 'node_mutation/location'
23
23
  autoload :Result, 'node_mutation/result'
24
24
  autoload :Strategy, 'node_mutation/strategy'
25
25
 
26
26
  attr_reader :actions
27
27
 
28
- class <<self
28
+ class << self
29
29
  # Configure NodeMutation
30
30
  # @param [Hash] options options to configure
31
31
  # @option options [NodeMutation::Adapter] :adapter the adpater
@@ -92,8 +92,7 @@ class NodeMutation
92
92
  # Delete source code of the child ast node.
93
93
  # @param node [Node] ast node
94
94
  # @param selectors [Array<Symbol>] selector names of child node.
95
- # @param options [Hash]
96
- # @option and_comma [Boolean] delete extra comma.
95
+ # @param and_comma [Boolean] delete extra comma.
97
96
  # @example
98
97
  # source code of the ast node is
99
98
  # FactoryBot.create(...)
@@ -101,8 +100,8 @@ class NodeMutation
101
100
  # mutation.delete(node, :receiver, :dot)
102
101
  # the source code will be rewritten to
103
102
  # create(...)
104
- def delete(node, *selectors, **options)
105
- @actions << DeleteAction.new(node, *selectors, **options).process
103
+ def delete(node, *selectors, and_comma: false)
104
+ @actions << DeleteAction.new(node, *selectors, and_comma: and_comma).process
106
105
  end
107
106
 
108
107
  # Insert code to the ast node.
@@ -110,6 +109,7 @@ class NodeMutation
110
109
  # @param code [String] code need to be inserted.
111
110
  # @param at [String] insert position, beginning or end
112
111
  # @param to [String] where to insert, if it is nil, will insert to current node.
112
+ # @param and_comma [Boolean] insert extra comma.
113
113
  # @example
114
114
  # source code of the ast node is
115
115
  # open('http://test.com')
@@ -117,8 +117,8 @@ class NodeMutation
117
117
  # mutation.insert(node, 'URI.', at: 'beginning')
118
118
  # the source code will be rewritten to
119
119
  # URI.open('http://test.com')
120
- def insert(node, code, at: 'end', to: nil)
121
- @actions << InsertAction.new(node, code, at: at, to: to).process
120
+ def insert(node, code, at: 'end', to: nil, and_comma: false)
121
+ @actions << InsertAction.new(node, code, at: at, to: to, and_comma: and_comma).process
122
122
  end
123
123
 
124
124
  # Prepend code to the ast node.
@@ -142,16 +142,15 @@ class NodeMutation
142
142
 
143
143
  # Remove source code of the ast node.
144
144
  # @param node [Node] ast node
145
- # @param options [Hash] options.
146
- # @option and_comma [Boolean] delete extra comma.
145
+ # @param and_comma [Boolean] delete extra comma.
147
146
  # @example
148
147
  # source code of the ast node is
149
148
  # puts "test"
150
149
  # then we call
151
150
  # mutation.remove(node)
152
151
  # the source code will be removed
153
- def remove(node, **options)
154
- @actions << RemoveAction.new(node, **options).process
152
+ def remove(node, and_comma: false)
153
+ @actions << RemoveAction.new(node, and_comma: and_comma).process
155
154
  end
156
155
 
157
156
  # Replace child node of the ast node with new code.
@@ -220,21 +219,17 @@ class NodeMutation
220
219
  return NodeMutation::Result.new(affected: false, conflicted: false)
221
220
  end
222
221
 
223
- conflict_actions = []
224
222
  source = +@source
225
223
  @actions.sort_by! { |action| [action.start, action.end] }
226
224
  conflict_actions = get_conflict_actions
227
225
  if conflict_actions.size > 0 && strategy?(Strategy::THROW_ERROR)
228
226
  raise ConflictActionError, "mutation actions are conflicted"
229
227
  end
228
+
230
229
  @actions.reverse_each do |action|
231
230
  source[action.start...action.end] = action.new_code if action.new_code
232
231
  end
233
- NodeMutation::Result.new(
234
- affected: true,
235
- conflicted: !conflict_actions.empty?,
236
- new_source: source
237
- )
232
+ NodeMutation::Result.new(affected: true, conflicted: !conflict_actions.empty?, new_source: source)
238
233
  end
239
234
 
240
235
  # Test actions and return the actions.
@@ -249,17 +244,13 @@ class NodeMutation
249
244
  return NodeMutation::Result.new(affected: false, conflicted: false, actions: [])
250
245
  end
251
246
 
252
- conflict_actions = []
253
247
  @actions.sort_by! { |action| [action.start, action.end] }
254
248
  conflict_actions = get_conflict_actions
255
249
  if conflict_actions.size > 0 && strategy?(Strategy::THROW_ERROR)
256
250
  raise ConflictActionError, "mutation actions are conflicted"
257
251
  end
258
- NodeMutation::Result.new(
259
- affected: true,
260
- conflicted: !conflict_actions.empty?,
261
- actions: format_actions(@actions)
262
- )
252
+
253
+ NodeMutation::Result.new(affected: true, conflicted: !conflict_actions.empty?, actions: @actions)
263
254
  end
264
255
 
265
256
  private
@@ -294,8 +285,4 @@ class NodeMutation
294
285
  def strategy?(strategy)
295
286
  NodeMutation.strategy & strategy == strategy
296
287
  end
297
-
298
- def format_actions(actions)
299
- actions.map { |action| OpenStruct.new(start: action.start, end: action.end, new_code: action.new_code ) }
300
- end
301
288
  end
@@ -19,11 +19,12 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  # Specify which files should be added to the gem when it is released.
21
21
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
- `git ls-files -z`.split("\x0").reject do |f|
24
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
22
+ spec.files =
23
+ Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
25
27
  end
26
- end
27
28
  spec.bindir = "exe"
28
29
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
30
  spec.require_paths = ["lib"]
@@ -5,15 +5,15 @@ class NodeMutation::Adapter[T]
5
5
 
6
6
  def file_content: (node: T) -> String
7
7
 
8
- def child_node_range: (node: T, child_name: String) -> OpenStruct
8
+ def child_node_range: (node: T, child_name: String) -> NodeMutation::Range
9
9
 
10
10
  def get_start: (node: T) -> Integer
11
11
 
12
12
  def get_end: (node: T) -> Integer
13
13
 
14
- def get_start_loc: (node: T) -> OpenStruct
14
+ def get_start_loc: (node: T) -> NodeMutation::Location
15
15
 
16
- def get_end_loc: (node: T) -> OpenStruct
16
+ def get_end_loc: (node: T) -> NodeMutation::Location
17
17
 
18
18
  def get_indent: (node: T) -> Integer
19
19
  end
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.10.0
4
+ version: 1.11.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-03-01 00:00:00.000000000 Z
11
+ date: 2023-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erubis
@@ -51,7 +51,9 @@ files:
51
51
  - lib/node_mutation/action/replace_with_action.rb
52
52
  - lib/node_mutation/action/wrap_action.rb
53
53
  - lib/node_mutation/adapter.rb
54
+ - lib/node_mutation/location.rb
54
55
  - lib/node_mutation/parser_adapter.rb
56
+ - lib/node_mutation/range.rb
55
57
  - lib/node_mutation/result.rb
56
58
  - lib/node_mutation/strategy.rb
57
59
  - lib/node_mutation/version.rb
@@ -80,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
82
  - !ruby/object:Gem::Version
81
83
  version: '0'
82
84
  requirements: []
83
- rubygems_version: 3.4.1
85
+ rubygems_version: 3.4.6
84
86
  signing_key:
85
87
  specification_version: 4
86
88
  summary: ast node mutation apis