synvert-core 0.56.1 → 0.58.2

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/lib/synvert/core/node_ext.rb +26 -5
  4. data/lib/synvert/core/rewriter/action/append_action.rb +3 -16
  5. data/lib/synvert/core/rewriter/action/delete_action.rb +5 -13
  6. data/lib/synvert/core/rewriter/action/insert_action.rb +3 -16
  7. data/lib/synvert/core/rewriter/action/insert_after_action.rb +3 -12
  8. data/lib/synvert/core/rewriter/action/prepend_action.rb +13 -21
  9. data/lib/synvert/core/rewriter/action/remove_action.rb +9 -17
  10. data/lib/synvert/core/rewriter/action/replace_action.rb +3 -12
  11. data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +3 -11
  12. data/lib/synvert/core/rewriter/action/replace_with_action.rb +3 -12
  13. data/lib/synvert/core/rewriter/action/wrap_action.rb +3 -12
  14. data/lib/synvert/core/rewriter/action.rb +31 -10
  15. data/lib/synvert/core/rewriter/instance.rb +15 -13
  16. data/lib/synvert/core/rewriter/ruby_version.rb +1 -1
  17. data/lib/synvert/core/rewriter/scope/within_scope.rb +70 -22
  18. data/lib/synvert/core/version.rb +1 -1
  19. data/spec/synvert/core/node_ext_spec.rb +30 -0
  20. data/spec/synvert/core/rewriter/action/append_action_spec.rb +2 -2
  21. data/spec/synvert/core/rewriter/action/delete_action_spec.rb +1 -1
  22. data/spec/synvert/core/rewriter/action/insert_action_spec.rb +2 -2
  23. data/spec/synvert/core/rewriter/action/insert_after_action_spec.rb +1 -1
  24. data/spec/synvert/core/rewriter/action/prepend_action_spec.rb +4 -4
  25. data/spec/synvert/core/rewriter/action/remove_action_spec.rb +1 -1
  26. data/spec/synvert/core/rewriter/action/replace_action_spec.rb +1 -1
  27. data/spec/synvert/core/rewriter/action/replace_with_action_spec.rb +2 -2
  28. data/spec/synvert/core/rewriter/action/wrap_action_spec.rb +1 -1
  29. data/spec/synvert/core/rewriter/instance_spec.rb +44 -14
  30. data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +27 -0
  31. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e60611e972f774c97a102f5db6bc171b024cb48aa4dcc94dd07e9de6d25506a2
4
- data.tar.gz: 2a7f4da54b1ed5caeb7fea49dd07ca1cc9fad1aceef082d223e420b928825bb9
3
+ metadata.gz: b5580dae81209573a74a22792f5f50f329805eb67d74c67d28beb9438115a6ba
4
+ data.tar.gz: 3e97957ff1b4af95a5506ff240c0e500ccf69242d4c03a5db16c84ae03917520
5
5
  SHA512:
6
- metadata.gz: f2fa37ef33a1ec38afbf094715b239c46e35e92aa386f5fae635be4419da964a8543f07df36c4321408b9a5d80a9a252d68674ccb9040865191d0299d9775667
7
- data.tar.gz: a31385980557ef6c6031365f1554c5e957e6933b3aaaa65d902011dc3920599c8d24ae80bde55227f36643ebcc6808ee55ff2f028ca4c92cab94eeb04e606413
6
+ metadata.gz: 6829aded4d70daf81fa7cab074c7029ee945997434d357cc251cd993ba89ec56bb1b74b4d98964a150cad0d8f3fbb0f9781f31c5eab760b3c5ba392dffac17f1
7
+ data.tar.gz: 57d35f30118d60e234595d66b65580f29b205e7f09115ce3b44c1085d6845ff02c831855e2cd7267983b9e7a87a1480080f28a4cb533a0520b944e445bccd15b
data/CHANGELOG.md CHANGED
@@ -1,9 +1,22 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 0.56.1 (2021-09-19)
3
+ ## 0.58.2 (2021-10-23)
4
+
5
+ * Do not break the whole `recursive_children`
6
+
7
+ ## 0.58.0 (2021-10-23)
8
+
9
+ * Support `left_value` and `right_value` for `and` and `or` node
10
+ * Rewrite `within_node` and `within_direct_node`, `WithinScope` accepts `recursive` and `direct` options
11
+
12
+ ## 0.57.0 (2021-10-02)
4
13
 
5
14
  * Compare ruby version in `.ruby-version` or `.rvmrc`
15
+ * Support `left_value` and `right_value` for `casgn` node
16
+ * Refactor - `calculate_position` to set `begin_pos` and `end_pos`
6
17
  * Abstract `squeeze_spaces` and `squeeze_lines`
18
+ * Remove unused comma after delete/remove action
19
+ * Handle array child node in `childNodeRange`
7
20
 
8
21
  ## 0.56.0 (2021-09-14)
9
22
 
@@ -215,7 +215,7 @@ module Parser::AST
215
215
  # @return [Parser::AST::Node] variable nodes.
216
216
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
217
217
  def left_value
218
- if %i[masgn lvasgn ivasgn].include? type
218
+ if %i[masgn lvasgn ivasgn cvasgn and or].include? type
219
219
  children[0]
220
220
  elsif :or_asgn == type
221
221
  children[0].children[0]
@@ -229,7 +229,7 @@ module Parser::AST
229
229
  # @return [Array<Parser::AST::Node>] variable nodes.
230
230
  # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
231
231
  def right_value
232
- if %i[masgn lvasgn ivasgn or_asgn].include? type
232
+ if %i[masgn lvasgn ivasgn cvasgn or_asgn and or].include? type
233
233
  children[1]
234
234
  else
235
235
  raise Synvert::Core::MethodNotSupported, "right_value is not handled for #{debug_info}"
@@ -373,7 +373,28 @@ module Parser::AST
373
373
  if respond_to?(direct_child_name)
374
374
  child_node = send(direct_child_name)
