synvert-core 0.62.1 → 0.64.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/Gemfile +0 -2
  4. data/README.md +73 -33
  5. data/lib/synvert/core/configuration.rb +12 -0
  6. data/lib/synvert/core/exceptions.rb +0 -4
  7. data/lib/synvert/core/node_ext.rb +206 -103
  8. data/lib/synvert/core/rewriter/action/append_action.rb +4 -3
  9. data/lib/synvert/core/rewriter/action/delete_action.rb +13 -6
  10. data/lib/synvert/core/rewriter/action/insert_action.rb +16 -7
  11. data/lib/synvert/core/rewriter/action/insert_after_action.rb +3 -2
  12. data/lib/synvert/core/rewriter/action/prepend_action.rb +3 -2
  13. data/lib/synvert/core/rewriter/action/remove_action.rb +16 -10
  14. data/lib/synvert/core/rewriter/action/replace_action.rb +13 -5
  15. data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +18 -11
  16. data/lib/synvert/core/rewriter/action/replace_with_action.rb +6 -5
  17. data/lib/synvert/core/rewriter/action/wrap_action.rb +13 -5
  18. data/lib/synvert/core/rewriter/action.rb +20 -9
  19. data/lib/synvert/core/rewriter/any_value.rb +1 -0
  20. data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +4 -0
  21. data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +4 -0
  22. data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +4 -0
  23. data/lib/synvert/core/rewriter/condition.rb +11 -3
  24. data/lib/synvert/core/rewriter/gem_spec.rb +7 -4
  25. data/lib/synvert/core/rewriter/helper.rb +2 -2
  26. data/lib/synvert/core/rewriter/instance.rb +195 -94
  27. data/lib/synvert/core/rewriter/ruby_version.rb +4 -4
  28. data/lib/synvert/core/rewriter/scope/goto_scope.rb +5 -6
  29. data/lib/synvert/core/rewriter/scope/within_scope.rb +9 -4
  30. data/lib/synvert/core/rewriter/scope.rb +8 -0
  31. data/lib/synvert/core/rewriter/warning.rb +1 -1
  32. data/lib/synvert/core/rewriter.rb +90 -43
  33. data/lib/synvert/core/version.rb +1 -1
  34. data/lib/synvert/core.rb +0 -1
  35. data/spec/spec_helper.rb +0 -3
  36. data/spec/synvert/core/node_ext_spec.rb +28 -7
  37. data/spec/synvert/core/rewriter/action_spec.rb +0 -4
  38. data/spec/synvert/core/rewriter/gem_spec_spec.rb +11 -10
  39. data/spec/synvert/core/rewriter/instance_spec.rb +7 -17
  40. data/synvert-core-ruby.gemspec +2 -1
  41. 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
- # Begin position of code to replace.
11
- #
12
- # @return [Integer] begin position.
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
- # The rewritten code, always empty string.
27
- def rewritten_code
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
- def initialize(instance, code = nil)
8
- super
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, add, replace or remove code.
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 add, replace or remove.
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
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Synvert::Core
4
+ # A new type to match any value.
4
5
  class Rewriter::AnyValue
5
6
  end
6
7
  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 condition.
6
+ # Initialize a Condition.
7
7
  #
8
8
  # @param instance [Synvert::Core::Rewriter::Instance]
9
9
  # @param rules [Hash]
10
- # @param block [Block]
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 gem_spec.
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 `({{arguments}})` if node.arguments present, otherwise return nothing.
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