ruby-next-core 0.9.1 → 0.10.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/README.md +20 -6
  4. data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
  5. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +201 -0
  6. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
  7. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +121 -0
  8. data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
  9. data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
  10. data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +107 -0
  11. data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
  12. data/lib/ruby-next.rb +8 -6
  13. data/lib/ruby-next/cli.rb +2 -2
  14. data/lib/ruby-next/commands/core_ext.rb +1 -1
  15. data/lib/ruby-next/commands/nextify.rb +44 -10
  16. data/lib/ruby-next/core.rb +27 -21
  17. data/lib/ruby-next/core/array/deconstruct.rb +9 -9
  18. data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
  19. data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
  20. data/lib/ruby-next/core/enumerable/filter.rb +8 -8
  21. data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
  22. data/lib/ruby-next/core/enumerable/tally.rb +7 -7
  23. data/lib/ruby-next/core/enumerator/produce.rb +12 -12
  24. data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
  25. data/lib/ruby-next/core/hash/except.rb +11 -0
  26. data/lib/ruby-next/core/hash/merge.rb +8 -8
  27. data/lib/ruby-next/core/kernel/then.rb +2 -2
  28. data/lib/ruby-next/core/proc/compose.rb +11 -11
  29. data/lib/ruby-next/core/string/split.rb +6 -6
  30. data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
  31. data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
  32. data/lib/ruby-next/core/symbol/end_with.rb +4 -4
  33. data/lib/ruby-next/core/symbol/start_with.rb +4 -4
  34. data/lib/ruby-next/core/time/ceil.rb +6 -6
  35. data/lib/ruby-next/core/time/floor.rb +4 -4
  36. data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
  37. data/lib/ruby-next/core_ext.rb +1 -1
  38. data/lib/ruby-next/language.rb +30 -6
  39. data/lib/ruby-next/language/proposed.rb +3 -0
  40. data/lib/ruby-next/language/rewriters/args_forward.rb +24 -20
  41. data/lib/ruby-next/language/rewriters/base.rb +1 -1
  42. data/lib/ruby-next/language/rewriters/endless_method.rb +26 -3
  43. data/lib/ruby-next/language/rewriters/endless_range.rb +1 -0
  44. data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
  45. data/lib/ruby-next/language/rewriters/method_reference.rb +2 -1
  46. data/lib/ruby-next/language/rewriters/numbered_params.rb +1 -0
  47. data/lib/ruby-next/language/rewriters/pattern_matching.rb +103 -12
  48. data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +74 -11
  49. data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
  50. data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
  51. data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
  52. data/lib/ruby-next/logging.rb +1 -1
  53. data/lib/ruby-next/rubocop.rb +91 -9
  54. data/lib/ruby-next/setup_self.rb +22 -0
  55. data/lib/ruby-next/version.rb +1 -1
  56. data/lib/uby-next.rb +8 -4
  57. metadata +21 -7
@@ -4,40 +4,103 @@ module RubyNext
4
4
  module Language
5
5
  module Rewriters
6
6
  class RightHandAssignment < Base
7
+ NAME = "right-hand-assignment"
7
8
  SYNTAX_PROBE = "1 + 2 => a"
8
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.8.0")
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
9
10
 
10
11
  def on_rasgn(node)
11
12
  context.track! self
12
13
 
14
+ node = super(node)
15
+
13
16
  val_node, asgn_node = *node
14
17
 
15
18
  remove(val_node.loc.expression.end.join(asgn_node.loc.expression))
16
19
  insert_before(val_node.loc.expression, "#{asgn_node.loc.expression.source} = ")
17
20
 