375
375
 
376
- return child_node.child_node_range(nested_child_name) if nested_child_name
376
+ if nested_child_name
377
+ if child_node.is_a?(Array)
378
+ child_direct_child_name, *child_nested_child_name = nested_child_name
379
+ child_direct_child_node = child_direct_child_name =~ /\A\d+\z/ ? child_node[child_direct_child_name] : child_node.send(child_direct_child_name)
380
+ if child_nested_child_name.length > 0
381
+ return child_direct_child_node.child_node_range(child_nested_child_name.join('.'))
382
+ elsif child_direct_child_node
383
+ return (
384
+ Parser::Source::Range.new(
385
+ '(string)',
386
+ child_direct_child_node.loc.expression.begin_pos,
387
+ child_direct_child_node.loc.expression.end_pos
388
+ )
389
+ )
390
+ else
391
+ raise Synvert::Core::MethodNotSupported,
392
+ "child_node_range is not handled for #{debug_info}, child_name: #{child_name}"
393
+ end
394
+ end
395
+
396
+ return child_node.child_node_range(nested_child_name)
397
+ end
377
398
 
378
399
  return nil if child_node.nil?
379
400
 
@@ -411,8 +432,8 @@ module Parser::AST
411
432
  def recursive_children(&block)
412
433
  children.each do |child|
413
434
  if child.is_a?(Parser::AST::Node)
414
- yield child
415
- child.recursive_children(&block)
435
+ stop = yield child
436
+ child.recursive_children(&block) unless stop == :stop
416
437
  end
417
438
  end
418
439
  end
@@ -5,22 +5,9 @@ module Synvert::Core
5
5
  class Rewriter::AppendAction < Rewriter::Action
6
6
  END_LENGTH = "\nend".length
7
7
 
8
- # Begin position to append code.
9
- #
10
- # @return [Integer] begin position.
11
- def begin_pos
12
- if :begin == @node.type
13
- @node.loc.expression.end_pos
14
- else
15
- @node.loc.expression.end_pos - @node.column - END_LENGTH
16
- end
17
- end
18
-
19
- # End position, always same to begin position.
20
- #
21
- # @return [Integer] end position.
22
- def end_pos
23
- begin_pos
8
+ def calculate_position
9
+ @begin_pos = :begin == @node.type ? @node.loc.expression.end_pos : @node.loc.expression.end_pos - @node.column - END_LENGTH
10
+ @end_pos = @begin_pos
24
11
  end
25
12
 
26
13
  private
@@ -8,19 +8,11 @@ module Synvert::Core
8
8
  @selectors = selectors
9
9
  end
10
10
 
11
- # Begin position of code to delete.
12
- #
13
- # @return [Integer] begin position.
14
- def begin_pos
15
- pos = @selectors.map { |selector| @node.child_node_range(selector) }.compact.map(&:begin_pos).min
16
- squeeze_spaces(pos, end_pos)
17
- end
18
-
19
- # End position of code to delete.
20
- #
21
- # @return [Integer] end position.
22
- def end_pos
23
- @selectors.map { |selector| @node.child_node_range(selector) }.compact.map(&:end_pos).max
11
+ def calculate_position
12
+ @begin_pos = @selectors.map { |selector| @node.child_node_range(selector) }.compact.map(&:begin_pos).min
13
+ @end_pos = @selectors.map { |selector| @node.child_node_range(selector) }.compact.map(&:end_pos).max
14
+ squeeze_spaces
15
+ remove_comma
24
16
  end
25
17
 
26
18
  # The rewritten code, always empty string.
@@ -8,22 +8,9 @@ module Synvert::Core
8
8
  @at = at
9
9
  end
10
10
 
11
- # Begin position to insert code.
12
- #
13
- # @return [Integer] begin position.
14
- def begin_pos
15
- if @at == 'end'
16
- @node.loc.expression.end_pos
17
- else
18
- @node.loc.expression.begin_pos
19
- end
20
- end
21
-
22
- # End position, always same to begin position.
23
- #
24
- # @return [Integer] end position.
25
- def end_pos
26
- begin_pos
11
+ def calculate_position
12
+ @begin_pos = @at == 'end' ? @node.loc.expression.end_pos : @node.loc.expression.begin_pos
13
+ @end_pos = @begin_pos
27
14
  end
28
15
 
29
16
  # The rewritten source code.
@@ -3,18 +3,9 @@
3
3
  module Synvert::Core
4
4
  # InsertAfterAction to insert code next to the node.
5
5
  class Rewriter::InsertAfterAction < Rewriter::Action
6
- # Begin position to insert code.
7
- #
8
- # @return [Integer] begin position.
9
- def begin_pos
10
- @node.loc.expression.end_pos
11
- end
12
-
13
- # End position, always same to begin position.
14
- #
15
- # @return [Integer] end position.
16
- def end_pos
17
- begin_pos
6
+ def calculate_position
7
+ @begin_pos = @node.loc.expression.end_pos
8
+ @end_pos = @begin_pos
18
9
  end
19
10
 
20
11
  private
@@ -5,29 +5,21 @@ module Synvert::Core
5
5
  class Rewriter::PrependAction < Rewriter::Action
6
6
  DO_LENGTH = ' do'.length
7
7
 
8
- # Begin position to prepend code.
9
- #
10
- # @return [Integer] begin position.
11
- def begin_pos
12
- case @node.type
13
- when :block
14
- if @node.children[1].children.empty?
15
- @node.children[0].loc.expression.end_pos + DO_LENGTH
8
+ def calculate_position
9
+ @begin_pos =
10
+ case @node.type
11
+ when :block
12
+ if @node.children[1].children.empty?
13
+ @node.children[0].loc.expression.end_pos + DO_LENGTH
14
+ else
15
+ @node.children[1].loc.expression.end_pos
16
+ end
17
+ when :class
18
+ @node.children[1] ? @node.children[1].loc.expression.end_pos : @node.children[0].loc.expression.end_pos
16
19
  else
