synvert-core 1.4.0 → 1.6.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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +0 -1
  3. data/.gitignore +0 -5
  4. data/CHANGELOG.md +12 -0
  5. data/Gemfile +0 -3
  6. data/Gemfile.lock +101 -0
  7. data/Guardfile +0 -9
  8. data/README.md +31 -13
  9. data/Rakefile +1 -15
  10. data/lib/synvert/core/engine/erb.rb +1 -1
  11. data/lib/synvert/core/engine.rb +1 -1
  12. data/lib/synvert/core/node_ext.rb +0 -639
  13. data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +20 -17
  14. data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +1 -5
  15. data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +1 -1
  16. data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +1 -5
  17. data/lib/synvert/core/rewriter/condition.rb +5 -1
  18. data/lib/synvert/core/rewriter/instance.rb +91 -140
  19. data/lib/synvert/core/rewriter/scope/query_scope.rb +8 -6
  20. data/lib/synvert/core/rewriter/scope/within_scope.rb +4 -87
  21. data/lib/synvert/core/rewriter.rb +0 -10
  22. data/lib/synvert/core/version.rb +1 -1
  23. data/lib/synvert/core.rb +4 -6
  24. data/spec/synvert/core/engine/erb_spec.rb +3 -3
  25. data/spec/synvert/core/node_ext_spec.rb +0 -965
  26. data/spec/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action_spec.rb +21 -1
  27. data/spec/synvert/core/rewriter/instance_spec.rb +64 -131
  28. data/spec/synvert/core/rewriter/scope/goto_scope_spec.rb +1 -4
  29. data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +1 -16
  30. data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +22 -13
  31. data/synvert-core-ruby.gemspec +5 -3
  32. metadata +46 -62
  33. data/lib/synvert/core/array_ext.rb +0 -48
  34. data/lib/synvert/core/node_query/compiler/array.rb +0 -34
  35. data/lib/synvert/core/node_query/compiler/attribute.rb +0 -39
  36. data/lib/synvert/core/node_query/compiler/attribute_list.rb +0 -24
  37. data/lib/synvert/core/node_query/compiler/basic_selector.rb +0 -28
  38. data/lib/synvert/core/node_query/compiler/boolean.rb +0 -23
  39. data/lib/synvert/core/node_query/compiler/comparable.rb +0 -86
  40. data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +0 -51
  41. data/lib/synvert/core/node_query/compiler/expression.rb +0 -41
  42. data/lib/synvert/core/node_query/compiler/float.rb +0 -23
  43. data/lib/synvert/core/node_query/compiler/identifier.rb +0 -41
  44. data/lib/synvert/core/node_query/compiler/integer.rb +0 -23
  45. data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +0 -7
  46. data/lib/synvert/core/node_query/compiler/nil.rb +0 -23
  47. data/lib/synvert/core/node_query/compiler/parse_error.rb +0 -7
  48. data/lib/synvert/core/node_query/compiler/regexp.rb +0 -37
  49. data/lib/synvert/core/node_query/compiler/selector.rb +0 -113
  50. data/lib/synvert/core/node_query/compiler/string.rb +0 -23
  51. data/lib/synvert/core/node_query/compiler/symbol.rb +0 -23
  52. data/lib/synvert/core/node_query/compiler.rb +0 -25
  53. data/lib/synvert/core/node_query/lexer.rex +0 -99
  54. data/lib/synvert/core/node_query/lexer.rex.rb +0 -299
  55. data/lib/synvert/core/node_query/parser.racc.rb +0 -306
  56. data/lib/synvert/core/node_query/parser.y +0 -60
  57. data/lib/synvert/core/node_query.rb +0 -36
  58. data/lib/synvert/core/rewriter/action/append_action.rb +0 -28
  59. data/lib/synvert/core/rewriter/action/delete_action.rb +0 -34
  60. data/lib/synvert/core/rewriter/action/insert_action.rb +0 -34
  61. data/lib/synvert/core/rewriter/action/insert_after_action.rb +0 -22
  62. data/lib/synvert/core/rewriter/action/prepend_action.rb +0 -44
  63. data/lib/synvert/core/rewriter/action/remove_action.rb +0 -56
  64. data/lib/synvert/core/rewriter/action/replace_action.rb +0 -33
  65. data/lib/synvert/core/rewriter/action/replace_with_action.rb +0 -36
  66. data/lib/synvert/core/rewriter/action/wrap_action.rb +0 -37
  67. data/lib/synvert/core/rewriter/action.rb +0 -102
  68. data/spec/synvert/core/node_query/lexer_spec.rb +0 -580
  69. data/spec/synvert/core/node_query/parser_spec.rb +0 -337
  70. data/spec/synvert/core/rewriter/action/append_action_spec.rb +0 -70
  71. data/spec/synvert/core/rewriter/action/delete_action_spec.rb +0 -26
  72. data/spec/synvert/core/rewriter/action/insert_action_spec.rb +0 -70
  73. data/spec/synvert/core/rewriter/action/insert_after_action_spec.rb +0 -26
  74. data/spec/synvert/core/rewriter/action/prepend_action_spec.rb +0 -175
  75. data/spec/synvert/core/rewriter/action/remove_action_spec.rb +0 -26
  76. data/spec/synvert/core/rewriter/action/replace_action_spec.rb +0 -28
  77. data/spec/synvert/core/rewriter/action/replace_with_action_spec.rb +0 -59
  78. data/spec/synvert/core/rewriter/action/wrap_action_spec.rb +0 -31
  79. data/spec/synvert/core/rewriter/action_spec.rb +0 -14
