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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/lib/node_mutation/action/delete_action.rb +3 -3
- data/lib/node_mutation/action/insert_action.rb +8 -2
- data/lib/node_mutation/action/remove_action.rb +46 -16
- data/lib/node_mutation/action/replace_action.rb +4 -2
- data/lib/node_mutation/action/wrap_action.rb +1 -1
- data/lib/node_mutation/action.rb +31 -25
- data/lib/node_mutation/location.rb +3 -0
- data/lib/node_mutation/parser_adapter.rb +35 -37
- data/lib/node_mutation/range.rb +3 -0
- data/lib/node_mutation/result.rb +6 -2
- data/lib/node_mutation/version.rb +1 -1
- data/lib/node_mutation.rb +16 -29
- data/node_mutation.gemspec +5 -4
- data/sig/node_mutation/adapter.rbs +3 -3
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d41515d4be39619d95802e531496159773a1202de61c8fa3c3d269d2a72926da
|
4
|
+
data.tar.gz: 2bd0415eec90cb45cdc2dcfacce1cf6529fcdf9f12e6fcd44054aa273e4c4b0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
@@ -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
|
-
|
26
|
+
.compact.map(&:start).min
|
27
27
|
@end = @selectors.map { |selector| NodeMutation.adapter.child_node_range(@node, selector) }
|
28
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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[
|
38
|
+
NodeMutation.adapter.get_source(@node) == file_source[@start...@end].strip.chomp(',')
|
41
39
|
end
|
42
40
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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 }
|
27
|
-
|
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
|
data/lib/node_mutation/action.rb
CHANGED
@@ -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 }
|
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
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
71
|
-
@end +=
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
@
|
85
|
-
|
86
|
-
|
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
|
|
@@ -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,
|
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
|
-
|
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,
|
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
|
-
|
76
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
91
|
+
NodeMutation::Range.new(node.loc.name.begin_pos, node.loc.name.end_pos)
|
94
92
|
when %i[defs dot]
|
95
|
-
|
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
|
-
|
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
|
-
|
97
|
+
NodeMutation::Range.new(node.loc.name.begin_pos, node.loc.name.end_pos)
|
100
98
|
when %i[send dot], %i[csend dot]
|
101
|
-
|
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
|
-
|
102
|
+
NodeMutation::Range.new(node.loc.selector.begin_pos, node.loc.operator.end_pos)
|
105
103
|
else
|
106
|
-
|
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
|
-
|
108
|
+
NodeMutation::Range.new(node.loc.begin.begin_pos, node.loc.end.end_pos)
|
111
109
|
end
|
112
110
|
else
|
113
|
-
raise NodeMutation::MethodNotSupported,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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,
|
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 =
|
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
|
data/lib/node_mutation/result.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
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,
|
105
|
-
@actions << DeleteAction.new(node, *selectors,
|
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
|
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,
|
154
|
-
@actions << RemoveAction.new(node,
|
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
|
-
|
259
|
-
|
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
|
data/node_mutation.gemspec
CHANGED
@@ -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 =
|
23
|
-
|
24
|
-
|
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) ->
|
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) ->
|
14
|
+
def get_start_loc: (node: T) -> NodeMutation::Location
|
15
15
|
|
16
|
-
def get_end_loc: (node: T) ->
|
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.
|
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-
|
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.
|
85
|
+
rubygems_version: 3.4.6
|
84
86
|
signing_key:
|
85
87
|
specification_version: 4
|
86
88
|
summary: ast node mutation apis
|