17
- @node.children[1].loc.expression.end_pos
20
+ @node.children.last.loc.expression.end_pos
18
21
  end
19
- when :class
20
- @node.children[1] ? @node.children[1].loc.expression.end_pos : @node.children[0].loc.expression.end_pos
21
- else
22
- @node.children.last.loc.expression.end_pos
23
- end
24
- end
25
-
26
- # End position, always same to begin position.
27
- #
28
- # @return [Integer] end position.
29
- def end_pos
30
- begin_pos
22
+ @end_pos = @begin_pos
31
23
  end
32
24
 
33
25
  private
@@ -10,23 +10,16 @@ module Synvert::Core
10
10
  # Begin position of code to replace.
11
11
  #
12
12
  # @return [Integer] begin position.
13
- def begin_pos
13
+ def calculate_position
14
14
  if take_whole_line?
15
- start_index
15
+ @begin_pos = start_index
16
+ @end_pos = end_index
17
+ squeeze_lines
16
18
  else
17
- pos = @node.loc.expression.begin_pos
18
- squeeze_spaces(pos, end_pos)
19
- end
20
- end
21
-
22
- # End position of code to replace.
23
- #
24
- # @return [Integer] end position.
25
- def end_pos
26
- if take_whole_line?
27
- end_index
28
- else
29
- @node.loc.expression.end_pos
19
+ @begin_pos = @node.loc.expression.begin_pos
20
+ @end_pos = @node.loc.expression.end_pos
21
+ squeeze_spaces
22
+ remove_comma
30
23
  end
31
24
  end
32
25
 
@@ -48,8 +41,7 @@ module Synvert::Core
48
41
 
49
42
  def end_index
50
43
  index = file_source[@node.loc.expression.end_pos..-1].index("\n")
51
- pos = index ? @node.loc.expression.end_pos + index + "\n".length : @node.loc.expression.end_pos
52
- squeeze_lines(pos, @node.loc.expression.first_line, @node.loc.expression.last_line)
44
+ index ? @node.loc.expression.end_pos + index + "\n".length : @node.loc.expression.end_pos
53
45
  end
54
46
  end
55
47
  end
@@ -8,18 +8,9 @@ module Synvert::Core
8
8
  @selectors = selectors
9
9
  end
10
10
 
11
- # Begin position of code to replace.
12
- #
13
- # @return [Integer] begin position.
14
- def begin_pos
15
- @selectors.map { |selector| @node.child_node_range(selector).begin_pos }.min
16
- end
17
-
18
- # End position of code to replace.
19
- #
20
- # @return [Integer] end position.
21
- def end_pos
22
- @selectors.map { |selector| @node.child_node_range(selector).end_pos }.max
11
+ def calculate_position
12
+ @begin_pos = @selectors.map { |selector| @node.child_node_range(selector).begin_pos }.min
13
+ @end_pos = @selectors.map { |selector| @node.child_node_range(selector).end_pos }.max
23
14
  end
24
15
 
25
16
  # The rewritten source code.
@@ -8,25 +8,17 @@ module Synvert::Core
8
8
  super
9
9
  end
10
10
 
11
- # Begin position of code to replace.
12
- #
13
- # @return [Integer] begin position.
14
- def begin_pos
11
+ def calculate_position
15
12
  node_begin_pos = @node.loc.expression.begin_pos
16
13
  while @node.loc.expression.source_buffer.source[node_begin_pos -= 1] == ' '
17
14
  end
18
- node_begin_pos - Engine::ERUBY_STMT_SPLITTER.length + 1
19
- end
15
+ @begin_pos = node_begin_pos - Engine::ERUBY_STMT_SPLITTER.length + 1
20
16
 
21
- # End position of code to replace.
22
- #
23
- # @return [Integer] end position.
24
- def end_pos
25
17
  node_begin_pos = @node.loc.expression.begin_pos
26
18
  node_begin_pos += @node.loc.expression.source.index 'do'
27
19
  while @node.loc.expression.source_buffer.source[node_begin_pos += 1] != '@'
28
20
  end
29
- node_begin_pos
21
+ @end_pos = node_begin_pos
30
22
  end
31
23
 
32
24
  # The rewritten erb expr code.
@@ -3,18 +3,9 @@
3
3
  module Synvert::Core
4
4
  # ReplaceWithAction to replace code.
5
5
  class Rewriter::ReplaceWithAction < Rewriter::Action
6
- # Begin position of code to replace.
7
- #
8
- # @return [Integer] begin position.
9
- def begin_pos
10
- @node.loc.expression.begin_pos
11
- end
12
-
13
- # End position of code to replace.
14
- #
15
- # @return [Integer] end position.
16
- def end_pos
17
- @node.loc.expression.end_pos
6
+ def calculate_position
7
+ @begin_pos = @node.loc.expression.begin_pos
8
+ @end_pos = @node.loc.expression.end_pos
18
9
  end
19
10
 
20
11
  # The rewritten source code with proper indent.
@@ -11,18 +11,9 @@ module Synvert::Core
11
11
  @indent = indent || @node.column
12
12
  end
13
13
 
14
- # Begin position of code to wrap.
15
- #
16
- # @return [Integer] begin position.
17
- def begin_pos
18
- @node.loc.expression.begin_pos
19
- end
20
-
21
- # End position of code to wrap.
22
- #
23
- # @return [Integer] end position.
24
- def end_pos
25
- @node.loc.expression.end_pos
14
+ def calculate_position
15
+ @begin_pos = @node.loc.expression.begin_pos
16
+ @end_pos = @node.loc.expression.end_pos
26
17
  end
27
18
 
28
19
  # The rewritten source code.
@@ -5,6 +5,12 @@ module Synvert::Core
5
5
  class Rewriter::Action
6
6
  DEFAULT_INDENT = 2
7
7
 