@@ -4,37 +4,40 @@ module Synvert::Core
4
4
  # ReplaceErbStmtWithExprAction to replace erb stmt code to expr,
5
5
  # @example
6
6
  # e.g. <% form_for ... %> => <%= form_for ... %>.
7
- class Rewriter::ReplaceErbStmtWithExprAction < Rewriter::Action
7
+ class Rewriter::ReplaceErbStmtWithExprAction < NodeMutation::Action
8
8
  # Initialize a ReplaceErbStmtWithExprAction.
9
9
  #
10
- # @param instance [Synvert::Core::Rewriter::Instance]
11
- def initialize(instance)
12
- super(instance, nil)
10
+ # @param node [Synvert::Core::Rewriter::Node]
11
+ def initialize(node)
12
+ super(node, nil)
13
13
  end
14
14
 
15
- # The rewritten erb expr code.
15
+ # The new erb expr code.
16
16
  #
17
- # @return [String] rewritten code.
18
- def rewritten_code
19
- @node.loc.expression.source_buffer.source[begin_pos...end_pos]
20
- .sub(Engine::ERUBY_STMT_SPLITTER, '@output_buffer.append= ')
21
- .sub(Engine::ERUBY_STMT_SPLITTER, Engine::ERUBY_EXPR_SPLITTER)
17
+ # @return [String] new code.
18
+ def new_code
19
+ NodeMutation.adapter.file_content(@node)[@start...@end]
20
+ .sub(Engine::ERUBY_STMT_SPLITTER, '@output_buffer.append= ')
21
+ .sub(Engine::ERUBY_STMT_SPLITTER, Engine::ERUBY_EXPR_SPLITTER)
22
22
  end
23
23
 
24
24
  private
25
25
 
26
26
  # Calculate the begin the end positions.
27
27
  def calculate_position
28
- node_begin_pos = @node.loc.expression.begin_pos
29
- while @node.loc.expression.source_buffer.source[node_begin_pos -= 1] == ' '
28
+ node_start = NodeMutation.adapter.get_start(@node)
29
+ node_source = NodeMutation.adapter.get_source(@node)
30
+ file_content = NodeMutation.adapter.file_content(@node)
31
+
32
+ whitespace_index = node_start
33
+ while file_content[whitespace_index -= 1] == ' '
30
34
  end
31
- @begin_pos = node_begin_pos - Engine::ERUBY_STMT_SPLITTER.length + 1
35
+ @start = whitespace_index - Engine::ERUBY_STMT_SPLITTER.length + 1
32
36
 
