ruby-next-core 0.9.1 → 0.10.3

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 (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