8
+ # @!attribute [r] begin_pos
9
+ # @return [Integer] begin position
10
+ # @!attribute [r] end_pos
11
+ # @return [Integer] end position
12
+ attr_reader :begin_pos, :end_pos
13
+
8
14
  # Initialize an action.
9
15
  #
10
16
  # @param instance [Synvert::Core::Rewriter::Instance]
@@ -15,6 +21,11 @@ module Synvert::Core
15
21
  @node = @instance.current_node
16
22
  end
17
23
 
24
+ def process
25
+ calculate_position
26
+ self
27
+ end
28
+
18
29
  # Line number of current node.
19
30
  #
20
31
  # @return [Integer] line number.
@@ -42,23 +53,33 @@ module Synvert::Core
42
53
  @rewritten_source ||= @node.rewritten_source(@code)
43
54
  end
44
55
 
45
- def squeeze_spaces(begin_pos, end_pos)
46
- if file_source[begin_pos - 1] == ' ' && file_source[end_pos] == ' '
47
- begin_pos - 1
48
- else
49
- begin_pos
56
+ def squeeze_spaces
57
+ if file_source[@begin_pos - 1] == ' ' && file_source[@end_pos] == ' '
58
+ @begin_pos -= 1
50
59
  end
51
60
  end
52
61
 
53
- def squeeze_lines(end_pos, begin_line, end_line)
62
+ def squeeze_lines
54
63
  lines = file_source.split("\n")
64
+ begin_line = @node.loc.expression.first_line
65
+ end_line = @node.loc.expression.last_line
55
66
  before_line_is_blank = begin_line == 1 || lines[begin_line - 2] == ''
56
67
  after_line_is_blank = lines[end_line] == ''
57
68
 
58
- if before_line_is_blank && after_line_is_blank
59
- end_pos + "\n".length
60
- else
61
- end_pos
69
+ if lines.length > 1 && before_line_is_blank && after_line_is_blank
70
+ @end_pos += "\n".length
71
+ end
72
+ end
73
+
74
+ def remove_comma
75
+ if ',' == file_source[@begin_pos - 1]
76
+ @begin_pos -= 1
77
+ elsif ', ' == file_source[@begin_pos - 2, 2]
78
+ @begin_pos -= 2
79
+ elsif ', ' == file_source[@end_pos, 2]
80
+ @end_pos += 2
81
+ elsif ',' == file_source[@end_pos]
82
+ @end_pos += 1
62
83
  end
63
84
  end
64
85
 
@@ -161,9 +161,11 @@ module Synvert::Core
161
161
  # then continue operating on each matching ast node.
162
162
  #
163
163
  # @param rules [Hash] rules to find mathing ast nodes.
164
+ # @param options [Hash] optional, set if recursive or not.
164
165
  # @param block [Block] block code to continue operating on the matching nodes.
165
- def within_node(rules, &block)
166
- Rewriter::WithinScope.new(self, rules, { recursive: true }, &block).process
166
+ def within_node(rules, options = nil, &block)
167
+ options ||= { recursive: true }
168
+ Rewriter::WithinScope.new(self, rules, options, &block).process
167
169
  end
168
170
 
169
171
  alias with_node within_node
@@ -174,7 +176,7 @@ module Synvert::Core
174
176
  # @param rules [Hash] rules to find mathing ast nodes.
175
177
  # @param block [Block] block code to continue operating on the matching nodes.
176
178
  def within_direct_node(rules, &block)
177
- Rewriter::WithinScope.new(self, rules, { recursive: false }, &block).process
179
+ Rewriter::WithinScope.new(self, rules, { direct: true }, &block).process
178
180
  end
179
181
 
180
182
  alias with_direct_node within_direct_node
@@ -221,7 +223,7 @@ module Synvert::Core
221
223
  #
222
224
  # @param code [String] code need to be appended.
223
225
  def append(code)
224
- @actions << Rewriter::AppendAction.new(self, code)
226
+ @actions << Rewriter::AppendAction.new(self, code).process
225
227
  end
226
228
 
227
229
  # Parse prepend dsl, it creates a [Synvert::Core::Rewriter::PrependAction] to
@@ -229,7 +231,7 @@ module Synvert::Core
229
231
  #
230
232
  # @param code [String] code need to be prepended.
231
233
  def prepend(code)
232
- @actions << Rewriter::PrependAction.new(self, code)
234
+ @actions << Rewriter::PrependAction.new(self, code).process
233
235
  end
234
236
 
235
237
  # Parse insert dsl, it creates a [Synvert::Core::Rewriter::InsertAction] to
@@ -238,7 +240,7 @@ module Synvert::Core
238
240
  # @param code [String] code need to be inserted.
239
241
  # @param at [String] insert position, beginning or end, end is the default.
240
242
  def insert(code, at: 'end')
241
- @actions << Rewriter::InsertAction.new(self, code, at: at)
243
+ @actions << Rewriter::InsertAction.new(self, code, at: at).process
242
244
  end
243
245
 
244
246
  # Parse insert_after dsl, it creates a [Synvert::Core::Rewriter::InsertAfterAction] to
@@ -246,7 +248,7 @@ module Synvert::Core
246
248
  #
247
249
  # @param code [String] code need to be inserted.
248
250
  def insert_after(node)
249
- @actions << Rewriter::InsertAfterAction.new(self, node)
251
+ @actions << Rewriter::InsertAfterAction.new(self, node).process
250
252
  end
251
253
 
252
254
  # Parse replace_with dsl, it creates a [Synvert::Core::Rewriter::ReplaceWithAction] to
@@ -254,7 +256,7 @@ module Synvert::Core
254
256
  #
255
257
  # @param code [String] code need to be replaced with.
256
258
  def replace_with(code)
257
- @actions << Rewriter::ReplaceWithAction.new(self, code)
259
+ @actions << Rewriter::ReplaceWithAction.new(self, code).process
258
260
  end
259
261
 
260
262
  # Parse replace with dsl, it creates a [Synvert::Core::Rewriter::ReplaceAction] to