33
- node_begin_pos = @node.loc.expression.begin_pos
34
- node_begin_pos += @node.loc.expression.source.index 'do'
35
- while @node.loc.expression.source_buffer.source[node_begin_pos += 1] != '@'
37
+ at_index = node_start + node_source.index('do')
38
+ while file_content[at_index += 1] != '@'
36
39
  end
37
- @end_pos = node_begin_pos
40
+ @end = at_index
38
41
  end
39
42
  end
40
43
  end
@@ -9,11 +9,7 @@ module Synvert::Core
9
9
  #
10
10
  # @return [Boolean]
11
11
  def match?
12
- match = false
13
- @instance.current_node.recursive_children do |child_node|
14
- match ||= child_node&.match?(@rules)
15
- end
16
- match
12
+ @node_query.query_nodes(target_node, including_self: false, stop_at_first_match: true).size > 0
17
13
  end
18
14
  end
19
15
  end
@@ -9,7 +9,7 @@ module Synvert::Core
9
9
  #
10
10
  # @return [Boolean]
11
11
  def match?
12
- @instance.current_node.body.size == 1 && @instance.current_node.body.first.match?(@rules)
12
+ target_node.body.size == 1 && @node_query.match_node?(target_node.body.first)
13
13
  end
14
14
  end
15
15
  end
@@ -9,11 +9,7 @@ module Synvert::Core
9
9
  #
10
10
  # return [Boolean]
11
11
  def match?
12
- match = false
13
- @instance.current_node.recursive_children do |child_node|
14
- match ||= child_node&.match?(@rules)
15
- end
16
- !match
12
+ @node_query.query_nodes(target_node, including_self: false, stop_at_first_match: true).size == 0
17
13
  end
18
14
  end
19
15
  end
@@ -10,7 +10,7 @@ module Synvert::Core
10
10
  # @yield run when condition matches
11
11
  def initialize(instance, rules, &block)
12
12
  @instance = instance
13
- @rules = rules
13
+ @node_query = NodeQuery.new(rules)
14
14
  @block = block
15
15
  end
16
16
 
@@ -27,5 +27,9 @@ module Synvert::Core
27
27
  def match?
28
28
  raise NotImplementedError, 'must be implemented by subclasses'
29
29
  end
30
+
31
+ def target_node
32
+ @instance.current_node
33
+ end
30
34
  end
31
35
  end
@@ -20,66 +20,13 @@ module Synvert::Core
20
20
  rewriter.helpers.each { |helper| singleton_class.send(:define_method, helper[:name], &helper[:block]) }
21
21
  end
22
22
 
23
- class << self
24
- # Get file source.
25
- #
26
- # @param file_path [String] file path
27
- # @return [String] file source
28
- def file_source(file_path)
29
- @file_source ||= {}
30
- @file_source[file_path] ||=
31
- begin
32
- source = File.read(file_path, encoding: 'UTF-8')
33
- source = Engine::ERB.encode(source) if /\.erb$/.match?(file_path)
34
- source
35
- end
36
- end
37
-
38
- # Get file ast.
39
- #
40
- # @param file_path [String] file path
41
- # @return [String] ast node for file
42
- def file_ast(file_path)
43
- @file_ast ||= {}
44
- @file_ast[file_path] ||=
45
- begin
46
- buffer = Parser::Source::Buffer.new file_path
47
- buffer.source = file_source(file_path)
48
-
49
- parser = Parser::CurrentRuby.new
50
- parser.reset
51
- parser.parse buffer
52
- end
53
- end
54
-
55
- # Write source to file and remove cached file source and ast.
56
- #
57
- # @param file_path [String] file path
58
- # @param source [String] file source
59
- def write_file(file_path, source)
60
- source = Engine::ERB.decode(source) if /\.erb/.match?(file_path)
61
- File.write(file_path, source.gsub(/ +\n/, "\n"))
62
- @file_source[file_path] = nil
63
- @file_ast[file_path] = nil
64
- end
65
-
66
- # Reset file source and ast.
67
- def reset
68
- @file_source = {}
69
- @file_ast = {}
70
- end
71
- end
72
-
73
23
  # @!attribute [rw] current_node
74
24
  # @return current parsing node
75
25
  # @!attribute [rw] current_file
76
26
  # @return current filename
