ruby-next-core 0.15.3 → 1.0.0.rc.1
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 +28 -0
- data/README.md +118 -48
- data/bin/mspec +11 -0
- data/lib/.rbnext/2.1/ruby-next/commands/nextify.rb +295 -0
- data/lib/.rbnext/2.1/ruby-next/core.rb +10 -2
- data/lib/.rbnext/2.1/ruby-next/language.rb +54 -10
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +82 -2
- data/lib/.rbnext/2.3/ruby-next/config.rb +79 -0
- data/lib/.rbnext/2.3/ruby-next/core/data.rb +159 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/pattern_matching.rb +2 -2
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +6 -32
- data/lib/.rbnext/2.3/ruby-next/utils.rb +3 -22
- data/lib/.rbnext/2.6/ruby-next/core/data.rb +159 -0
- data/lib/.rbnext/2.7/ruby-next/core/data.rb +159 -0
- data/lib/.rbnext/2.7/ruby-next/core.rb +10 -2
- data/lib/.rbnext/2.7/ruby-next/language/paco_parsers/string_literals.rb +109 -0
- data/lib/.rbnext/2.7/ruby-next/language/rewriters/2.7/pattern_matching.rb +2 -2
- data/lib/.rbnext/2.7/ruby-next/language/rewriters/text.rb +132 -0
- data/lib/.rbnext/3.2/ruby-next/commands/base.rb +55 -0
- data/lib/.rbnext/3.2/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
- data/lib/.rbnext/3.2/ruby-next/rubocop.rb +210 -0
- data/lib/ruby-next/commands/nextify.rb +84 -2
- data/lib/ruby-next/config.rb +27 -0
- data/lib/ruby-next/core/data.rb +159 -0
- data/lib/ruby-next/core/matchdata/deconstruct.rb +9 -0
- data/lib/ruby-next/core/matchdata/deconstruct_keys.rb +20 -0
- data/lib/ruby-next/core/matchdata/named_captures.rb +11 -0
- data/lib/ruby-next/core/refinement/import.rb +44 -36
- data/lib/ruby-next/core/time/deconstruct_keys.rb +30 -0
- data/lib/ruby-next/core.rb +10 -2
- data/lib/ruby-next/irb.rb +2 -2
- data/lib/ruby-next/language/bootsnap.rb +2 -25
- data/lib/ruby-next/language/paco_parser.rb +7 -0
- data/lib/ruby-next/language/paco_parsers/base.rb +47 -0
- data/lib/ruby-next/language/paco_parsers/comments.rb +26 -0
- data/lib/ruby-next/language/paco_parsers/string_literals.rb +109 -0
- data/lib/ruby-next/language/parser.rb +24 -2
- data/lib/ruby-next/language/rewriters/3.0/args_forward_leading.rb +2 -2
- data/lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb +1 -1
- data/lib/ruby-next/language/rewriters/3.2/anonymous_restargs.rb +104 -0
- data/lib/ruby-next/language/rewriters/abstract.rb +57 -0
- data/lib/ruby-next/language/rewriters/base.rb +6 -32
- data/lib/ruby-next/language/rewriters/edge/it_param.rb +58 -0
- data/lib/ruby-next/language/rewriters/edge.rb +12 -0
- data/lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb +3 -0
- data/lib/ruby-next/language/rewriters/proposed/method_reference.rb +9 -20
- data/lib/ruby-next/language/rewriters/text.rb +132 -0
- data/lib/ruby-next/language/runtime.rb +9 -86
- data/lib/ruby-next/language/setup.rb +5 -2
- data/lib/ruby-next/language/unparser.rb +5 -0
- data/lib/ruby-next/language.rb +54 -10
- data/lib/ruby-next/pry.rb +1 -1
- data/lib/ruby-next/rubocop.rb +2 -0
- data/lib/ruby-next/utils.rb +3 -22
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next.rb +2 -2
- metadata +65 -12
@@ -13,7 +13,7 @@ module RubyNext
|
|
13
13
|
|
14
14
|
MSG
|
15
15
|
|
16
|
-
class Base <
|
16
|
+
class Base < Abstract
|
17
17
|
class LocalsTracker
|
18
18
|
using(Module.new do
|
19
19
|
refine ::Parser::AST::Node do
|
@@ -65,39 +65,15 @@ module RubyNext
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
|
69
|
-
# Returns true if the syntax is not supported
|
70
|
-
# by the current Ruby (performs syntax check, not version check)
|
71
|
-
def unsupported_syntax?
|
72
|
-
save_verbose, $VERBOSE = $VERBOSE, nil
|
73
|
-
eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
|
74
|
-
Kernel.send eval_mid, self::SYNTAX_PROBE, nil, __FILE__, __LINE__
|
75
|
-
false
|
76
|
-
rescue SyntaxError, StandardError
|
77
|
-
true
|
78
|
-
ensure
|
79
|
-
$VERBOSE = save_verbose
|
80
|
-
end
|
81
|
-
|
82
|
-
# Returns true if the syntax is supported
|
83
|
-
# by the specified version
|
84
|
-
def unsupported_version?(version)
|
85
|
-
self::MIN_SUPPORTED_VERSION > version
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
68
|
+
attr_reader :locals
|
89
69
|
|
90
|
-
|
91
|
-
|
92
|
-
end
|
70
|
+
def self.ast?
|
71
|
+
true
|
93
72
|
end
|
94
73
|
|
95
|
-
|
96
|
-
|
97
|
-
def initialize(context)
|
98
|
-
@context = context
|
74
|
+
def initialize(*args)
|
99
75
|
@locals = LocalsTracker.new
|
100
|
-
super
|
76
|
+
super
|
101
77
|
end
|
102
78
|
|
103
79
|
def s(type, *children)
|
@@ -145,8 +121,6 @@ module RubyNext
|
|
145
121
|
|
146
122
|
Unparser.unparse(ast).chomp
|
147
123
|
end
|
148
|
-
|
149
|
-
attr_reader :context
|
150
124
|
end
|
151
125
|
end
|
152
126
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyNext
|
4
|
+
module Language
|
5
|
+
module Rewriters
|
6
|
+
class ItParam < Base
|
7
|
+
using RubyNext
|
8
|
+
|
9
|
+
NAME = "it-param"
|
10
|
+
SYNTAX_PROBE = "proc { it.keys }.call({})"
|
11
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.4.0")
|
12
|
+
|
13
|
+
def on_block(node)
|
14
|
+
proc_or_lambda, args, body = *node.children
|
15
|
+
|
16
|
+
return super unless block_has_it?(body)
|
17
|
+
|
18
|
+
context.track! self
|
19
|
+
|
20
|
+
new_body = s(:begin,
|
21
|
+
s(:lvasgn, :it, s(:lvar, :_1)),
|
22
|
+
body)
|
23
|
+
|
24
|
+
insert_before(body.loc.expression, "it = _1;")
|
25
|
+
|
26
|
+
process(
|
27
|
+
node.updated(:numblock, [
|
28
|
+
proc_or_lambda,
|
29
|
+
args,
|
30
|
+
new_body
|
31
|
+
])
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# It's important to check if the current block refers to `it` variable somewhere
|
38
|
+
# (and not within a nested block), so we don't declare numbered params
|
39
|
+
def block_has_it?(node)
|
40
|
+
# traverse node children deeply
|
41
|
+
tree = [node]
|
42
|
+
|
43
|
+
while (child = tree.shift)
|
44
|
+
return true if it?(child)
|
45
|
+
|
46
|
+
if child.is_a?(Parser::AST::Node)
|
47
|
+
tree.unshift(*child.children.select { |c| c.is_a?(Parser::AST::Node) && c.type != :block && c.type != :numblock })
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def it?(node)
|
53
|
+
node.is_a?(Parser::AST::Node) && node.type == :send && node.children[0].nil? && node.children[1] == :it && node.children[2].nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,3 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Load edge Ruby features
|
4
|
+
|
5
|
+
require "ruby-next/language/rewriters/edge/it_param"
|
6
|
+
|
7
|
+
# We must add this rewriter before nubmered params rewriter to allow it to transform the source code further
|
8
|
+
|
9
|
+
number_params = RubyNext::Language.rewriters.index(RubyNext::Language::Rewriters::NumberedParams)
|
10
|
+
|
11
|
+
if number_params
|
12
|
+
RubyNext::Language.rewriters.insert(number_params, RubyNext::Language::Rewriters::ItParam)
|
13
|
+
else
|
14
|
+
RubyNext::Language.rewriters << RubyNext::Language::Rewriters::ItParam
|
15
|
+
end
|
@@ -1,33 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "parser/rubynext"
|
4
|
+
RubyNext::Language.parser_class = ::Parser::RubyNext
|
5
|
+
|
3
6
|
module RubyNext
|
4
7
|
module Language
|
5
8
|
module Rewriters
|
6
|
-
class MethodReference <
|
9
|
+
class MethodReference < Text
|
7
10
|
NAME = "method-reference"
|
8
11
|
SYNTAX_PROBE = "Language.:transform"
|
9
12
|
MIN_SUPPORTED_VERSION = Gem::Version.new(RubyNext::NEXT_VERSION)
|
10
13
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
receiver, mid = *node.children
|
15
|
-
|
16
|
-
replace(
|
17
|
-
node.children.first.loc.expression.end.join(
|
18
|
-
node.loc.expression.end
|
19
|
-
),
|
20
|
-
".method(:#{mid})"
|
21
|
-
)
|
14
|
+
def safe_rewrite(source)
|
15
|
+
source.gsub(/\.:([\w_]+)/) do |match|
|
16
|
+
context.track! self
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
[
|
26
|
-
receiver,
|
27
|
-
:method,
|
28
|
-
s(:sym, mid)
|
29
|
-
]
|
30
|
-
)
|
18
|
+
".method(:#{$1})"
|
19
|
+
end
|
31
20
|
end
|
32
21
|
end
|
33
22
|
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ruby-next/language/paco_parser"
|
4
|
+
|
5
|
+
module RubyNext
|
6
|
+
module Language
|
7
|
+
module Rewriters
|
8
|
+
class Text < Abstract
|
9
|
+
using RubyNext
|
10
|
+
|
11
|
+
class Normalizer < PacoParsers::Base
|
12
|
+
attr_reader :store
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@store = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def normalizing(source)
|
19
|
+
many(
|
20
|
+
alt(
|
21
|
+
ruby_comment,
|
22
|
+
ruby_string,
|
23
|
+
ruby_code
|
24
|
+
)
|
25
|
+
).parse(source, with_callstack: true)
|
26
|
+
.then(&:join)
|
27
|
+
.then do
|
28
|
+
if block_given?
|
29
|
+
yield _1
|
30
|
+
else
|
31
|
+
_1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
.then do |new_source|
|
35
|
+
restore(new_source)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ruby_comment
|
40
|
+
parse_comments.fmap do |result|
|
41
|
+
store << result
|
42
|
+
"# A#{store.size}Я\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def ruby_string
|
47
|
+
parse_strings.fmap do |result|
|
48
|
+
result.each_with_object([]) do |(type, str), acc|
|
49
|
+
if type == :literal
|
50
|
+
store << str
|
51
|
+
acc << "_A#{store.size}Я_"
|
52
|
+
else
|
53
|
+
acc << str
|
54
|
+
end
|
55
|
+
acc
|
56
|
+
end.join
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def ruby_code
|
61
|
+
any_char
|
62
|
+
end
|
63
|
+
|
64
|
+
def restore(source)
|
65
|
+
source.gsub(/(?:\# |_)A(\d+)Я(?:_|\n)/m) do |*args|
|
66
|
+
store[$1.to_i - 1]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_comments
|
71
|
+
memoize { PacoParsers::Comments.new.default }
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_strings
|
75
|
+
memoize { PacoParsers::StringLiterals.new.default }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Base class for rewriting parsers which adds the #track! method
|
80
|
+
class PacoParser < PacoParsers::Base
|
81
|
+
attr_reader :rewriter, :context
|
82
|
+
|
83
|
+
def initialize(rewriter, context)
|
84
|
+
@rewriter = rewriter
|
85
|
+
@context = context
|
86
|
+
end
|
87
|
+
|
88
|
+
def track!
|
89
|
+
context.track!(rewriter)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class << self
|
94
|
+
def parser(&block)
|
95
|
+
@paco_parser = Class.new(PacoParser, &block)
|
96
|
+
end
|
97
|
+
|
98
|
+
def paco_parser
|
99
|
+
return @paco_parser if @paco_parser
|
100
|
+
|
101
|
+
superclass.paco_parser if superclass.respond_to?(:paco_parser)
|
102
|
+
end
|
103
|
+
|
104
|
+
def text?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Rewrite source code by ignoring string literals and comments
|
110
|
+
def rewrite(source)
|
111
|
+
Normalizer.new.normalizing(source) do |normalized|
|
112
|
+
safe_rewrite(normalized)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def safe_rewrite(source)
|
117
|
+
source
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def parse(source)
|
123
|
+
parser_class = self.class.paco_parser
|
124
|
+
raise "No parser defined for #{self.class}" unless parser_class
|
125
|
+
|
126
|
+
paco_parser = self.class.paco_parser.new(self, context)
|
127
|
+
paco_parser.parse(source)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "require-hooks/setup"
|
4
4
|
|
5
5
|
require "ruby-next"
|
6
|
-
require "ruby-next/utils"
|
7
6
|
require "ruby-next/language"
|
8
7
|
require "ruby-next/language/eval"
|
9
8
|
|
@@ -16,102 +15,26 @@ module RubyNext
|
|
16
15
|
using RubyNext
|
17
16
|
|
18
17
|
class << self
|
19
|
-
|
20
|
-
|
21
|
-
def load(path, wrap: false)
|
22
|
-
raise "RubyNext cannot handle `load(smth, wrap: true)`" if wrap
|
23
|
-
|
24
|
-
contents = File.read(path)
|
18
|
+
def load(path, contents)
|
19
|
+
contents ||= File.read(path)
|
25
20
|
new_contents = transform contents
|
26
21
|
|
27
22
|
RubyNext.debug_source new_contents, path
|
28
23
|
|
29
|
-
|
30
|
-
true
|
24
|
+
new_contents
|
31
25
|
end
|
32
26
|
|
33
27
|
def transform(contents, **options)
|
34
28
|
Language.transform(contents, rewriters: Language.current_rewriters, **options)
|
35
29
|
end
|
36
|
-
|
37
|
-
def feature_path(path)
|
38
|
-
path = resolve_feature_path(path)
|
39
|
-
return if path.nil?
|
40
|
-
return if File.extname(path) != ".rb"
|
41
|
-
return unless Language.transformable?(path)
|
42
|
-
path
|
43
|
-
end
|
44
|
-
|
45
|
-
if defined?(JRUBY_VERSION) || defined?(TruffleRuby)
|
46
|
-
def evaluate(code, filepath)
|
47
|
-
new_toplevel.eval(code, filepath)
|
48
|
-
end
|
49
|
-
|
50
|
-
def new_toplevel
|
51
|
-
# Create new "toplevel" binding to avoid lexical scope re-use
|
52
|
-
# (aka "leaking refinements")
|
53
|
-
eval "proc{binding}.call", TOPLEVEL_BINDING, __FILE__, __LINE__
|
54
|
-
end
|
55
|
-
else
|
56
|
-
def evaluate(code, filepath)
|
57
|
-
# This is workaround to solve the "leaking refinements" problem in MRI
|
58
|
-
RubyVM::InstructionSequence.compile(code, filepath).then do |iseq|
|
59
|
-
iseq.eval
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
30
|
end
|
64
31
|
end
|
65
32
|
end
|
66
33
|
end
|
67
34
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def require(path)
|
74
|
-
realpath = RubyNext::Language::Runtime.feature_path(path)
|
75
|
-
return require_without_ruby_next(path) unless realpath
|
76
|
-
|
77
|
-
return false if $LOADED_FEATURES.include?(realpath)
|
78
|
-
|
79
|
-
$LOADED_FEATURES << realpath
|
80
|
-
|
81
|
-
RubyNext::Language::Runtime.load(realpath)
|
82
|
-
|
83
|
-
true
|
84
|
-
rescue => e
|
85
|
-
$LOADED_FEATURES.delete realpath
|
86
|
-
RubyNext.warn "RubyNext failed to require '#{path}': #{e.message}"
|
87
|
-
require_without_ruby_next(path)
|
88
|
-
end
|
89
|
-
|
90
|
-
alias_method :require_relative_without_ruby_next, :require_relative
|
91
|
-
def require_relative(path)
|
92
|
-
loc = caller_locations(1..1).first
|
93
|
-
from = loc.absolute_path || loc.path || File.join(Dir.pwd, "main")
|
94
|
-
realpath = File.absolute_path(
|
95
|
-
File.join(
|
96
|
-
File.dirname(File.absolute_path(from)),
|
97
|
-
path
|
98
|
-
)
|
99
|
-
)
|
100
|
-
require(realpath)
|
101
|
-
rescue => e
|
102
|
-
RubyNext.warn "RubyNext failed to require relative '#{path}' from #{from}: #{e.message}"
|
103
|
-
require_relative_without_ruby_next(path)
|
104
|
-
end
|
105
|
-
|
106
|
-
alias_method :load_without_ruby_next, :load
|
107
|
-
def load(path, wrap = false)
|
108
|
-
realpath = RubyNext::Language::Runtime.feature_path(path)
|
109
|
-
|
110
|
-
return load_without_ruby_next(path, wrap) unless realpath
|
111
|
-
|
112
|
-
RubyNext::Language::Runtime.load(realpath, wrap: wrap)
|
113
|
-
rescue => e
|
114
|
-
RubyNext.warn "RubyNext failed to load '#{path}': #{e.message}"
|
115
|
-
load_without_ruby_next(path)
|
116
|
-
end
|
35
|
+
RequireHooks.source_transform(
|
36
|
+
patterns: RubyNext::Language.include_patterns,
|
37
|
+
exclude_patterns: RubyNext::Language.exclude_patterns
|
38
|
+
) do |path, contents|
|
39
|
+
RubyNext::Language::Runtime.load(path, contents)
|
117
40
|
end
|
@@ -12,7 +12,10 @@ module RubyNext
|
|
12
12
|
return if File.directory?(target_dir)
|
13
13
|
|
14
14
|
Dir.chdir(root_dir) do
|
15
|
-
|
15
|
+
command = "bundle exec ruby-next nextify " \
|
16
|
+
"./#{lib_dir} -o #{target_dir} --min-version=#{RubyNext.current_ruby_version}"
|
17
|
+
|
18
|
+
unless system("#{command} > /dev/null 2>&1")
|
16
19
|
RubyNext.warn "Traspiled files are missing in: #{target_dir}. \n" \
|
17
20
|
"Make sure you have gem 'ruby-next' in your Gemfile to auto-transpile the required files from source on load. " \
|
18
21
|
"Otherwise the code from #{root_dir} may not work correctly."
|
@@ -43,7 +46,7 @@ module RubyNext
|
|
43
46
|
|
44
47
|
dirname = File.realpath(dirname)
|
45
48
|
|
46
|
-
return if Language.runtime? && Language.
|
49
|
+
return if Language.runtime? && Language.target_dir?(dirname)
|
47
50
|
|
48
51
|
next_dirname = File.join(dirname, rbnext_dir)
|
49
52
|
|
@@ -5,6 +5,11 @@ save_verbose, $VERBOSE = $VERBOSE, nil
|
|
5
5
|
require "parser/current"
|
6
6
|
$VERBOSE = save_verbose
|
7
7
|
|
8
|
+
# For backward compatibility with older Unparser for EOL Rubies
|
9
|
+
if !Parser::Lexer.const_defined?(:ESCAPES)
|
10
|
+
Parser::Lexer::ESCAPES = Parser::LexerStrings::ESCAPES
|
11
|
+
end
|
12
|
+
|
8
13
|
require "unparser"
|
9
14
|
|
10
15
|
# For backward compatibility with older Unparser
|
data/lib/ruby-next/language.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
gem "ruby-next-parser", ">= 2.8.0.3"
|
4
4
|
gem "unparser", ">= 0.4.7"
|
5
5
|
|
6
|
-
require "set"
|
6
|
+
require "set" # rubocop:disable Lint/RedundantRequireStatement
|
7
7
|
|
8
8
|
require "ruby-next"
|
9
9
|
|
@@ -62,8 +62,15 @@ module RubyNext
|
|
62
62
|
end
|
63
63
|
|
64
64
|
class << self
|
65
|
+
attr_reader :include_patterns
|
66
|
+
attr_reader :exclude_patterns
|
67
|
+
|
68
|
+
def watch_dirs
|
69
|
+
warn "[DEPRECATED] Use `RubyNext::Language.include_patterns` instead of `RubyNext::Language.watch_dirs`"
|
70
|
+
@watch_dirs
|
71
|
+
end
|
72
|
+
|
65
73
|
attr_accessor :rewriters
|
66
|
-
attr_reader :watch_dirs
|
67
74
|
|
68
75
|
attr_accessor :strategy
|
69
76
|
|
@@ -95,14 +102,17 @@ module RubyNext
|
|
95
102
|
end
|
96
103
|
|
97
104
|
def transform(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, context: TransformContext.new)
|
105
|
+
text_rewriters, ast_rewriters = rewriters.partition(&:text?)
|
106
|
+
|
98
107
|
retried = 0
|
99
|
-
new_source =
|
108
|
+
new_source = text_rewrite(source, rewriters: text_rewriters, using: using, context: context)
|
109
|
+
|
100
110
|
begin
|
101
111
|
new_source =
|
102
112
|
if mode == :rewrite
|
103
|
-
rewrite(
|
113
|
+
rewrite(new_source, rewriters: ast_rewriters, using: using, context: context)
|
104
114
|
else
|
105
|
-
regenerate(
|
115
|
+
regenerate(new_source, rewriters: ast_rewriters, using: using, context: context)
|
106
116
|
end
|
107
117
|
rescue Unparser::UnknownNodeError => err
|
108
118
|
RubyNext.warn "Ruby Next fallbacks to \"rewrite\" transpiling mode since the version of Unparser you use doesn't support some syntax yet: #{err.message}.\n" \
|
@@ -119,8 +129,17 @@ module RubyNext
|
|
119
129
|
Core.inject! new_source.dup
|
120
130
|
end
|
121
131
|
|
132
|
+
def target_dir?(dirname)
|
133
|
+
# fnmatch? requires a file name, not a folder
|
134
|
+
fname = File.join(dirname, "x.rb")
|
135
|
+
|
136
|
+
include_patterns.any? { |pattern| File.fnmatch?(pattern, fname) } &&
|
137
|
+
exclude_patterns.none? { |pattern| File.fnmatch?(pattern, fname) }
|
138
|
+
end
|
139
|
+
|
122
140
|
def transformable?(path)
|
123
|
-
|
141
|
+
include_patterns.any? { |pattern| File.fnmatch?(pattern, path) } &&
|
142
|
+
exclude_patterns.none? { |pattern| File.fnmatch?(pattern, path) }
|
124
143
|
end
|
125
144
|
|
126
145
|
# Rewriters required for the current version
|
@@ -165,14 +184,36 @@ module RubyNext
|
|
165
184
|
end
|
166
185
|
end
|
167
186
|
|
187
|
+
def text_rewrite(source, rewriters:, using:, context:)
|
188
|
+
rewriters.inject(source) do |src, rewriter|
|
189
|
+
rewriter.new(context).rewrite(src)
|
190
|
+
end.then do |new_source|
|
191
|
+
next source unless context.dirty?
|
192
|
+
|
193
|
+
new_source
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
168
197
|
attr_writer :watch_dirs
|
198
|
+
attr_writer :include_patterns, :exclude_patterns
|
169
199
|
end
|
170
200
|
|
171
201
|
self.rewriters = []
|
172
|
-
self.watch_dirs =
|
202
|
+
self.watch_dirs = [].tap do |dirs|
|
203
|
+
# For backward compatibility
|
204
|
+
dirs.define_singleton_method(:<<) do |dir|
|
205
|
+
super(dir)
|
206
|
+
RubyNext::Language.include_patterns << File.join(dir, "*.rb")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
self.include_patterns = %w[app lib spec test].map { |path| File.join(Dir.pwd, path, "*.rb") }
|
211
|
+
self.exclude_patterns = %w[vendor/bundle].map { |path| File.join(Dir.pwd, path, "*") }
|
173
212
|
self.mode = ENV.fetch("RUBY_NEXT_TRANSPILE_MODE", "rewrite").to_sym
|
174
213
|
|
214
|
+
require "ruby-next/language/rewriters/abstract"
|
175
215
|
require "ruby-next/language/rewriters/base"
|
216
|
+
require "ruby-next/language/rewriters/text"
|
176
217
|
|
177
218
|
require "ruby-next/language/rewriters/2.1/numeric_literals"
|
178
219
|
rewriters << Rewriters::NumericLiterals
|
@@ -212,7 +253,7 @@ module RubyNext
|
|
212
253
|
rewriters << Rewriters::InPattern
|
213
254
|
|
214
255
|
require "ruby-next/language/rewriters/3.0/endless_method"
|
215
|
-
|
256
|
+
rewriters << RubyNext::Language::Rewriters::EndlessMethod
|
216
257
|
|
217
258
|
require "ruby-next/language/rewriters/3.1/oneline_pattern_parensless"
|
218
259
|
rewriters << Rewriters::OnelinePatternParensless
|
@@ -232,16 +273,19 @@ module RubyNext
|
|
232
273
|
require "ruby-next/language/rewriters/3.1/shorthand_hash"
|
233
274
|
rewriters << RubyNext::Language::Rewriters::ShorthandHash
|
234
275
|
|
276
|
+
require "ruby-next/language/rewriters/3.2/anonymous_restargs"
|
277
|
+
rewriters << RubyNext::Language::Rewriters::AnonymousRestArgs
|
278
|
+
|
235
279
|
# Put endless range in the end, 'cause Parser fails to parse it in
|
236
280
|
# pattern matching
|
237
281
|
require "ruby-next/language/rewriters/2.6/endless_range"
|
238
282
|
rewriters << Rewriters::EndlessRange
|
239
283
|
|
240
|
-
if
|
284
|
+
if RubyNext.edge_syntax?
|
241
285
|
require "ruby-next/language/rewriters/edge"
|
242
286
|
end
|
243
287
|
|
244
|
-
if
|
288
|
+
if RubyNext.proposed_syntax?
|
245
289
|
require "ruby-next/language/rewriters/proposed"
|
246
290
|
end
|
247
291
|
end
|
data/lib/ruby-next/pry.rb
CHANGED
data/lib/ruby-next/rubocop.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
# edge features and fix some bugs with 2.7+ syntax
|
5
5
|
|
6
6
|
require "parser/ruby-next/version"
|
7
|
+
require "ruby-next/config"
|
7
8
|
require "ruby-next/language/parser"
|
8
9
|
|
9
10
|
module RuboCop
|
@@ -38,6 +39,7 @@ module RuboCop
|
|
38
39
|
def parser_class(version)
|
39
40
|
return super unless version == RUBY_NEXT_VERSION
|
40
41
|
|
42
|
+
require "parser/rubynext"
|
41
43
|
Parser::RubyNext
|
42
44
|
end
|
43
45
|
end
|
data/lib/ruby-next/utils.rb
CHANGED
@@ -4,28 +4,6 @@ module RubyNext
|
|
4
4
|
module Utils
|
5
5
|
module_function
|
6
6
|
|
7
|
-
if $LOAD_PATH.respond_to?(:resolve_feature_path)
|
8
|
-
def resolve_feature_path(feature)
|
9
|
-
$LOAD_PATH.resolve_feature_path(feature)&.last
|
10
|
-
rescue LoadError
|
11
|
-
end
|
12
|
-
else
|
13
|
-
def resolve_feature_path(path)
|
14
|
-
if File.file?(relative = File.expand_path(path))
|
15
|
-
path = relative
|
16
|
-
end
|
17
|
-
|
18
|
-
path = "#{path}.rb" if File.extname(path).empty?
|
19
|
-
|
20
|
-
return path if Pathname.new(path).absolute?
|
21
|
-
|
22
|
-
$LOAD_PATH.find do |lp|
|
23
|
-
lpath = File.join(lp, path)
|
24
|
-
return File.realpath(lpath) if File.file?(lpath)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
7
|
def source_with_lines(source, path)
|
30
8
|
source.lines.map.with_index do |line, i|
|
31
9
|
"#{(i + 1).to_s.rjust(4)}: #{line}"
|
@@ -36,6 +14,7 @@ module RubyNext
|
|
36
14
|
|
37
15
|
# Returns true if modules refinement is supported in current version
|
38
16
|
def refine_modules?
|
17
|
+
save_verbose, $VERBOSE = $VERBOSE, nil
|
39
18
|
@refine_modules ||=
|
40
19
|
begin
|
41
20
|
# Make sure that including modules within refinements works
|
@@ -61,6 +40,8 @@ module RubyNext
|
|
61
40
|
true
|
62
41
|
rescue TypeError, NoMethodError
|
63
42
|
false
|
43
|
+
ensure
|
44
|
+
$VERBOSE = save_verbose
|
64
45
|
end
|
65
46
|
end
|
66
47
|
end
|
data/lib/ruby-next/version.rb
CHANGED