@@ -263,25 +265,25 @@ module Synvert::Core
263
265
  # @param selectors [Array<Symbol>] selector names of child node.
264
266
  # @param with [String] code need to be replaced with.
265
267
  def replace(*selectors, with:)
266
- @actions << Rewriter::ReplaceAction.new(self, *selectors, with: with)
268
+ @actions << Rewriter::ReplaceAction.new(self, *selectors, with: with).process
267
269
  end
268
270
 
269
271
  # Parse replace_erb_stmt_with_expr dsl, it creates a [Synvert::Core::Rewriter::ReplaceErbStmtWithExprAction] to
270
272
  # replace erb stmt code to expr code.
271
273
  def replace_erb_stmt_with_expr
272
- @actions << Rewriter::ReplaceErbStmtWithExprAction.new(self)
274
+ @actions << Rewriter::ReplaceErbStmtWithExprAction.new(self).process
273
275
  end
274
276
 
275
277
  # Parse remove dsl, it creates a [Synvert::Core::Rewriter::RemoveAction] to remove current node.
276
278
  def remove
277
- @actions << Rewriter::RemoveAction.new(self)
279
+ @actions << Rewriter::RemoveAction.new(self).process
278
280
  end
279
281
 
280
282
  # Parse delete dsl, it creates a [Synvert::Core::Rewriter::DeleteAction] to delete child nodes.
281
283
  #
282
284
  # @param selectors [Array<Symbol>] selector names of child node.
283
285
  def delete(*selectors)
284
- @actions << Rewriter::DeleteAction.new(self, *selectors)
286
+ @actions << Rewriter::DeleteAction.new(self, *selectors).process
285
287
  end
286
288
 
287
289
  # Parse wrap with dsl, it creates a [Synvert::Core::Rewriter::WrapAction] to
@@ -290,7 +292,7 @@ module Synvert::Core
290
292
  # @param with [String] code need to be wrapped with.
291
293
  # @param indent [Integer] number of whitespaces.
292
294
  def wrap(with:, indent: nil)
293
- @actions << Rewriter::WrapAction.new(self, with: with, indent: indent)
295
+ @actions << Rewriter::WrapAction.new(self, with: with, indent: indent).process
294
296
  end
295
297
 
296
298
  # Parse warn dsl, it creates a [Synvert::Core::Rewriter::Warning] to save warning message.
@@ -21,7 +21,7 @@ module Synvert::Core
21
21
  elsif File.exist?(File.join(Configuration.path, '.rvmrc'))
22
22
  versionFile = '.rvmrc'
23
23
  end
24
- return true if !versionFile
24
+ return true unless versionFile
25
25
 
26
26
  version = File.read(File.join(Configuration.path, versionFile))
27
27
  Gem::Version.new(version) >= Gem::Version.new(@version)
@@ -9,7 +9,7 @@ module Synvert::Core
9
9
  # @param rules [Hash]
10
10
  # @param options [Hash]
11
11
  # @param block [Block]
12
- def initialize(instance, rules, options = { recursive: true }, &block)
12
+ def initialize(instance, rules, options = {}, &block)
13
13
  @instance = instance
14
14
  @rules = rules
15
15
  @options = options
@@ -23,7 +23,14 @@ module Synvert::Core
23
23
  current_node = @instance.current_node
24
24
  return unless current_node
25
25
 
26
- matching_nodes = find_matching_nodes(current_node)
26
+ matching_nodes =
27
+ if @options[:direct]
28
+ find_direct_matching_nodes(current_node)
29
+ elsif @options[:recursive]
30
+ find_recursive_matching_nodes(current_node)
31
+ else
32
+ find_matching_nodes(current_node)
33
+ end
27
34
  @instance.process_with_node current_node do
28
35
  matching_nodes.each do |matching_node|
29
36
  @instance.process_with_node matching_node do
@@ -35,36 +42,77 @@ module Synvert::Core
35
42
 
36
43
  private
37
44
 
38
- def find_matching_nodes(current_node)
45
+ # Find the matching nodes only in current or direct children.
46
+ def find_direct_matching_nodes(current_node)
39
47
  matching_nodes = []
40
- if @options[:recursive]
41
- if current_node.is_a?(Parser::AST::Node)
42
- matching_nodes << current_node if current_node.match? @rules
43
- current_node.recursive_children do |child_node|
44
- matching_nodes << child_node if child_node.match? @rules
45
- end
46
- else
47
- current_node.each do |node|
48
- matching_nodes << node if node.match? @rules
49
- node.recursive_children do |child_node|
50
- matching_nodes << child_node if child_node.match? @rules
51
- end
52
- end
53
- end
54
- elsif current_node.is_a?(Parser::AST::Node)
48
+ if current_node.is_a?(Parser::AST::Node)
55
49
  if current_node.type == :begin
56
50
  current_node.children.each do |child_node|
57
- matching_nodes << child_node if child_node.match? @rules
51
+ matching_nodes << child_node if child_node.match?(@rules)
58
52
  end
59
- elsif current_node.match? @rules
53
+ elsif current_node.match?(@rules)
60
54
  matching_nodes << current_node
61
55
  end
62
56
  else
63
57
  current_node.each do |child_node|