77
- attr_accessor :current_node, :current_file
78
-
79
- # Current file source
80
- def file_source
81
- self.class.file_source(current_file)
82
- end
27
+ # @!attribute [rw] current_mutation
28
+ # @return current mutation
29
+ attr_accessor :current_node, :current_file, :current_mutation
83
30
 
84
31
  # Process the instance.
85
32
  # It finds specified files, for each file, it executes the block code, rewrites the original code,
@@ -132,11 +79,13 @@ module Synvert::Core
132
79
  # # matches FactoryBot.create(:user)
133
80
  # find_node '.send[receiver=FactoryBot][message=create][arguments.size=1]' do
134
81
  # end
135
- # @param query_string [String] query string to find matching ast nodes.
82
+ # @param nql [String] node query language to find matching ast nodes.
136
83
  # @yield run on the matching nodes.
137
84
  # @raise [Synvert::Core::NodeQuery::Compiler::ParseError] if query string is invalid.
138
- def find_node(query_string, &block)
139
- Rewriter::QueryScope.new(self, query_string, &block).process
85
+ def find_node(nql, options = {}, &block)
86
+ Rewriter::QueryScope.new(self, nql, options, &block).process
87
+ rescue NodeQueryLexer::ScanError, Racc::ParseError => e
88
+ raise NodeQuery::Compiler::ParseError, "Invalid query string: #{nql}"
140
89
  end
141
90
 
142
91
  # Parse +within_node+ dsl, it creates a {Synvert::Core::Rewriter::WithinScope} to recursively find matching ast nodes,
@@ -147,12 +96,11 @@ module Synvert::Core
147
96
  # end
148
97
  # @param rules [Hash] rules to find mathing ast nodes.
149
98
  # @param options [Hash] optional
150
- # @option stop_when_match [Boolean] set if stop when match, default is false
151
- # @option direct [Boolean] set if find direct matching ast nodes, default is false
99
+ # @option including_self [Boolean] set if query the current node, default is true
100
+ # @option stop_at_first_match [Boolean] set if stop at first match, default is false
101
+ # @option recursive [Boolean] set if recursively query child nodes, default is true
152
102
  # @yield run on the matching nodes.
153
103
  def within_node(rules, options = {}, &block)
154
- options[:stop_when_match] ||= false
155
- options[:direct] ||= false
156
104
  Rewriter::WithinScope.new(self, rules, options, &block).process
157
105
  end
158
106
 
@@ -231,7 +179,7 @@ module Synvert::Core
231
179
  # end
232
180
  # @param code [String] code need to be appended.
233
181
  def append(code)
234
- @actions << Rewriter::AppendAction.new(self, code).process
182
+ @current_mutation.append(@current_node, code)
235
183
  end
236
184
 
237
185
  # Parse +prepend+ dsl, it creates a {Synvert::Core::Rewriter::PrependAction} to
@@ -250,7 +198,7 @@ module Synvert::Core
250
198
  # end
251
199
  # @param code [String] code need to be prepended.
252
200
  def prepend(code)
253
- @actions << Rewriter::PrependAction.new(self, code).process
201
+ @current_mutation.prepend(@current_node, code)
254
202
  end
255
203
 
256
204
  # Parse +insert+ dsl, it creates a {Synvert::Core::Rewriter::InsertAction} to insert code.
@@ -265,7 +213,7 @@ module Synvert::Core
265
213
  # @param at [String] insert position, beginning or end
266
214
  # @param to [String] where to insert, if it is nil, will insert to current node.
267
215
  def insert(code, at: 'end', to: nil)
268
- @actions << Rewriter::InsertAction.new(self, code, at: at, to: to).process
216
+ @current_mutation.insert(@current_node, code, at: at, to: to)
269
217
  end
270
218
 
271
219
  # Parse +insert_after+ dsl, it creates a {Synvert::Core::Rewriter::InsertAfterAction} to
@@ -280,7 +228,22 @@ module Synvert::Core
280
228
  # end
281
229
  # @param code [String] code need to be inserted.
282
230
  def insert_after(code)
