synvert-core 0.62.1 → 0.64.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 +4 -4
- data/CHANGELOG.md +9 -1
- data/Gemfile +0 -2
- data/README.md +73 -33
- data/lib/synvert/core/configuration.rb +12 -0
- data/lib/synvert/core/exceptions.rb +0 -4
- data/lib/synvert/core/node_ext.rb +206 -103
- data/lib/synvert/core/rewriter/action/append_action.rb +4 -3
- data/lib/synvert/core/rewriter/action/delete_action.rb +13 -6
- 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 +13 -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 +13 -5
- data/lib/synvert/core/rewriter/action.rb +20 -9
- 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 +7 -4
- data/lib/synvert/core/rewriter/helper.rb +2 -2
- data/lib/synvert/core/rewriter/instance.rb +195 -94
- 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/within_scope.rb +9 -4
- data/lib/synvert/core/rewriter/scope.rb +8 -0
- data/lib/synvert/core/rewriter/warning.rb +1 -1
- data/lib/synvert/core/rewriter.rb +90 -43
- data/lib/synvert/core/version.rb +1 -1
- data/lib/synvert/core.rb +0 -1
- data/spec/spec_helper.rb +0 -3
- data/spec/synvert/core/node_ext_spec.rb +28 -7
- data/spec/synvert/core/rewriter/action_spec.rb +0 -4
- data/spec/synvert/core/rewriter/gem_spec_spec.rb +11 -10
- data/spec/synvert/core/rewriter/instance_spec.rb +7 -17
- data/synvert-core-ruby.gemspec +2 -1
- metadata +21 -7
@@ -3,13 +3,14 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# InsertAfterAction to insert code next to the node.
|
5
5
|
class Rewriter::InsertAfterAction < Rewriter::Action
|
6
|
+
private
|
7
|
+
|
8
|
+
# Calculate the begin and end positions.
|
6
9
|
def calculate_position
|
7
10
|
@begin_pos = @node.loc.expression.end_pos
|
8
11
|
@end_pos = @begin_pos
|
9
12
|
end
|
10
13
|
|
11
|
-
private
|
12
|
-
|
13
14
|
# Indent of the node.
|
14
15
|
#
|
15
16
|
# @param node [Parser::AST::Node]
|
@@ -3,8 +3,11 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# PrependAction to prepend code to the top of node body.
|
5
5
|
class Rewriter::PrependAction < Rewriter::Action
|
6
|
+
private
|
7
|
+
|
6
8
|
DO_LENGTH = ' do'.length
|
7
9
|
|
10
|
+
# Calculate the begin and end positions.
|
8
11
|
def calculate_position
|
9
12
|
@begin_pos =
|
10
13
|
case @node.type
|
@@ -26,8 +29,6 @@ module Synvert::Core
|
|
26
29
|
@end_pos = @begin_pos
|
27
30
|
end
|
28
31
|
|
29
|
-
private
|
30
|
-
|
31
32
|
# Indent of the node.
|
32
33
|
#
|
33
34
|
# @param node [Parser::AST::Node]
|
@@ -3,13 +3,21 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# RemoveAction to remove current node.
|
5
5
|
class Rewriter::RemoveAction < Rewriter::Action
|
6
|
+
# Initialize a RemoveAction.
|
7
|
+
#
|
8
|
+
# @param instance [Synvert::Core::Rewriter::RemoveAction]
|
6
9
|
def initialize(instance)
|
7
10
|
super(instance, nil)
|
8
11
|
end
|
9
12
|
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
+
# The rewritten code, always empty string.
|
14
|
+
def rewritten_code
|
15
|
+
''
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Calculate the begin the end positions.
|
13
21
|
def calculate_position
|
14
22
|
if take_whole_line?
|
15
23
|
@begin_pos = start_index
|
@@ -23,22 +31,20 @@ module Synvert::Core
|
|
23
31
|
end
|
24
32
|
end
|
25
33
|
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
34
|
+
# Check if the source code of current node takes the whole line.
|
35
|
+
#
|
36
|
+
# @return [Boolean]
|
33
37
|
def take_whole_line?
|
34
38
|
@node.to_source == file_source[start_index...end_index].strip
|
35
39
|
end
|
36
40
|
|
41
|
+
# Get the start position of the line
|
37
42
|
def start_index
|
38
43
|
index = file_source[0..@node.loc.expression.begin_pos].rindex("\n")
|
39
44
|
index ? index + "\n".length : @node.loc.expression.begin_pos
|
40
45
|
end
|
41
46
|
|
47
|
+
# Get the end position of the line
|
42
48
|
def end_index
|
43
49
|
index = file_source[@node.loc.expression.end_pos..-1].index("\n")
|
44
50
|
index ? @node.loc.expression.end_pos + index + "\n".length : @node.loc.expression.end_pos
|
@@ -3,21 +3,29 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# ReplaceAction to replace child node with code.
|
5
5
|
class Rewriter::ReplaceAction < Rewriter::Action
|
6
|
+
# Initailize a ReplaceAction.
|
7
|
+
#
|
8
|
+
# @param instance [Synvert::Core::Rewriter::Instance]
|
9
|
+
# @param selectors [Array<Symbol|String>] used to select child nodes
|
10
|
+
# @param with [String] the new code
|
6
11
|
def initialize(instance, *selectors, with:)
|
7
12
|
super(instance, with)
|
8
13
|
@selectors = selectors
|
9
14
|
end
|
10
15
|
|
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
|
14
|
-
end
|
15
|
-
|
16
16
|
# The rewritten source code.
|
17
17
|
#
|
18
18
|
# @return [String] rewritten code.
|
19
19
|
def rewritten_code
|
20
20
|
rewritten_source
|
21
21
|
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Calculate the begin the end positions.
|
26
|
+
def calculate_position
|
27
|
+
@begin_pos = @selectors.map { |selector| @node.child_node_range(selector).begin_pos }.min
|
28
|
+
@end_pos = @selectors.map { |selector| @node.child_node_range(selector).end_pos }.max
|
29
|
+
end
|
22
30
|
end
|
23
31
|
end
|
@@ -2,12 +2,28 @@
|
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# ReplaceErbStmtWithExprAction to replace erb stmt code to expr,
|
5
|
+
# @example
|
5
6
|
# e.g. <% form_for ... %> => <%= form_for ... %>.
|
6
7
|
class Rewriter::ReplaceErbStmtWithExprAction < Rewriter::Action
|
7
|
-
|
8
|
-
|
8
|
+
# Initialize a ReplaceErbStmtWithExprAction.
|
9
|
+
#
|
10
|
+
# @param instance [Synvert::Core::Rewriter::Instance]
|
11
|
+
def initialize(instance)
|
12
|
+
super(instance, nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
# The rewritten erb expr code.
|
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)
|
9
22
|
end
|
10
23
|
|
24
|
+
private
|
25
|
+
|
26
|
+
# Calculate the begin the end positions.
|
11
27
|
def calculate_position
|
12
28
|
node_begin_pos = @node.loc.expression.begin_pos
|
13
29
|
while @node.loc.expression.source_buffer.source[node_begin_pos -= 1] == ' '
|
@@ -20,14 +36,5 @@ module Synvert::Core
|
|
20
36
|
end
|
21
37
|
@end_pos = node_begin_pos
|
22
38
|
end
|
23
|
-
|
24
|
-
# The rewritten erb expr code.
|
25
|
-
#
|
26
|
-
# @return [String] rewritten code.
|
27
|
-
def rewritten_code
|
28
|
-
@node.loc.expression.source_buffer.source[begin_pos...end_pos]
|
29
|
-
.sub(Engine::ERUBY_STMT_SPLITTER, '@output_buffer.append= ')
|
30
|
-
.sub(Engine::ERUBY_STMT_SPLITTER, Engine::ERUBY_EXPR_SPLITTER)
|
31
|
-
end
|
32
39
|
end
|
33
40
|
end
|
@@ -3,11 +3,6 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# ReplaceWithAction to replace code.
|
5
5
|
class Rewriter::ReplaceWithAction < Rewriter::Action
|
6
|
-
def calculate_position
|
7
|
-
@begin_pos = @node.loc.expression.begin_pos
|
8
|
-
@end_pos = @node.loc.expression.end_pos
|
9
|
-
end
|
10
|
-
|
11
6
|
# The rewritten source code with proper indent.
|
12
7
|
#
|
13
8
|
# @return [String] rewritten code.
|
@@ -25,6 +20,12 @@ module Synvert::Core
|
|
25
20
|
|
26
21
|
private
|
27
22
|
|
23
|
+
# Calculate the begin the end positions.
|
24
|
+
def calculate_position
|
25
|
+
@begin_pos = @node.loc.expression.begin_pos
|
26
|
+
@end_pos = @node.loc.expression.end_pos
|
27
|
+
end
|
28
|
+
|
28
29
|
# Indent of the node
|
29
30
|
#
|
30
31
|
# @return [String] n times whitesphace
|
@@ -6,16 +6,16 @@ module Synvert::Core
|
|
6
6
|
# Note: if WrapAction is conflicted with another action (begin_pos and end_pos are overlapped),
|
7
7
|
# we have to put those 2 actions into 2 within_file scopes.
|
8
8
|
class Rewriter::WrapAction < Rewriter::Action
|
9
|
+
# Initialize a WrapAction.
|
10
|
+
#
|
11
|
+
# @param instance [Synvert::Core::Rewriter::WrapAction]
|
12
|
+
# @param with [String] new code to wrap
|
13
|
+
# @param indent [Integer, nil] number of whitespaces
|
9
14
|
def initialize(instance, with:, indent: nil)
|
10
15
|
super(instance, with)
|
11
16
|
@indent = indent || @node.column
|
12
17
|
end
|
13
18
|
|
14
|
-
def calculate_position
|
15
|
-
@begin_pos = @node.loc.expression.begin_pos
|
16
|
-
@end_pos = @node.loc.expression.end_pos
|
17
|
-
end
|
18
|
-
|
19
19
|
# The rewritten source code.
|
20
20
|
#
|
21
21
|
# @return [String] rewritten code.
|
@@ -24,5 +24,13 @@ module Synvert::Core
|
|
24
24
|
@node.to_source.split("\n").map { |line| " #{line}" }.join("\n") +
|
25
25
|
"\n#{' ' * @indent}end"
|
26
26
|
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Calculate the begin the end positions.
|
31
|
+
def calculate_position
|
32
|
+
@begin_pos = @node.loc.expression.begin_pos
|
33
|
+
@end_pos = @node.loc.expression.end_pos
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
|
-
# Action defines rewriter action,
|
4
|
+
# Action defines rewriter action, insert, replace or delete code.
|
5
5
|
class Rewriter::Action
|
6
6
|
DEFAULT_INDENT = 2
|
7
7
|
|
@@ -14,25 +14,21 @@ module Synvert::Core
|
|
14
14
|
# Initialize an action.
|
15
15
|
#
|
16
16
|
# @param instance [Synvert::Core::Rewriter::Instance]
|
17
|
-
# @param code [String] new code to
|
17
|
+
# @param code [String] new code to insert, replace or delete.
|
18
18
|
def initialize(instance, code)
|
19
19
|
@instance = instance
|
20
20
|
@code = code
|
21
21
|
@node = @instance.current_node
|
22
22
|
end
|
23
23
|
|
24
|
+
# Calculate begin and end positions, and return self.
|
25
|
+
#
|
26
|
+
# @return [Synvert::Core::Rewriter::Action] self
|
24
27
|
def process
|
25
28
|
calculate_position
|
26
29
|
self
|
27
30
|
end
|
28
31
|
|
29
|
-
# Line number of current node.
|
30
|
-
#
|
31
|
-
# @return [Integer] line number.
|
32
|
-
def line
|
33
|
-
@node.loc.expression.line
|
34
|
-
end
|
35
|
-
|
36
32
|
# The rewritten source code with proper indent.
|
37
33
|
#
|
38
34
|
# @return [String] rewritten code.
|
@@ -46,6 +42,13 @@ module Synvert::Core
|
|
46
42
|
|
47
43
|
protected
|
48
44
|
|
45
|
+
# Calculate the begin the end positions.
|
46
|
+
#
|
47
|
+
# @abstract
|
48
|
+
def calculate_position
|
49
|
+
raise NotImplementedError, 'must be implemented by subclasses'
|
50
|
+
end
|
51
|
+
|
49
52
|
# The rewritten source code.
|
50
53
|
#
|
51
54
|
# @return [String] rewritten source code.
|
@@ -53,12 +56,14 @@ module Synvert::Core
|
|
53
56
|
@rewritten_source ||= @node.rewritten_source(@code)
|
54
57
|
end
|
55
58
|
|
59
|
+
# Squeeze spaces from source code.
|
56
60
|
def squeeze_spaces
|
57
61
|
if file_source[@begin_pos - 1] == ' ' && file_source[@end_pos] == ' '
|
58
62
|
@begin_pos -= 1
|
59
63
|
end
|
60
64
|
end
|
61
65
|
|
66
|
+
# Squeeze empty lines from source code.
|
62
67
|
def squeeze_lines
|
63
68
|
lines = file_source.split("\n")
|
64
69
|
begin_line = @node.loc.expression.first_line
|
@@ -71,6 +76,9 @@ module Synvert::Core
|
|
71
76
|
end
|
72
77
|
end
|
73
78
|
|
79
|
+
# Remove unused comma.
|
80
|
+
# e.g. `foobar(foo, bar)`, if we remove `foo`, the comma should also be removed,
|
81
|
+
# the code should be changed to `foobar(bar)`.
|
74
82
|
def remove_comma
|
75
83
|
if ',' == file_source[@begin_pos - 1]
|
76
84
|
@begin_pos -= 1
|
@@ -83,6 +91,9 @@ module Synvert::Core
|
|
83
91
|
end
|
84
92
|
end
|
85
93
|
|
94
|
+
# Return file source.
|
95
|
+
#
|
96
|
+
# @return [String]
|
86
97
|
def file_source
|
87
98
|
@file_source ||= @instance.file_source
|
88
99
|
end
|
@@ -3,7 +3,11 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# IfExistCondition checks if matching node exists in the node children.
|
5
5
|
class Rewriter::IfExistCondition < Rewriter::Condition
|
6
|
+
private
|
7
|
+
|
6
8
|
# check if any child node matches the rules.
|
9
|
+
#
|
10
|
+
# @return [Boolean]
|
7
11
|
def match?
|
8
12
|
match = false
|
9
13
|
@instance.current_node.recursive_children do |child_node|
|
@@ -3,7 +3,11 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# IfOnlyExistCondition checks if node has only one child node and the child node matches rules.
|
5
5
|
class Rewriter::IfOnlyExistCondition < Rewriter::Condition
|
6
|
+
private
|
7
|
+
|
6
8
|
# check if only have one child node and the child node matches rules.
|
9
|
+
#
|
10
|
+
# @return [Boolean]
|
7
11
|
def match?
|
8
12
|
@instance.current_node.body.size == 1 && @instance.current_node.body.first.match?(@rules)
|
9
13
|
end
|
@@ -3,7 +3,11 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# UnlessExistCondition checks if matching node doesn't exist in the node children.
|
5
5
|
class Rewriter::UnlessExistCondition < Rewriter::Condition
|
6
|
+
private
|
7
|
+
|
6
8
|
# check if none of child node matches the rules.
|
9
|
+
#
|
10
|
+
# return [Boolean]
|
7
11
|
def match?
|
8
12
|
match = false
|
9
13
|
@instance.current_node.recursive_children do |child_node|
|
@@ -3,12 +3,11 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# Condition checks if rules matches.
|
5
5
|
class Rewriter::Condition
|
6
|
-
# Initialize a
|
6
|
+
# Initialize a Condition.
|
7
7
|
#
|
8
8
|
# @param instance [Synvert::Core::Rewriter::Instance]
|
9
9
|
# @param rules [Hash]
|
10
|
-
# @
|
11
|
-
# @return [Synvert::Core::Rewriter::Condition]
|
10
|
+
# @yield run when condition matches
|
12
11
|
def initialize(instance, rules, &block)
|
13
12
|
@instance = instance
|
14
13
|
@rules = rules
|
@@ -19,5 +18,14 @@ module Synvert::Core
|
|
19
18
|
def process
|
20
19
|
@instance.instance_eval(&@block) if match?
|
21
20
|
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
# Check if condition matches
|
25
|
+
#
|
26
|
+
# @abstract
|
27
|
+
def match?
|
28
|
+
raise NotImplementedError, 'must be implemented by subclasses'
|
29
|
+
end
|
22
30
|
end
|
23
31
|
end
|
@@ -3,12 +3,16 @@
|
|
3
3
|
module Synvert::Core
|
4
4
|
# GemSpec checks and compares gem version.
|
5
5
|
class Rewriter::GemSpec
|
6
|
+
# @!attribute [r] name
|
7
|
+
# @return [String] the name of gem_spec
|
8
|
+
# @!attribute [r] version
|
9
|
+
# @return [String] the version of gem_spec
|
6
10
|
attr_reader :name, :version
|
7
11
|
|
8
|
-
# Initialize a
|
12
|
+
# Initialize a GemSpec.
|
9
13
|
#
|
10
14
|
# @param name [String] gem name
|
11
|
-
# @param version [String] gem version, e.g. '~> 2.0.0'
|
15
|
+
# @param version [String] gem version, e.g. '~> 2.0.0'
|
12
16
|
def initialize(name, version)
|
13
17
|
@name = name
|
14
18
|
@version = version
|
@@ -17,9 +21,8 @@ module Synvert::Core
|
|
17
21
|
# Check if the specified gem version in Gemfile.lock matches gem_spec comparator.
|
18
22
|
#
|
19
23
|
# @return [Boolean] true if matches, otherwise false.
|
20
|
-
# @raise [Synvert::Core::GemfileLockNotFound] raise if Gemfile.lock does not exist.
|
21
24
|
def match?
|
22
|
-
gemfile_lock_path = File.join(Configuration.path, 'Gemfile.lock')
|
25
|
+
gemfile_lock_path = File.expand_path(File.join(Configuration.path, 'Gemfile.lock'))
|
23
26
|
|
24
27
|
# if Gemfile.lock does not exist, just ignore this check
|
25
28
|
return true unless File.exist?(gemfile_lock_path)
|
@@ -24,7 +24,7 @@ module Synvert::Core
|
|
24
24
|
|
25
25
|
# Add arguments with parenthesis if necessary.
|
26
26
|
#
|
27
|
-
# @return [String] return
|
27
|
+
# @return [String] return (!{{arguments}}) if node.arguments present, otherwise return nothing.
|
28
28
|
#
|
29
29
|
# @example
|
30
30
|
#
|
@@ -73,7 +73,7 @@ module Synvert::Core
|
|
73
73
|
# Reject some keys from hash node.
|
74
74
|
#
|
75
75
|
# @param hash_node [Parser::AST::Node]
|
76
|
-
# @param keys [Array] keys should be rejected from the hash.
|
76
|
+
# @param keys [Array<String, Symbol>] keys should be rejected from the hash.
|
77
77
|
# @return [String] source of of the hash node after rejecting some keys.
|
78
78
|
#
|
79
79
|
# @example
|