64
- matching_nodes << child_node if child_node.match? @rules
58
+ matching_nodes << child_node if child_node.match?(@rules)
59
+ end
60
+ end
61
+ matching_nodes
62
+ end
63
+
64
+ # Find matching nodes in all recursive children.
65
+ def find_recursive_matching_nodes(current_node)
66
+ matching_nodes = []
67
+ if current_node.is_a?(Parser::AST::Node)
68
+ matching_nodes << current_node if current_node.match?(@rules)
69
+ current_node.recursive_children do |child_node|
70
+ matching_nodes << child_node if child_node.match?(@rules)
71
+ end
72
+ else
73
+ current_node.each do |node|
74
+ matching_nodes << node if node.match?(@rules)
75
+ node.recursive_children do |child_node|
76
+ matching_nodes << child_node if child_node.match?(@rules)
77
+ end
78
+ end
79
+ end
80
+ matching_nodes
81
+ end
82
+
83
+ # Find matching nodes in recursive children but do not continue on matching nodes.
84
+ def find_matching_nodes(current_node)
85
+ matching_nodes = []
86
+ if current_node.is_a?(Parser::AST::Node)
87
+ if current_node.match?(@rules)
88
+ matching_nodes << current_node
89
+ return matching_nodes
90
+ end
91
+ current_node.recursive_children do |child_node|
92
+ stop = nil
93
+ if child_node.match?(@rules)
94
+ matching_nodes << child_node
95
+ stop = :stop
96
+ end
97
+ stop
98
+ end
99
+ else
100
+ current_node.each do |node|
101
+ if node.match?(@rules)
102
+ matching_nodes << node
103
+ next
104
+ end
105
+ node.recursive_children do |child_node|
106
+ stop = nil
107
+ if child_node.match?(@rules)
108
+ matching_nodes << child_node
109
+ stop = :stop
110
+ end
111
+ stop
112
+ end
65
113
  end
66
114
  end
67
115
  matching_nodes
68
116
  end
69
117
  end
70
- end
118
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '0.56.1'
5
+ VERSION = '0.58.2'
6
6
  end
7
7
  end
@@ -282,10 +282,25 @@ describe Parser::AST::Node do
282
282
  expect(node.left_value).to eq :@a
283
283
  end
284
284
 
285
+ it 'gets for cvasgn' do
286
+ node = parse('@@a = 1')
287
+ expect(node.left_value).to eq :@@a
288
+ end
289
+
285
290
  it 'gets for or_asgn' do
286
291
  node = parse('a ||= 1')
287
292
  expect(node.left_value).to eq :a
288
293
  end
294
+
295
+ it 'gets for and' do
296
+ node = parse('foo && bar')
297
+ expect(node.left_value).to eq parse('foo')
298
+ end
299
+
300
+ it 'gets for or' do
301
+ node = parse('foo || bar')
302
+ expect(node.left_value).to eq parse('foo')
303
+ end
289
304
  end
290
305
 
291
306
  describe '#right_value' do
@@ -309,10 +324,25 @@ describe Parser::AST::Node do
309
324
  expect(node.right_value).to eq parse('1')
310
325
  end
311
326
 
327
+ it 'gets for cvasgn' do
328
+ node = parse('@@a = 1')
329
+ expect(node.right_value).to eq parse('1')
330
+ end
331
+
312
332
  it 'gets for or_asgn' do
313
333
  node = parse('a ||= 1')
314
334
  expect(node.right_value).to eq parse('1')
315
335
  end
336
+
337
+ it 'gets for and' do
338
+ node = parse('foo && bar')
339
+ expect(node.right_value).to eq parse('bar')
340
+ end
341
+
342
+ it 'gets for or' do
343
+ node = parse('foo || bar')
344
+ expect(node.right_value).to eq parse('bar')
345
+ end
316
346
  end
317
347
 
318
348
  describe '#to_value' do
@@ -9,7 +9,7 @@ module Synvert::Core
9
9
  source = "class User\n has_many :posts\nend"
10
10
  class_node = Parser::CurrentRuby.parse(source)
11
11
  instance = double(current_node: class_node)
12
- Rewriter::AppendAction.new(instance, "def as_json\n super\nend")
12
+ Rewriter::AppendAction.new(instance, "def as_json\n super\nend").process
13
13
  end
14
14
 
15
15
  it 'gets begin_pos' do
@@ -30,7 +30,7 @@ module Synvert::Core
30
30
  source = "gem 'rails'\ngem 'mysql2'"
31
31
  begin_node = Parser::CurrentRuby.parse(source)
32
32
  instance = double(current_node: begin_node)
33
- Rewriter::AppendAction.new(instance, "gem 'twitter'")
33
+ Rewriter::AppendAction.new(instance, "gem 'twitter'").process
34
34
  end
35
35
 
36
36
  it 'gets begin_pos' do
@@ -8,7 +8,7 @@ module Synvert::Core
8
8
  source = 'arr.map {}.flatten'
9
9
  node = Parser::CurrentRuby.parse(source)
10
10
  instance = double(current_node: node, file_source: source)
11
- Rewriter::DeleteAction.new(instance, :dot, :message)
11
+ Rewriter::DeleteAction.new(instance, :dot, :message).process
12
12
  }
13
13
 
14
14
  it 'gets begin_pos' do
@@ -9,7 +9,7 @@ module Synvert::Core
9
9
  source = " User.where(username: 'Richard')"
10
10
  node = Parser::CurrentRuby.parse(source)
11
11
  instance = double(current_node: node)
12
- Rewriter::InsertAction.new(instance, '.first', at: 'end')
12
+ Rewriter::InsertAction.new(instance, '.first', at: 'end').process
13
13
  }
14
14
 
15
15
  it 'gets begin_pos' do
@@ -30,7 +30,7 @@ module Synvert::Core
30
30
  source = " open('http://test.com')"
31
31
  node = Parser::CurrentRuby.parse(source)
32
32
  instance = double(current_node: node)
33
- Rewriter::InsertAction.new(instance, 'URI.', at: 'beginning')
33
+ Rewriter::InsertAction.new(instance, 'URI.', at: 'beginning').process
34
34
  }
35
35
 
36
36
  it 'gets begin_pos' do
@@ -8,7 +8,7 @@ module Synvert::Core
8
8
  source = ' include Foo'
9
9
  node = Parser::CurrentRuby.parse(source)
10
10
  instance = double(current_node: node)
11
- Rewriter::InsertAfterAction.new(instance, 'include Bar')
11
+ Rewriter::InsertAfterAction.new(instance, 'include Bar').process
12
12
  }
13
13
 
14
14
  it 'gets begin_pos' do