283
- @actions << Rewriter::InsertAfterAction.new(self, code).process
231
+ @current_mutation.insert_after(@current_node, code)
232
+ end
233
+
234
+ # Parse +replace_erb_stmt_with_expr+ dsl, it creates a {Synvert::Core::Rewriter::ReplaceErbStmtWithExprAction} to
235
+ # replace erb stmt code to expr code.
236
+ # @example
237
+ # # <% form_for post do |f| %>
238
+ # # <% end %>
239
+ # # =>
240
+ # # <%= form_for post do |f| %>
241
+ # # <% end %>
242
+ # with_node type: 'block', caller: { type: 'send', receiver: nil, message: 'form_for' } do
243
+ # replace_erb_stmt_with_expr
244
+ # end
245
+ def replace_erb_stmt_with_expr
246
+ @current_mutation.actions << Rewriter::ReplaceErbStmtWithExprAction.new(@current_node).process
284
247
  end
285
248
 
286
249
  # Parse +replace_with+ dsl, it creates a {Synvert::Core::Rewriter::ReplaceWithAction} to
@@ -294,7 +257,7 @@ module Synvert::Core
294
257
  # end
295
258
  # @param code [String] code need to be replaced with.
296
259
  def replace_with(code)
297
- @actions << Rewriter::ReplaceWithAction.new(self, code).process
260
+ @current_mutation.replace_with(@current_node, code)
298
261
  end
299
262
 
300
263
  # Parse +replace+ dsl, it creates a {Synvert::Core::Rewriter::ReplaceAction} to
@@ -310,22 +273,7 @@ module Synvert::Core
310
273
  # @param selectors [Array<Symbol>] selector names of child node.
311
274
  # @param with [String] code need to be replaced with.
312
275
  def replace(*selectors, with:)
313
- @actions << Rewriter::ReplaceAction.new(self, *selectors, with: with).process
314
- end
315
-
316
- # Parse +replace_erb_stmt_with_expr+ dsl, it creates a {Synvert::Core::Rewriter::ReplaceErbStmtWithExprAction} to
317
- # replace erb stmt code to expr code.
318
- # @example
319
- # # <% form_for post do |f| %>
320
- # # <% end %>
321
- # # =>
322
- # # <%= form_for post do |f| %>
323
- # # <% end %>
324
- # with_node type: 'block', caller: { type: 'send', receiver: nil, message: 'form_for' } do
325
- # replace_erb_stmt_with_expr
326
- # end
327
- def replace_erb_stmt_with_expr
328
- @actions << Rewriter::ReplaceErbStmtWithExprAction.new(self).process
276
+ @current_mutation.replace(@current_node, *selectors, with: with)
329
277
  end
330
278
 
331
279
  # Parse +remove+ dsl, it creates a {Synvert::Core::Rewriter::RemoveAction} to remove current node.
@@ -336,7 +284,7 @@ module Synvert::Core
336
284
  # @param options [Hash] options.
337
285
  # @option and_comma [Boolean] delete extra comma.
338
286
  def remove(**options)
339
- @actions << Rewriter::RemoveAction.new(self, **options).process
287
+ @current_mutation.remove(@current_node, **options)
340
288
  end
341
289
 
342
290
  # Parse +delete+ dsl, it creates a {Synvert::Core::Rewriter::DeleteAction} to delete child nodes.
@@ -351,7 +299,7 @@ module Synvert::Core
351
299
  # @param options [Hash]
352
300
  # @option and_comma [Boolean] delete extra comma.
353
301
  def delete(*selectors, **options)
354
- @actions << Rewriter::DeleteAction.new(self, *selectors, **options).process
302
+ @current_mutation.delete(@current_node, *selectors, **options)
355
303
  end
356
304
 
357
305
  # Parse +wrap+ dsl, it creates a {Synvert::Core::Rewriter::WrapAction} to
@@ -368,9 +316,8 @@ module Synvert::Core
368
316
  # wrap with: 'module Synvert'
369
317
  # end
370
318
  # @param with [String] code need to be wrapped with.
371
- # @param indent [Integer, nil] number of whitespaces.
372
- def wrap(with:, indent: nil)
373
- @actions << Rewriter::WrapAction.new(self, with: with, indent: indent).process
319
+ def wrap(with:)
320
+ @current_mutation.wrap(@current_node, with: with)
374
321
  end
