synvert-core 0.63.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.gitignore +4 -0
- data/CHANGELOG.md +9 -1
- data/Guardfile +11 -2
- data/README.md +74 -34
- data/Rakefile +15 -1
- data/lib/synvert/core/array_ext.rb +41 -0
- data/lib/synvert/core/configuration.rb +12 -0
- data/lib/synvert/core/engine/erb.rb +9 -8
- data/lib/synvert/core/exceptions.rb +0 -4
- data/lib/synvert/core/node_ext.rb +232 -128
- data/lib/synvert/core/node_query/compiler/array.rb +34 -0
- data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
- data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
- data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
- data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
- data/lib/synvert/core/node_query/compiler/float.rb +23 -0
- data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
- data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
- data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
- data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
- data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
- data/lib/synvert/core/node_query/compiler/string.rb +34 -0
- data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
- data/lib/synvert/core/node_query/compiler.rb +24 -0
- data/lib/synvert/core/node_query/lexer.rex +96 -0
- data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
- data/lib/synvert/core/node_query/parser.racc.rb +518 -0
- data/lib/synvert/core/node_query/parser.y +84 -0
- data/lib/synvert/core/node_query.rb +36 -0
- data/lib/synvert/core/rewriter/action/append_action.rb +4 -3
- data/lib/synvert/core/rewriter/action/delete_action.rb +17 -8
- data/lib/synvert/core/rewriter/action/insert_action.rb +16 -7
- data/lib/synvert/core/rewriter/action/insert_after_action.rb +3 -2
- data/lib/synvert/core/rewriter/action/prepend_action.rb +3 -2
- data/lib/synvert/core/rewriter/action/remove_action.rb +16 -10
- data/lib/synvert/core/rewriter/action/replace_action.rb +15 -5
- data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +18 -11
- data/lib/synvert/core/rewriter/action/replace_with_action.rb +6 -5
- data/lib/synvert/core/rewriter/action/wrap_action.rb +16 -7
- data/lib/synvert/core/rewriter/action.rb +22 -10
- data/lib/synvert/core/rewriter/any_value.rb +1 -0
- data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition.rb +11 -3
- data/lib/synvert/core/rewriter/gem_spec.rb +6 -3
- data/lib/synvert/core/rewriter/helper.rb +7 -4
- data/lib/synvert/core/rewriter/instance.rb +217 -104
- data/lib/synvert/core/rewriter/ruby_version.rb +4 -4
- data/lib/synvert/core/rewriter/scope/goto_scope.rb +5 -6
- data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
- data/lib/synvert/core/rewriter/scope/within_scope.rb +10 -5
- data/lib/synvert/core/rewriter/scope.rb +8 -0
- data/lib/synvert/core/rewriter/warning.rb +1 -1
- data/lib/synvert/core/rewriter.rb +91 -43
- data/lib/synvert/core/version.rb +1 -1
- data/lib/synvert/core.rb +22 -6
- data/spec/synvert/core/engine/erb_spec.rb +2 -2
- data/spec/synvert/core/node_ext_spec.rb +36 -12
- data/spec/synvert/core/node_query/lexer_spec.rb +512 -0
- data/spec/synvert/core/node_query/parser_spec.rb +270 -0
- data/spec/synvert/core/rewriter/action_spec.rb +0 -4
- data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
- data/spec/synvert/core/rewriter/gem_spec_spec.rb +1 -1
- data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
- data/spec/synvert/core/rewriter/instance_spec.rb +31 -20
- data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
- data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
- data/spec/synvert/core/rewriter_spec.rb +4 -2
- data/synvert-core-ruby.gemspec +7 -2
- metadata +91 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f889cb977b7bf6e847bdeeac011ee0d23210ae8db7a0652e8c00d5d98c72a701
|
4
|
+
data.tar.gz: 46d220401cc8c9c0422428c2915862148b4ad8dc236f637ed52dbc45207de742
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 926df3dd8aa3689938e90c530fb432e0703cff2f89f35f46950b3630eb98b8da6de0e0d5eb75c5c24262f3712400d4570ac05abd5ebbbe6c6954d6c997048432
|
7
|
+
data.tar.gz: 321fb626763ca31e6156c462bb6846ce73d00639a620b0b7048b024fbe50d14f63f6d3209e3a534b0475c29c29e74dcf0a7c204f14ad0927244bd5cbb157b3d6
|
data/.github/workflows/main.yml
CHANGED
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
-
## 0.
|
3
|
+
## 1.0.0 (2022-04-25)
|
4
|
+
|
5
|
+
* Introduce new node query language
|
6
|
+
* Drop ruby 2.5 support
|
7
|
+
|
8
|
+
## 0.64.0 (2022-04-02)
|
4
9
|
|
5
10
|
* Read absolute path of Gemfile.lock
|
11
|
+
* Remove unused `Node#to_s`
|
12
|
+
* Yardoc comments
|
13
|
+
* Drop `within_direct_node(rules)`, use `within_node(rules, { direct: true })` instead
|
6
14
|
|
7
15
|
## 0.63.0 (2022-02-26)
|
8
16
|
|
data/Guardfile
CHANGED
@@ -2,6 +2,15 @@
|
|
2
2
|
|
3
3
|
guard :rspec, cmd: 'bundle exec rspec' do
|
4
4
|
watch(%r{^spec/.+_spec\.rb$})
|
5
|
-
watch(%r{^lib/(.+)\.rb$})
|
6
|
-
watch('
|
5
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
6
|
+
watch('lib/synvert/core/node_query/lexer.rex.rb') { 'spec/synvert/core/node_query/lexer_spec.rb' }
|
7
|
+
watch('lib/synvert/core/node_query/compiler.rb') { 'spec/synvert/core/node_query/parser_spec.rb' }
|
8
|
+
watch(%r{^lib/synvert/core/node_query/compiler/.*\.rb$}) { 'spec/synvert/core/node_query/parser_spec.rb' }
|
9
|
+
watch('lib/synvert/core/node_query/parser.racc.rb') { 'spec/synvert/core/node_query/parser_spec.rb' }
|
10
|
+
watch('spec/spec_helper.rb') { "spec" }
|
11
|
+
end
|
12
|
+
|
13
|
+
guard :rake, task: 'generate' do
|
14
|
+
watch('lib/synvert/core/node_query/lexer.rex')
|
15
|
+
watch('lib/synvert/core/node_query/parser.y')
|
7
16
|
end
|
data/README.md
CHANGED
@@ -2,39 +2,79 @@
|
|
2
2
|
|
3
3
|
<img src="https://synvert.xinminlabs.com/img/logo_96.png" alt="logo" width="32" height="32" />
|
4
4
|
|
5
|
+
[![AwesomeCode Status for xinminlabs/synvert-core-ruby](https://awesomecode.io/projects/033f7f02-7b22-41c3-a902-fca37f1ec72a/status)](https://awesomecode.io/repos/xinminlabs/synvert-core-ruby)
|
5
6
|
![Main workflow](https://github.com/xinminlabs/synvert-core-ruby/actions/workflows/main.yml/badge.svg)
|
6
|
-
[![Gem Version](https://badge.fury.io/rb/synvert-core.png)](http://badge.fury.io/rb/synvert-core)
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
[
|
40
|
-
|
8
|
+
Synvert core provides a set of DSLs to rewrite ruby code. e.g.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
Synvert::Rewriter.new 'ruby', 'map_and_flatten_to_flat_map' do
|
12
|
+
description <<~EOS
|
13
|
+
It converts `map` and `flatten` to `flat_map`
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
enum.map do
|
17
|
+
# do something
|
18
|
+
end.flatten
|
19
|
+
```
|
20
|
+
|
21
|
+
=>
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
enum.flat_map do
|
25
|
+
# do something
|
26
|
+
end
|
27
|
+
```
|
28
|
+
EOS
|
29
|
+
|
30
|
+
within_files Synvert::ALL_RUBY_FILES do
|
31
|
+
with_node type: 'send', receiver: { type: 'block', caller: { type: 'send', message: 'map' } }, message: 'flatten', arguments: { size: 0 } do
|
32
|
+
delete :message, :dot
|
33
|
+
replace 'receiver.caller.message', with: 'flat_map'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
Want to see more examples, check out [synvert-snippets-ruby](https://github.com/xinminlabs/synvert-snippets-ruby).
|
40
|
+
|
41
|
+
Want to use the CLI, check out [synvert-ruby](https://github.com/xinminlabs/synvert-ruby).
|
42
|
+
|
43
|
+
DSLs are as follows
|
44
|
+
|
45
|
+
* [description](./Synvert/Core/Rewriter.html#description-instance_method) - set description of the rewriter
|
46
|
+
* [if_ruby](./Synvert/Core/Rewriter.html#if_ruby-instance_method) - check if ruby version is greater than or equal to the specified ruby version
|
47
|
+
* [if_gem](./Synvert/Core/Rewriter.html#if_gem-instance_method) - compare version of specified gem
|
48
|
+
* [within_files](./Synvert/Core/Rewriter.html#within_files-instance_method) - find specified files
|
49
|
+
* [within_file](./Synvert/Core/Rewriter.html#within_file-instance_method) - alias to within_files
|
50
|
+
* [add_file](./Synvert/Core/Rewriter.html#add_file-instance_method) - add a new file
|
51
|
+
* [remove_file](./Synvert/Core/Rewriter.html#remove_file-instance_method) - remove a file
|
52
|
+
* [helper_method](./Synvert/Core/Rewriter.html#helper_method-instance_method) - define a helper method
|
53
|
+
* [add_snippet](./Synvert/Core/Rewriter.html#add_snippet-instance_method) - call another rewriter
|
54
|
+
* [todo](./Synvert/Core/Rewriter.html#todo-instance_method) - set todo
|
55
|
+
* [redo_until_no_change](./Synvert/Core/Rewriter.html#redo_until_no_change-instance_method) - run the snippet until no change
|
56
|
+
|
57
|
+
Scopes:
|
58
|
+
|
59
|
+
* [within_node](./Synvert/Core/Rewriter/Instance.html#within_node-instance_method) - recursively find matching ast nodes
|
60
|
+
* [with_node](./Synvert/Core/Rewriter/Instance.html#with_node-instance_method) - alias to within_node
|
61
|
+
* [goto_node](./Synvert/Core/Rewriter/Instance.html#goto_node-instance_method) - go to a child node
|
62
|
+
|
63
|
+
Conditions:
|
64
|
+
|
65
|
+
* [if_exist_node](./Synvert/Core/Rewriter/Instance.html#if_exist_node-instance_method) - check if matching node exist in the child nodes
|
66
|
+
* [unless_exist_node](./Synvert/Core/Rewriter/Instance.html#unless_exist_node-instance_method) - check if matching node doesn't exist in the child nodes
|
67
|
+
* [if_only_exist_node](./Synvert/Core/Rewriter/Instance.html#if_only_exist_node-instance_method) - check if current node has only one child node and the child node matches rules
|
68
|
+
|
69
|
+
Actions:
|
70
|
+
|
71
|
+
* [append](./Synvert/Core/Rewriter/Instance.html#append-instance_method) - append the code to the bottom of current node body
|
72
|
+
* [prepend](./Synvert/Core/Rewriter/Instance.html#prepend-instance_method) - prepend the code to the bottom of current node body
|
73
|
+
* [insert](./Synvert/Core/Rewriter/Instance.html#insert-instance_method) - insert code
|
74
|
+
* [insert_after](./Synvert/Core/Rewriter/Instance.html#insert_after-instance_method) - insert the code next to the current node
|
75
|
+
* [replace](./Synvert/Core/Rewriter/Instance.html#replace-instance_method) - replace the code of specified child nodes
|
76
|
+
* [delete](./Synvert/Core/Rewriter/Instance.html#delete-instance_method) - delete the code specified child nodes
|
77
|
+
* [wrap](./Synvert/Core/Rewriter/Instance.html#wrap-instance_method) - wrap the current node with code
|
78
|
+
* [replace_with](./Synvert/Core/Rewriter/Instance.html#replace_with-instance_method) - replace the whole code of current node
|
79
|
+
* [warn](./Synvert/Core/Rewriter/Instance.html#warn-instance_method) - warn message
|
80
|
+
* [replace_erb_stmt_with_expr](./Synvert/Core/Rewriter/Instance.html#replace_erb_stmt_with_expr-instance_method) - replace erb stmt code to expr code
|
data/Rakefile
CHANGED
@@ -2,7 +2,21 @@
|
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require 'rspec/core/rake_task'
|
5
|
-
|
6
5
|
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
require 'oedipus_lex'
|
7
|
+
Rake.application.rake_require "oedipus_lex"
|
8
|
+
|
9
|
+
file "lib/synvert/core/node_query/lexer.rex.rb" => "lib/synvert/core/node_query/lexer.rex"
|
10
|
+
file "lib/synvert/core/node_query/parser.racc.rb" => "lib/synvert/core/node_query/parser.y"
|
11
|
+
|
12
|
+
task :lexer => "lib/synvert/core/node_query/lexer.rex.rb"
|
13
|
+
task :parser => "lib/synvert/core/node_query/parser.racc.rb"
|
14
|
+
task :generate => [:lexer, :parser]
|
15
|
+
|
16
|
+
rule '.racc.rb' => '.y' do |t|
|
17
|
+
cmd = "bundle exec racc -l -v -o #{t.name} #{t.source}"
|
18
|
+
sh cmd
|
19
|
+
end
|
7
20
|
|
8
21
|
task :default => :spec
|
22
|
+
task :spec => :generate
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Extend Array.
|
4
|
+
class Array
|
5
|
+
# Get child node by the name.
|
6
|
+
#
|
7
|
+
# @param child_name [String] name of child node.
|
8
|
+
# @return [Parser::AST::Node] the child node.
|
9
|
+
def child_node_by_name(child_name)
|
10
|
+
direct_child_name, nested_child_name = child_name.split('.', 2)
|
11
|
+
child_direct_child_node = direct_child_name =~ /\A\d+\z/ ? self[direct_child_name.to_i - 1] : self.send(direct_child_name)
|
12
|
+
return child_direct_child_node.child_node_by_name(nested_child_name) if nested_child_name
|
13
|
+
return child_direct_child_node if child_direct_child_node
|
14
|
+
|
15
|
+
raise Synvert::Core::MethodNotSupported,
|
16
|
+
"child_node_by_name is not handled for #{map(&:debug_info).join("\n")}, child_name: #{child_name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get the source range of child node.
|
20
|
+
#
|
21
|
+
# @param child_name [String] name of child node.
|
22
|
+
# @return [Parser::Source::Range] source range of child node.
|
23
|
+
def child_node_range(child_name)
|
24
|
+
direct_child_name, nested_child_name = child_name.split('.', 2)
|
25
|
+
child_direct_child_node = direct_child_name =~ /\A\d+\z/ ? self[direct_child_name.to_i - 1] : self.send(direct_child_name)
|
26
|
+
if nested_child_name
|
27
|
+
return child_direct_child_node.child_node_range(nested_child_name)
|
28
|
+
elsif child_direct_child_node
|
29
|
+
return (
|
30
|
+
Parser::Source::Range.new(
|
31
|
+
'(string)',
|
32
|
+
child_direct_child_node.loc.expression.begin_pos,
|
33
|
+
child_direct_child_node.loc.expression.end_pos
|
34
|
+
)
|
35
|
+
)
|
36
|
+
else
|
37
|
+
raise Synvert::Core::MethodNotSupported,
|
38
|
+
"child_node_range is not handled for #{map(&:debug_info).join("\n")}, child_name: #{child_name}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -4,16 +4,28 @@ module Synvert::Core
|
|
4
4
|
# Synvert global configuration.
|
5
5
|
class Configuration
|
6
6
|
class << self
|
7
|
+
# @!attribute [w] path
|
8
|
+
# @!attribute [w] skip_files
|
9
|
+
# @!attribute [w] show_run_process
|
7
10
|
attr_writer :path, :skip_files, :show_run_process
|
8
11
|
|
12
|
+
# Get the path.
|
13
|
+
#
|
14
|
+
# @return [String] default is '.'
|
9
15
|
def path
|
10
16
|
@path || '.'
|
11
17
|
end
|
12
18
|
|
19
|
+
# Get a list of skip files.
|
20
|
+
#
|
21
|
+
# @return [Array<String>] default is [].
|
13
22
|
def skip_files
|
14
23
|
@skip_files || []
|
15
24
|
end
|
16
25
|
|
26
|
+
# Check if show run process.
|
27
|
+
#
|
28
|
+
# @return [Boolean] default is false
|
17
29
|
def show_run_process
|
18
30
|
@show_run_process || false
|
19
31
|
end
|
@@ -43,11 +43,13 @@ module Synvert::Core
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def decode_html_output(source)
|
46
|
-
source.gsub(/@output_buffer.safe_append='(.+?)'.freeze;/m) { reverse_escape_text(Regexp.last_match(1)) }
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
source.gsub(/@output_buffer.safe_append='(.+?)'.freeze;/m) { reverse_escape_text(Regexp.last_match(1)) }
|
47
|
+
.gsub(
|
48
|
+
/@output_buffer.safe_append=\((.+?)\);#{ERUBY_EXPR_SPLITTER}/mo
|
49
|
+
) { reverse_escape_text(Regexp.last_match(1)) }
|
50
|
+
.gsub(
|
51
|
+
/@output_buffer.safe_append=(.+?)\s+(do|\{)(\s*\|[^|]*\|)?\s*#{ERUBY_EXPR_SPLITTER}/mo
|
52
|
+
) { reverse_escape_text(Regexp.last_match(1)) }
|
51
53
|
end
|
52
54
|
|
53
55
|
def remove_erubis_buf(source)
|
@@ -64,6 +66,7 @@ module Synvert::Core
|
|
64
66
|
|
65
67
|
# borrowed from rails
|
66
68
|
class Erubis < ::Erubis::Eruby
|
69
|
+
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
67
70
|
def add_preamble(src)
|
68
71
|
@newline_pending = 0
|
69
72
|
src << '@output_buffer = output_buffer || ActionView::OutputBuffer.new;'
|
@@ -76,7 +79,7 @@ module Synvert::Core
|
|
76
79
|
@newline_pending += 1
|
77
80
|
else
|
78
81
|
src << "@output_buffer.safe_append='"
|
79
|
-
src << "\n" * @newline_pending if @newline_pending > 0
|
82
|
+
src << ("\n" * @newline_pending) if @newline_pending > 0
|
80
83
|
src << escape_text(text)
|
81
84
|
src << "'.freeze;"
|
82
85
|
|
@@ -95,8 +98,6 @@ module Synvert::Core
|
|
95
98
|
end
|
96
99
|
end
|
97
100
|
|
98
|
-
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
99
|
-
|
100
101
|
def add_expr_literal(src, code)
|
101
102
|
flush_newline_if_pending(src)
|
102
103
|
if BLOCK_EXPR.match?(code)
|