18
- process(
19
- asgn_node.updated(
20
- nil,
21
- asgn_node.children + [val_node]
22
- )
21
+ asgn_node.updated(
22
+ nil,
23
+ asgn_node.children + [val_node]
23
24
  )
24
25
  end
25
26
 
27
+ def on_vasgn(node)
28
+ return super(node) unless rightward?(node)
29
+
30
+ context.track! self
31
+
32
+ name, val_node = *node
33
+
34
+ remove(val_node.loc.expression.end.join(node.loc.name))
35
+ insert_before(val_node.loc.expression, "#{name} = ")
36
+
37
+ super(node)
38
+ end
39
+
40
+ def on_casgn(node)
41
+ return super(node) unless rightward?(node)
42
+
43
+ context.track! self
44
+
45
+ scope_node, name, val_node = *node
46
+
47
+ if scope_node
48
+ scope = scope_node.type == :cbase ? scope_node.loc.expression.source : "#{scope_node.loc.expression.source}::"
49
+ name = "#{scope}#{name}"
50
+ end
51
+
52
+ remove(val_node.loc.expression.end.join(node.loc.name))
53
+ insert_before(val_node.loc.expression, "#{name} = ")
54
+
55
+ super(node)
56
+ end
57
+
26
58
  def on_mrasgn(node)
27
59
  context.track! self
28
60
 
61
+ node = super(node)
62
+
29
63
  lhs, rhs = *node
30
64
 
31
65
  replace(lhs.loc.expression.end.join(rhs.loc.expression), ")")
32
66
  insert_before(lhs.loc.expression, "#{rhs.loc.expression.source} = (")
33
67
 
34
- process(
35
- node.updated(
36
- :masgn,
37
- [rhs, lhs]
38
- )
68
+ node.updated(
69
+ :masgn,
70
+ [rhs, lhs]
39
71
  )
40
72
  end
73
+
74
+ def on_masgn(node)
75
+ return super(node) unless rightward?(node)
76
+
77
+ context.track! self
78
+
79
+ rhs, lhs = *node
80
+
81
+ replace(lhs.loc.expression.end.join(rhs.loc.expression), ")")
82
+ insert_before(lhs.loc.expression, "#{rhs.loc.expression.source} = (")
83
+
84
+ super(node)
85
+ end
86
+
87
+ private
88
+
89
+ def rightward?(node)
90
+ # Location could be empty for node built by rewriters
91
+ return false unless node.loc&.operator
92
+
93
+ assignee_loc =
94
+ if node.type == :masgn
95
+ node.children[0].loc.expression
96
+ else
97
+ node.loc.name
98
+ end
99
+
100
+ return false unless assignee_loc
101
+
102
+ assignee_loc.begin_pos > node.loc.operator.end_pos
103
+ end
41
104
  end
42
105
  end
43
106
  end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class SafeNavigation < Base
7
+ NAME = "safe-navigation"
8
+ SYNTAX_PROBE = "nil&.x&.nil?"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("2.3.0")
10
+
11
+ def on_csend(node)
12
+ node = super(node)
13
+
14
+ context.track! self
15
+
16
+ receiver, *args = *node
17
+
18
+ new_node = node.updated(
19
+ :and,
20
+ [
21
+ process(safe_navigation(receiver)),
22
+ s(:send, decsendize(receiver), *args)
23
+ ]
24
+ )
25
+
26
+ replace(node.loc.expression, new_node)
27
+
28
+ new_node
29
+ end
30
+
31
+ def on_block(node)
32
+ return super(node) unless node.children[0].type == :csend
33
+
34
+ context.track!(self)
35
+
36
+ new_node = super(node.updated(
37
+ :and,
38
+ [
39
+ process(safe_navigation(node.children[0].children[0])),
40
+ process(node.updated(nil, node.children.map(&method(:decsendize))))
41
+ ]
42
+ ))
43
+
44
+ replace(node.loc.expression, new_node)
45
+
46
+ new_node
47
+ end
48
+
49
+ def on_op_asgn(node)
50
+ return super(node) unless node.children[0].type == :csend
51
+
52
+ context.track!(self)
53
+
54
+ new_node = super(node.updated(
55
+ :and,
56
+ [
57
+ process(safe_navigation(node.children[0].children[0])),
58
+ process(node.updated(nil, node.children.map(&method(:decsendize))))
59
+ ]
60
+ ))
61
+
62
+ replace(node.loc.expression, new_node)
63
+
64
+ new_node
65
+ end
66
+
67
+ private
68
+
69
+ def decsendize(node)
70
+ return node unless node.is_a?(::Parser::AST::Node) && node.type == :csend
71
+
72
+ node.updated(:send, node.children.map(&method(:decsendize)))
73
+ end
74
+
75
+ # Transform: x&.y -> (!x.nil? && x.y) || nil
76
+ # This allows us to handle `false&.to_s == "false"`
77
+ def safe_navigation(node)
78
+ s(:or,
79
+ s(:send,
80
+ s(:send, node, :nil?),
81
+ :!),
82
+ s(:nil))
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class ShorthandHash < Base
7
+ NAME = "shorthand-hash"
8
+ SYNTAX_PROBE = "data = {x}"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
10
+
11
+ def on_ipair(node)
12
+ context.track! self
13
+
14
+ ident, = *node.children
15
+
16
+ key = key_from_ident(ident)
17
+
18
+ replace(
19
+ node.loc.expression,
20
+ "#{key}: #{key}"
21
+ )
22
+
23
+ node.updated(
24
+ :pair,
25
+ [
26
+ s(:sym, key),
27
+ ident
28
+ ]
29
+ )
30
+ end
31
+
32
+ private
33
+
34
+ def key_from_ident(node)
35
+ case node.type
36
+ when :send
37
+ node.children[1]
38
+ when :lvar
39
+ node.children[0]
40
+ else
41
+ raise ArgumentError, "Unsupport ipair node: #{node}"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class SquigglyHeredoc < Base
7
+ NAME = "squiggly-heredoc"
8
+ SYNTAX_PROBE = "txt = <<~TXT\n bla\n TXT"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("2.3.0")
10
+
11
+ def on_str(node)
12
+ node = super(node) if defined?(super_method)
13
+ return node unless node.loc.respond_to?(:heredoc_body) && node.loc.expression.source.include?("<<~")
14
+
15
+ context.track! self
16
+
17
+ replace(node.loc.expression, node.loc.expression.source.tr("~", "-"))
18
+
19
+ heredoc_loc = node.loc.heredoc_body.join(node.loc.heredoc_end)
20
+ heredoc_source, heredoc_end = heredoc_loc.source.split(/\n([^\n]+)\z/)
21
+
22
+ indent = heredoc_source.lines.map { |line| line.match(/^\s*/)[0].size }.min
23
+
24
+ new_source = heredoc_source.gsub!(%r{^\s{#{indent}}}, "")
25
+
26
+ replace(heredoc_loc, [new_source, heredoc_end].join("\n"))
27
+
28
+ node
29
+ end
30
+
31
+ alias on_dstr on_str
32
+ alias on_xstr on_str
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "utils"
3
+ require "ruby-next/utils"
4
4
 
5
5
  module RubyNext
6
6
  class << self
@@ -46,23 +46,25 @@ module RuboCop
46
46
  end
47
47
  end
48
48
 
49
+ # Let's make this file Ruby 2.2 compatible to avoid transpiling
50
+ # rubocop:disable Layout/HeredocIndentation
49
51
  module RuboCop
50
52
  module AST
51
53
  module Traversal
52
54
  # Fixed in https://github.com/rubocop-hq/rubocop/pull/7786
53
- unless defined?(::RuboCop::AST::CaseMatchNode)
54
- %i[case_match in_pattern].each do |type|
55
- module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
56
- def on_#{type}(node)
57
- node.children.each { |child| send(:"on_\#{child.type}", child) if child }
58
- nil
59
- end
60
- RUBY
61
- end
55
+ %i[case_match in_pattern find_pattern].each do |type|
56
+ next if method_defined?(:"on_#{type}")
57
+ module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
58
+ def on_#{type}(node)
59
+ node.children.each { |child| send(:"on_\#{child.type}", child) if child }
60
+ nil
61
+ end
62
+ RUBY
62
63
  end
63
64
  end
64
65
  end
65
66
  end
67
+ # rubocop:enable Layout/HeredocIndentation
66
68
 
67
69
  module RuboCop
68
70
  module Cop
@@ -73,6 +75,10 @@ module RuboCop
73
75
  trigger_responding_cops(:on_meth_ref, node)
74
76
  end
75
77
 
78
+ def on_ipair(node)
79
+ trigger_responding_cops(:on_ipair, node)
80
+ end
81
+
76
82
  unless method_defined?(:on_numblock)
77
83
  def on_numblock(node)
78
84
  children = node.children
@@ -111,5 +117,81 @@ module RuboCop
111
117
  end
112
118
  end
113
119
  end
120
+
121
+ module Layout
122
+ require "rubocop/cop/layout/assignment_indentation"
123
+ AssignmentIndentation.prepend(Module.new do
124
+ POTENTIAL_RIGHT_TYPES = %i[ivasgn lvasgn cvasgn gvasgn casgn masgn].freeze
125
+
126
+ def check_assignment(node, *)
127
+ return if rightward?(node)
128
+ super
129
+ end
130
+
131
+ private
132
+
133
+ def rightward?(node)
134
+ return unless POTENTIAL_RIGHT_TYPES.include?(node.type)
135
+
136
+ return unless node.loc.operator
137
+
138
+ assignee_loc =
139
+ if node.type == :masgn
140
+ node.children[0].loc.expression
141
+ else
142
+ node.loc.name
143
+ end
144
+
145
+ return false unless assignee_loc
146
+
147
+ assignee_loc.begin_pos > node.loc.operator.end_pos
148
+ end
149
+ end)
150
+
151
+ require "rubocop/cop/layout/empty_line_between_defs"
152
+ EmptyLineBetweenDefs.prepend(Module.new do
153
+ def def_end(node)
154
+ return super unless node.loc.end.nil?
155
+
156
+ node.loc.expression.line
157
+ end
158
+ end)
159
+ end
160
+
161
+ module Style
162
+ require "rubocop/cop/style/single_line_methods"
163
+ SingleLineMethods.prepend(Module.new do
164
+ def on_def(node)
165
+ return if node.loc.end.nil?
166
+ super
167
+ end
168
+
169
+ def on_defs(node)
170
+ return if node.loc.end.nil?
171
+ super
172
+ end
173
+ end)
174
+
175
+ require "rubocop/cop/style/def_with_parentheses"
176
+ DefWithParentheses.prepend(Module.new do
177
+ def on_def(node)
178
+ return if node.loc.end.nil?
179
+ super
180
+ end
181
+
182
+ def on_defs(node)
183
+ return if node.loc.end.nil?
184
+ super
185
+ end
186
+ end)
187
+
188
+ require "rubocop/cop/style/trailing_method_end_statement"
189
+ TrailingMethodEndStatement.prepend(Module.new do
190
+ def on_def(node)
191
+ return if node.loc.end.nil?
192
+ super
193
+ end
194
+ end)
195
+ end
114
196
  end
115
197
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file setup LOAD_PATH to load Ruby Next own transpiled paths
4
+ # (we cannot use language/setup here, 'cause it requires Core to be loaded)
5
+
6
+ version = RubyNext.next_version
7
+ next_dirname = File.join(__dir__, "..", ".rbnext")
8
+ lib_path = File.realpath(File.join(__dir__, ".."))
9
+ current_index = $LOAD_PATH.index(lib_path)
10
+
11
+ loop do
12
+ break unless version
13
+
14
+ version_dir = File.join(next_dirname, version.segments[0..1].join("."))
15
+
16
+ if File.exist?(version_dir)
17
+ $LOAD_PATH.insert current_index, version_dir
18
+ current_index += 1
19
+ end
20
+
21
+ version = RubyNext.next_version(version)
22
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyNext
4
- VERSION = "0.9.1"
4
+ VERSION = "0.10.3"
5
5
  end