375
322
 
376
323
  # Parse +warn+ dsl, it creates a {Synvert::Core::Rewriter::Warning} to save warning message.
@@ -397,65 +344,69 @@ module Synvert::Core
397
344
  #
398
345
  # @param file_path [String]
399
346
  def process_file(file_path)
400
- begin
401
- puts file_path if Configuration.show_run_process
402
- conflict_actions = []
403
- source = +self.class.file_source(file_path)
404
- ast = self.class.file_ast(file_path)
405
-
406
- @current_file = file_path
407
-
408
- process_with_node(ast) do
409
- instance_eval(&@block)
410
- rescue NoMethodError
411
- puts @current_node.debug_info
412
- raise
413
- end
414
-
415
- if @actions.length > 0
416
- @actions.sort_by! { |action| [action.begin_pos, action.end_pos] }
417
- conflict_actions = get_conflict_actions
418
- @actions.reverse_each do |action|
419
- source[action.begin_pos...action.end_pos] = action.rewritten_code
347
+ puts file_path if Configuration.show_run_process
348
+
349
+ @current_file = file_path
350
+ while true
351
+ source = read_source(file_path)
352
+ @current_mutation = NodeMutation.new(source)
353
+ begin
354
+ node = parse_code(file_path, source)
355
+
356
+ process_with_node(node) do
357
+ instance_eval(&@block)
358
+ rescue NoMethodError => e
359
+ puts [
360
+ "error: #{e.message}",
361
+ "file: #{file_path}",
362
+ "source: #{source}",
363
+ "line: #{current_node.line}"
364
+ ].join("\n")
365
+ raise
420
366
  end
421
- @actions = []
422
367
 
423
- update_file(file_path, source)
368
+ result = @current_mutation.process
369
+ if result.affected?
370
+ @rewriter.add_affected_file(file_path)
371
+ write_source(file_path, result.new_source)
372
+ end
373
+ break unless result.conflicted?
374
+ rescue Parser::SyntaxError
375
+ puts "[Warn] file #{file_path} was not parsed correctly."
376
+ # do nothing, iterate next file
424
377
  end
425
- rescue Parser::SyntaxError
426
- puts "[Warn] file #{file_path} was not parsed correctly."
427
- # do nothing, iterate next file
428
- end while !conflict_actions.empty?
378
+ end
429
379
  end
430
380
 
431
- # It changes source code from bottom to top, and it can change source code twice at the same time,
432
- # So if there is an overlap between two actions, it removes the conflict actions and operate them in the next loop.
433
- def get_conflict_actions
434
- i = @actions.length - 1
435
- j = i - 1
436
- conflict_actions = []
437
- return if i < 0
438
-
439
- begin_pos = @actions[i].begin_pos
440
- while j > -1
441
- if begin_pos < @actions[j].end_pos
442
- conflict_actions << @actions.delete_at(j)
443
- else
444
- i = j
445
- begin_pos = @actions[i].begin_pos
446
- end
447
- j -= 1
448
- end
449
- conflict_actions
381
+ # Read file source.
382
+ # @param file_path [String] file path
383
+ # @return [String] file source
384
+ def read_source(file_path)
385
+ source = File.read(file_path, encoding: 'UTF-8')
386
+ source = Engine::Erb.encode(source) if /\.erb$/.match?(file_path)
387
+ source
388
+ end
389
+
390
+ # Write file source to file.
391
+ # @param file_path [String] file path
392
+ # @param source [String] file source
393
+ def write_source(file_path, source)
394
+ source = Engine::Erb.decode(source) if /\.erb/.match?(file_path)
395
+ File.write(file_path, source.gsub(/ +\n/, "\n"))
450
396
  end
451
397
 
452
- # It updates a file with new source code.
398
+ # Parse code ast node.
453
399
  #
