node_mutation 1.10.0 → 1.11.0

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: 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