node_mutation 1.10.1 → 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: 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