454
- # @param file_path [String] the file path
455
- # @param source [String] the new source code
456
- def update_file(file_path, source)
457
- self.class.write_file(file_path, source)
458
- @rewriter.add_affected_file(file_path)
400
+ # @param file_path [String] file path
401
+ # @param file_path [String] file path
402
+ # @return [Node] ast node for file
403
+ def parse_code(file_path, source)
404
+ buffer = Parser::Source::Buffer.new file_path
405
+ buffer.source = source
406
+
407
+ parser = Parser::CurrentRuby.new
408
+ parser.reset
409
+ parser.parse buffer
459
410
  end
460
411
  end
461
412
  end
@@ -6,11 +6,14 @@ module Synvert::Core
6
6
  # Initialize a QueryScope.
7
7
  #
8
8
  # @param instance [Synvert::Core::Rewriter::Instance]
9
- # @param query_string [String]
9
+ # @param nql [String]
10
+ # @param options [Hash]
10
11
  # @yield run on all matching nodes
11
- def initialize(instance, query_string, &block)
12
+ def initialize(instance, nql, options = {}, &block)
12
13
  super(instance, &block)
13
- @query_string = query_string
14
+
15
+ @options = { including_self: true, stop_at_first_match: false, recursive: true }.merge(options)
16
+ @node_query = NodeQuery.new(nql)
14
17
  end
15
18
 
16
19
  # Find out the matching nodes.
@@ -22,15 +25,14 @@ module Synvert::Core
22
25
  current_node = @instance.current_node
23
26
  return unless current_node
24
27
 
28
+ matching_nodes = @node_query.query_nodes(current_node, @options)
25
29
  @instance.process_with_node(current_node) do
26
- NodeQuery::Parser.new.parse(@query_string).query_nodes(current_node).each do |node|
30
+ matching_nodes.each do |node|
27
31
  @instance.process_with_node(node) do
28
32
  @instance.instance_eval(&@block)
29
33
  end
30
34
  end
31
35
  end
32
- rescue NodeQuery::Lexer::ScanError, Racc::ParseError => e
33
- raise NodeQuery::Compiler::ParseError, "Invalid query string: #{@query_string}"
34
36
  end
35
37
  end
36
38
  end
@@ -11,8 +11,9 @@ module Synvert::Core
11
11
  # @yield run on all matching nodes
12
12
  def initialize(instance, rules, options = {}, &block)
13
13
  super(instance, &block)
14
- @rules = rules
15
- @options = options
14
+
15
+ @options = { including_self: true, stop_at_first_match: false, recursive: true }.merge(options)
16
+ @node_query = NodeQuery.new(rules)
16
17
  end
17
18
 
18
19
  # Find out the matching nodes.
@@ -22,14 +23,7 @@ module Synvert::Core
22
23
  current_node = @instance.current_node
23
24
  return unless current_node
24
25
 
25
- matching_nodes =
26
- if @options[:direct]
27
- find_direct_matching_nodes(current_node)
28
- elsif @options[:stop_when_match]
29
- find_matching_nodes(current_node)
30
- else
31
- find_recursive_matching_nodes(current_node)
32
- end
26
+ matching_nodes = @node_query.query_nodes(current_node, @options)
33
27
  @instance.process_with_node current_node do
34
28
  matching_nodes.each do |matching_node|
35
29
  @instance.process_with_node matching_node do
@@ -38,82 +32,5 @@ module Synvert::Core
38
32
  end
39
33
  end
40
34
  end