@@ -9,7 +9,7 @@ module Synvert::Core
9
9
  source = "Synvert::Application.configure do\nend"
10
10
  block_node = Parser::CurrentRuby.parse(source)
11
11
  instance = double(current_node: block_node)
12
- Rewriter::PrependAction.new(instance, 'config.eager_load = true')
12
+ Rewriter::PrependAction.new(instance, 'config.eager_load = true').process
13
13
  }
14
14
 
15
15
  it 'gets begin_pos' do
@@ -30,7 +30,7 @@ module Synvert::Core
30
30
  source = "RSpec.configure do |config|\nend"
31
31
  block_node = Parser::CurrentRuby.parse(source)
32
32
  instance = double(current_node: block_node)
33
- Rewriter::PrependAction.new(instance, '{{arguments.first}}.include FactoryGirl::Syntax::Methods')
33
+ Rewriter::PrependAction.new(instance, '{{arguments.first}}.include FactoryGirl::Syntax::Methods').process
34
34
  }
35
35
 
36
36
  it 'gets begin_pos' do
@@ -51,7 +51,7 @@ module Synvert::Core
51
51
  source = "class User\n has_many :posts\nend"
52
52
  class_node = Parser::CurrentRuby.parse(source)
53
53
  instance = double(current_node: class_node)
54
- Rewriter::PrependAction.new(instance, 'include Deletable')
54
+ Rewriter::PrependAction.new(instance, 'include Deletable').process
55
55
  }
56
56
 
57
57
  it 'gets begin_pos' do
@@ -72,7 +72,7 @@ module Synvert::Core
72
72
  source = "class User < ActiveRecord::Base\n has_many :posts\nend"
73
73
  class_node = Parser::CurrentRuby.parse(source)
74
74
  instance = double(current_node: class_node)
75
- Rewriter::PrependAction.new(instance, 'include Deletable')
75
+ Rewriter::PrependAction.new(instance, 'include Deletable').process
76
76
  }
77
77
 
78
78
  it 'gets begin_pos' do
@@ -8,7 +8,7 @@ module Synvert::Core
8
8
  source = "user = User.new params[:user]\nuser.save\nrender\n"
9
9
  send_node = Parser::CurrentRuby.parse(source).children[1]
10
10
  instance = double(current_node: send_node, file_source: source)
11
- Rewriter::RemoveAction.new(instance)
11
+ Rewriter::RemoveAction.new(instance).process
12
12
  }
13
13
 
14
14
  it 'gets begin_pos' do
@@ -9,7 +9,7 @@ module Synvert::Core
9
9
  source = 'FactoryBot.create(:user)'
10
10
  node = Parser::CurrentRuby.parse(source)
11
11
  instance = double(current_node: node)
12
- Rewriter::ReplaceAction.new(instance, :receiver, :dot, :message, with: 'create')
12
+ Rewriter::ReplaceAction.new(instance, :receiver, :dot, :message, with: 'create').process
13
13
  }
14
14
 
15
15
  it 'gets begin_pos' do
@@ -9,7 +9,7 @@ module Synvert::Core
9
9
  source = 'post = FactoryGirl.create_list :post, 2'
10
10
  send_node = Parser::CurrentRuby.parse(source).children[1]
11
11
  instance = double(current_node: send_node)
12
- Rewriter::ReplaceWithAction.new(instance, 'create_list {{arguments}}')
12
+ Rewriter::ReplaceWithAction.new(instance, 'create_list {{arguments}}').process
13
13
  }
14
14
 
15
15
  it 'gets begin_pos' do
@@ -30,7 +30,7 @@ module Synvert::Core
30
30
  source = ' its(:size) { should == 1 }'
31
31
  send_node = Parser::CurrentRuby.parse(source)
32
32
  instance = double(current_node: send_node)
33
- Rewriter::ReplaceWithAction.new(instance, <<~EOS)
33
+ Rewriter::ReplaceWithAction.new(instance, <<~EOS).process
34
34
  describe '#size' do
35
35
  subject { super().size }
36
36
  it { {{body}} }
@@ -8,7 +8,7 @@ module Synvert::Core
8
8
  source = "class Bar\nend"
9
9
  node = Parser::CurrentRuby.parse(source)
10
10
  instance = double(current_node: node)
11
- Rewriter::WrapAction.new(instance, with: 'module Foo')
11
+ Rewriter::WrapAction.new(instance, with: 'module Foo').process
12
12
  }
13
13
 
14
14
  it 'gets begin_pos' do
@@ -31,13 +31,23 @@ module Synvert::Core
31
31
  instance.with_node(type: 'send', message: 'create', &block)
32
32
  end
33
33
 
34
- it 'parses within_direct_node' do
34
+ it 'parses within_node with recursive false' do
35
35
  scope = double
36
36
  block = proc {}
37
37
  expect(Rewriter::WithinScope).to receive(:new)
38
38
  .with(instance, { type: 'send', message: 'create' }, { recursive: false }, &block)
39
39
  .and_return(scope)
40
40
  expect(scope).to receive(:process)
41
+ instance.within_node({ type: 'send', message: 'create' }, { recursive: false }, &block)
42
+ end
43
+
44
+ it 'parses within_direct_node' do
45
+ scope = double
46
+ block = proc {}
47
+ expect(Rewriter::WithinScope).to receive(:new)
48
+ .with(instance, { type: 'send', message: 'create' }, { direct: true }, &block)
49
+ .and_return(scope)
50
+ expect(scope).to receive(:process)
41
51
  instance.within_direct_node(type: 'send', message: 'create', &block)
42
52
  end
43
53
 
@@ -45,7 +55,7 @@ module Synvert::Core
45
55
  scope = double
46
56
  block = proc {}
47
57
  expect(Rewriter::WithinScope).to receive(:new)
48
- .with(instance, { type: 'send', message: 'create' }, { recursive: false }, &block)
58
+ .with(instance, { type: 'send', message: 'create' }, { direct: true }, &block)
49
59
  .and_return(scope)
