node_mutation 1.10.1 → 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: 3f69bebea36b4152c7edb233d2d179bfee796988977c36f6375a12a240a77615
4
- data.tar.gz: af1a35eb1b9ec9b3c657d976dd9b0b392d24288de6083f5d98c3d00dc036ff61
3
+ metadata.gz: d41515d4be39619d95802e531496159773a1202de61c8fa3c3d269d2a72926da
4
+ data.tar.gz: 2bd0415eec90cb45cdc2dcfacce1cf6529fcdf9f12e6fcd44054aa273e4c4b0b
5
5
  SHA512:
6
- metadata.gz: b07a1795e19142226bd48215514d67cf0f3428ec1b03dbcfe49ed4f9825c962427943b6a1f633bc7d1627178354c3c30989af1a5e63dcbe125af54c98f280a45
7
- data.tar.gz: f87aed954b86a4948939528ce1af8bbb779f4673ef8795cd498d015bce84d3abb271db2b44ffd65fdac2f571acbe8d03cfb2ae0cf54a8bd9d97780799e75a521
6
+ metadata.gz: 656c00012eac7a7b6d2229933fb7e08fd7f0f9a986e90f7db69cc5129097702a2850cdf5494c6f1dae3d6f7da9d0ba642ffd7eb4362517a5c0f465a0d49a52d3
7
+ data.tar.gz: 4faced52fd71462a10c54970afb4412858c355c96bf8713e132f886535d3a1309aa792bc1f4e946c0549492c351c9750fa3ab1d05c3ecd0fda0d58f84c98427f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
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
+
3
8
  ## 1.10.1 (2023-03-13)
4
9
 
5
10
  * Remove `OpenStruct`, use `Struct` instead
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- node_mutation (1.10.1)
4
+ node_mutation (1.11.0)
5
5
  erubis
6
6
 
7
7
  GEM
@@ -26,7 +26,7 @@ class NodeMutation::DeleteAction < NodeMutation::Action
26
26
  .compact.map(&:start).min
27
27
  @end = @selectors.map { |selector| NodeMutation.adapter.child_node_range(@node, selector) }
28
28
  .compact.map(&:end).max
29
- squeeze_spaces
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
@@ -53,23 +53,14 @@ class NodeMutation::Action
53
53
  @rewritten_source ||= NodeMutation.adapter.rewritten_source(@node, @code)
54
54
  end
55
55
 
56
- # Squeeze spaces from source code.
57
- def squeeze_spaces
58
- if file_source[@start - 1] == ' ' && [' ', "\n", ';'].include?(file_source[@end])
59
- @start -= 1
60
- end
61
- end
62
-
63
- # Squeeze empty lines from source code.
64
- def squeeze_lines
65
- lines = file_source.split("\n")
66
- begin_line = NodeMutation.adapter.get_start_loc(@node).line
67
- end_line = NodeMutation.adapter.get_end_loc(@node).line
68
- before_line_is_blank = begin_line == 1 || lines[begin_line - 2] == ''
69
- 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])
70
61
 
71
- if lines.length > 1 && before_line_is_blank && after_line_is_blank
72
- @end += "\n".length
62
+ while file_source[@end] == ' '
63
+ @end += 1
73
64
  end
74
65
  end
75
66
 
@@ -77,14 +68,28 @@ class NodeMutation::Action
77
68
  # e.g. `foobar(foo, bar)`, if we remove `foo`, the comma should also be removed,
78
69
  # the code should be changed to `foobar(bar)`.
79
70
  def remove_comma
80
- if ',' == file_source[@start - 1]
81
- @start -= 1
82
- elsif ', ' == file_source[@start - 2, 2]
83
- @start -= 2
84
- elsif ', ' == file_source[@end, 2]
85
- @end += 2
86
- elsif ',' == file_source[@end]
87
- @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
88
93
  end
89
94
  end
90
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
@@ -179,7 +181,7 @@ class NodeMutation::ParserAdapter < NodeMutation::Adapter
179
181
  if node.respond_to?(direct_child_name)
180
182
  child_node = node.send(direct_child_name)
181
183
  elsif direct_child_name.include?('(') && direct_child_name.include?(')')
182
- child_node = eval("node.#{direct_child_name}")
184
+ child_node = node.instance_eval(direct_child_name)
183
185
  else
184
186
  raise NodeMutation::MethodNotSupported, "#{direct_child_name} is not supported for #{get_source(node)}"
185
187
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class NodeMutation
4
- VERSION = "1.10.1"
4
+ VERSION = "1.11.0"
5
5
  end
data/lib/node_mutation.rb CHANGED
@@ -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.
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.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-13 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