41
-
42
- private
43
-
44
- # Find the matching nodes only in current or direct children.
45
- #
46
- # @param current_node [Parser::AST::Node]
47
- def find_direct_matching_nodes(current_node)
48
- matching_nodes = []
49
- if current_node.is_a?(Parser::AST::Node)
50
- if current_node.type == :begin
51
- current_node.children.each do |child_node|
52
- matching_nodes << child_node if child_node.match?(@rules)
53
- end
54
- elsif current_node.match?(@rules)
55
- matching_nodes << current_node
56
- end
57
- else
58
- current_node.each do |child_node|
59
- matching_nodes << child_node if child_node.match?(@rules)
60
- end
61
- end
62
- matching_nodes
63
- end
64
-
65
- # Find matching nodes in all recursive children.
66
- #
67
- # @param current_node [Parser::AST::Node]
68
- def find_recursive_matching_nodes(current_node)
69
- matching_nodes = []
70
- if current_node.is_a?(Parser::AST::Node)
71
- matching_nodes << current_node if current_node.match?(@rules)
72
- current_node.recursive_children do |child_node|
73
- matching_nodes << child_node if child_node.match?(@rules)
74
- end
75
- else
76
- current_node.each do |node|
77
- matching_nodes << node if node.match?(@rules)
78
- node.recursive_children do |child_node|
79
- matching_nodes << child_node if child_node.match?(@rules)
80
- end
81
- end
82
- end
83
- matching_nodes
84
- end
85
-
86
- # Find matching nodes in recursive children but do not continue on matching nodes.
87
- #
88
- # @param current_node [Parser::AST::Node]
89
- def find_matching_nodes(current_node)
90
- matching_nodes = []
91
- if current_node.is_a?(Parser::AST::Node)
92
- if current_node.match?(@rules)
93
- matching_nodes << current_node
94
- return matching_nodes
95
- end
96
- current_node.recursive_children do |child_node|
97
- if child_node.match?(@rules)
98
- matching_nodes << child_node
99
- next :stop
100
- end
101
- end
102
- else
103
- current_node.each do |node|
104
- if node.match?(@rules)
105
- matching_nodes << node
106
- next
107
- end
108
- node.recursive_children do |child_node|
109
- if child_node.match?(@rules)
110
- matching_nodes << child_node
111
- next :stop
112
- end
113
- end
114
- end
115
- end
116
- matching_nodes
117
- end
118
35
  end
119
36
  end
@@ -8,17 +8,7 @@ module Synvert::Core
8
8
  # One Rewriter checks if the depndency version matches, and it can contain one or many {Synvert::Core::Rewriter::Instance},
9
9
  # which define the behavior what files and what codes to detect and rewrite to what code.
10
10
  class Rewriter
11
- autoload :Action, 'synvert/core/rewriter/action'
12
- autoload :AppendAction, 'synvert/core/rewriter/action/append_action'
13
- autoload :DeleteAction, 'synvert/core/rewriter/action/delete_action'
14
- autoload :InsertAction, 'synvert/core/rewriter/action/insert_action'
15
- autoload :InsertAfterAction, 'synvert/core/rewriter/action/insert_after_action'
16
- autoload :RemoveAction, 'synvert/core/rewriter/action/remove_action'
17
- autoload :PrependAction, 'synvert/core/rewriter/action/prepend_action'
18
- autoload :ReplaceAction, 'synvert/core/rewriter/action/replace_action'
19
11
  autoload :ReplaceErbStmtWithExprAction, 'synvert/core/rewriter/action/replace_erb_stmt_with_expr_action'
20
- autoload :ReplaceWithAction, 'synvert/core/rewriter/action/replace_with_action'
21
- autoload :WrapAction, 'synvert/core/rewriter/action/wrap_action'
22
12
 
23
13
  autoload :Warning, 'synvert/core/rewriter/warning'
24
14
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '1.4.0'
5
+ VERSION = '1.6.0'
6
6
  end
7
7
  end
data/lib/synvert/core.rb CHANGED
@@ -4,14 +4,13 @@ require 'synvert/core/version'
4
4
  require 'bundler'
5
5
  require 'parser'
6
6
  require 'parser/current'
7
- require 'ast'
7
+ require 'parser_node_ext'
8
+ require_relative './core/node_ext'
8
9
  require 'active_support'
9
- require 'active_support/core_ext/object'
10
- require 'active_support/core_ext/array'
11
10
  require 'erubis'
12
11
  require 'set'
13
- require 'synvert/core/array_ext'
14
- require 'synvert/core/node_ext'
12
+ require 'node_query'
13
+ require 'node_mutation'
15
14
 
16
15
  module Synvert
17
16
  module Core
@@ -20,7 +19,6 @@ module Synvert
20
19
  autoload :Engine, 'synvert/core/engine'
21
20
  autoload :RewriterNotFound, 'synvert/core/exceptions'
22
21
  autoload :MethodNotSupported, 'synvert/core/exceptions'
23
- autoload :NodeQuery, 'synvert/core/node_query'
24
22
  end
25
23
  end
26
24