50
60
  expect(scope).to receive(:process)
51
61
  instance.with_direct_node(type: 'send', message: 'create', &block)
@@ -90,58 +100,78 @@ module Synvert::Core
90
100
  end
91
101
 
92
102
  it 'parses append' do
93
- expect(Rewriter::AppendAction).to receive(:new).with(instance, 'include FactoryGirl::Syntax::Methods')
103
+ action = double
104
+ expect(Rewriter::AppendAction).to receive(:new).with(instance, 'include FactoryGirl::Syntax::Methods').and_return(action)
105
+ expect(action).to receive(:process)
94
106
  instance.append 'include FactoryGirl::Syntax::Methods'
95
107
  end
96
108
 
97
109
  it 'parses prepend' do
110
+ action = double
98
111
  expect(Rewriter::PrependAction).to receive(:new).with(
99
112
  instance,
100
113
  '{{arguments.first}}.include FactoryGirl::Syntax::Methods'
101
- )
114
+ ).and_return(action)
115
+ expect(action).to receive(:process)
102
116
  instance.prepend '{{arguments.first}}.include FactoryGirl::Syntax::Methods'
103
117
  end
104
118
 
105
- it 'parses insert' do
106
- expect(Rewriter::InsertAction).to receive(:new).with(instance, '.first', at: 'end')
119
+ it 'parses insert at end' do
120
+ action = double
121
+ expect(Rewriter::InsertAction).to receive(:new).with(instance, '.first', at: 'end').and_return(action)
122
+ expect(action).to receive(:process)
107
123
  instance.insert '.first'
108
124
  end
109
125
 
110
- it 'parses insert' do
111
- expect(Rewriter::InsertAction).to receive(:new).with(instance, 'URI.', at: 'beginning')
126
+ it 'parses insert at beginning' do
127
+ action = double
128
+ expect(Rewriter::InsertAction).to receive(:new).with(instance, 'URI.', at: 'beginning').and_return(action)
129
+ expect(action).to receive(:process)
112
130
  instance.insert 'URI.', at: 'beginning'
113
131
  end
114
132
 
115
133
  it 'parses insert_after' do
134
+ action = double
116
135
  expect(Rewriter::InsertAfterAction).to receive(:new).with(
117
136
  instance,
118
137
  '{{arguments.first}}.include FactoryGirl::Syntax::Methods'
119
- )
138
+ ).and_return(action)
139
+ expect(action).to receive(:process)
120
140
  instance.insert_after '{{arguments.first}}.include FactoryGirl::Syntax::Methods'
121
141
  end
122
142
 
123
143
  it 'parses replace_with' do
124
- expect(Rewriter::ReplaceWithAction).to receive(:new).with(instance, 'create {{arguments}}')
144
+ action = double
145
+ expect(Rewriter::ReplaceWithAction).to receive(:new).with(instance, 'create {{arguments}}').and_return(action)
146
+ expect(action).to receive(:process)
125
147
  instance.replace_with 'create {{arguments}}'
126
148
  end
127
149
 
128
150
  it 'parses replace with' do
129
- expect(Rewriter::ReplaceAction).to receive(:new).with(instance, :message, with: 'test')
151
+ action = double
152
+ expect(Rewriter::ReplaceAction).to receive(:new).with(instance, :message, with: 'test').and_return(action)
153
+ expect(action).to receive(:process)
130
154
  instance.replace :message, with: 'test'
131
155
  end
132
156
 
133
157
  it 'parses remove' do
134
- expect(Rewriter::RemoveAction).to receive(:new).with(instance)
158
+ action = double
159
+ expect(Rewriter::RemoveAction).to receive(:new).with(instance).and_return(action)
160
+ expect(action).to receive(:process)
135
161
  instance.remove
136
162
  end
137
163
 
138
164
  it 'parses remove' do
139
- expect(Rewriter::DeleteAction).to receive(:new).with(instance, :dot, :message)
165
+ action = double
166
+ expect(Rewriter::DeleteAction).to receive(:new).with(instance, :dot, :message).and_return(action)
167
+ expect(action).to receive(:process)
140
168
  instance.delete :dot, :message
141
169
  end
142
170
 
143
171
  it 'parses wrap with' do
144
- expect(Rewriter::WrapAction).to receive(:new).with(instance, with: 'module Foo', indent: nil)
172
+ action = double
173
+ expect(Rewriter::WrapAction).to receive(:new).with(instance, with: 'module Foo', indent: nil).and_return(action)
174
+ expect(action).to receive(:process)
145
175
  instance.wrap with: 'module Foo'
146
176
  end
147
177
 
@@ -51,6 +51,33 @@ module Synvert::Core
51
51
  expect(type_in_scope).to eq :send
52
52
  expect(instance.current_node.type).to eq :block
53
53
  end
54
+
55
+ it 'matches multiple block nodes' do
56
+ block_nodes = []
57
+ scope = Rewriter::WithinScope.new(instance, { type: 'block' }, { recursive: true }) do
58
+ block_nodes << node
59
+ end
60
+ scope.process
61
+ expect(block_nodes.size).to eq 2
62
+ end
63
+
64
+ it 'matches only one block node if no recursive' do
65
+ block_nodes = []
66
+ scope = Rewriter::WithinScope.new(instance, { type: 'block' } , { recursive: false }) do
67
+ block_nodes << node
68
+ end
69
+ scope.process
70
+ expect(block_nodes.size).to eq 1
71
+ end
72
+
73
+ it 'matches only one direct node' do
74
+ block_nodes = []
75
+ scope = Rewriter::WithinScope.new(instance, { type: 'block' } , { direct: true }) do
76
+ block_nodes << node
77
+ end
78
+ scope.process
79
+ expect(block_nodes.size).to eq 1
80
+ end
54
81
  end
55
82
  end
56
83
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synvert-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.56.1
4
+ version: 0.58.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-18 00:00:00.000000000 Z
11
+ date: 2021-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport