ruby-next-core 0.15.3 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +127 -54
  4. data/bin/mspec +11 -0
  5. data/lib/.rbnext/2.1/ruby-next/commands/nextify.rb +295 -0
  6. data/lib/.rbnext/2.1/ruby-next/core.rb +10 -2
  7. data/lib/.rbnext/2.1/ruby-next/language.rb +59 -12
  8. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +83 -3
  9. data/lib/.rbnext/2.3/ruby-next/config.rb +79 -0
  10. data/lib/.rbnext/2.3/ruby-next/core/data.rb +163 -0
  11. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +4 -4
  12. data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/pattern_matching.rb +2 -2
  13. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +6 -32
  14. data/lib/.rbnext/2.3/ruby-next/utils.rb +3 -22
  15. data/lib/.rbnext/2.6/ruby-next/core/data.rb +163 -0
  16. data/lib/.rbnext/2.7/ruby-next/core/data.rb +163 -0
  17. data/lib/.rbnext/2.7/ruby-next/core.rb +10 -2
  18. data/lib/.rbnext/2.7/ruby-next/language/paco_parsers/string_literals.rb +109 -0
  19. data/lib/.rbnext/2.7/ruby-next/language/rewriters/2.7/pattern_matching.rb +2 -2
  20. data/lib/.rbnext/2.7/ruby-next/language/rewriters/text.rb +132 -0
  21. data/lib/.rbnext/3.2/ruby-next/commands/base.rb +55 -0
  22. data/lib/.rbnext/3.2/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
  23. data/lib/.rbnext/3.2/ruby-next/rubocop.rb +210 -0
  24. data/lib/ruby-next/commands/nextify.rb +85 -3
  25. data/lib/ruby-next/config.rb +29 -2
  26. data/lib/ruby-next/core/data.rb +163 -0
  27. data/lib/ruby-next/core/matchdata/deconstruct.rb +9 -0
  28. data/lib/ruby-next/core/matchdata/deconstruct_keys.rb +20 -0
  29. data/lib/ruby-next/core/matchdata/named_captures.rb +11 -0
  30. data/lib/ruby-next/core/refinement/import.rb +44 -36
  31. data/lib/ruby-next/core/time/deconstruct_keys.rb +30 -0
  32. data/lib/ruby-next/core.rb +10 -2
  33. data/lib/ruby-next/irb.rb +2 -2
  34. data/lib/ruby-next/language/bootsnap.rb +2 -25
  35. data/lib/ruby-next/language/eval.rb +4 -4
  36. data/lib/ruby-next/language/paco_parser.rb +7 -0
  37. data/lib/ruby-next/language/paco_parsers/base.rb +47 -0
  38. data/lib/ruby-next/language/paco_parsers/comments.rb +26 -0
  39. data/lib/ruby-next/language/paco_parsers/string_literals.rb +109 -0
  40. data/lib/ruby-next/language/parser.rb +31 -6
  41. data/lib/ruby-next/language/rewriters/3.0/args_forward_leading.rb +2 -2
  42. data/lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb +1 -1
  43. data/lib/ruby-next/language/rewriters/3.1/shorthand_hash.rb +2 -1
  44. data/lib/ruby-next/language/rewriters/3.2/anonymous_restargs.rb +104 -0
  45. data/lib/ruby-next/language/rewriters/abstract.rb +57 -0
  46. data/lib/ruby-next/language/rewriters/base.rb +6 -32
  47. data/lib/ruby-next/language/rewriters/edge/it_param.rb +58 -0
  48. data/lib/ruby-next/language/rewriters/edge.rb +12 -0
  49. data/lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb +3 -0
  50. data/lib/ruby-next/language/rewriters/proposed/method_reference.rb +9 -20
  51. data/lib/ruby-next/language/rewriters/text.rb +132 -0
  52. data/lib/ruby-next/language/runtime.rb +9 -86
  53. data/lib/ruby-next/language/setup.rb +5 -2
  54. data/lib/ruby-next/language/unparser.rb +5 -0
  55. data/lib/ruby-next/language.rb +59 -12
  56. data/lib/ruby-next/pry.rb +1 -1
  57. data/lib/ruby-next/rubocop.rb +2 -0
  58. data/lib/ruby-next/utils.rb +3 -22
  59. data/lib/ruby-next/version.rb +1 -1
  60. data/lib/uby-next.rb +2 -2
  61. metadata +63 -10
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class Abstract < ::Parser::TreeRewriter
7
+ NAME = "custom-rewriter"
8
+ SYNTAX_PROBE = "1 = [}"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new(RubyNext::NEXT_VERSION)
10
+
11
+ class << self
12
+ # Returns true if the syntax is not supported
13
+ # by the current Ruby (performs syntax check, not version check)
14
+ def unsupported_syntax?
15
+ save_verbose, $VERBOSE = $VERBOSE, nil
16
+ eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
17
+ Kernel.send eval_mid, self::SYNTAX_PROBE, nil, __FILE__, __LINE__
18
+ false
19
+ rescue SyntaxError, StandardError
20
+ true
21
+ ensure
22
+ $VERBOSE = save_verbose
23
+ end
24
+
25
+ # Returns true if the syntax is supported
26
+ # by the specified version
27
+ def unsupported_version?(version)
28
+ version < self::MIN_SUPPORTED_VERSION
29
+ end
30
+
31
+ def text?
32
+ false
33
+ end
34
+
35
+ def ast?
36
+ false
37
+ end
38
+
39
+ private
40
+
41
+ def transform(source)
42
+ Language.transform(source, rewriters: [self], using: false)
43
+ end
44
+ end
45
+
46
+ def initialize(context)
47
+ @context = context
48
+ super()
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :context
54
+ end
55
+ end
56
+ end
57
+ end
@@ -13,7 +13,7 @@ module RubyNext
13
13
 
14
14
  MSG
15
15
 
16
- class Base < ::Parser::TreeRewriter
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
- class << self
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
- def transform(source)
91
- Language.transform(source, rewriters: [self], using: false)
92
- end
70
+ def self.ast?
71
+ true
93
72
  end
94
73
 
95
- attr_reader :locals
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,5 +1,8 @@
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
@@ -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 < Base
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 on_meth_ref(node)
12
- context.track! self
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
- node.updated(
24
- :send,
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 "pathname"
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
- include Utils
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
- evaluate(new_contents, path)
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
- # Patch Kernel to hijack require/require_relative/load/eval
69
- module Kernel
70
- module_function
71
-
72
- alias_method :require_without_ruby_next, :require
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
- unless system("bundle exec ruby-next nextify ./#{lib_dir} -o #{target_dir} --min-version=#{RubyNext.current_ruby_version} > /dev/null 2>&1")
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.watch_dirs.include?(dirname)
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
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- gem "ruby-next-parser", ">= 2.8.0.3"
4
- gem "unparser", ">= 0.4.7"
3
+ # Checking gem specs doesn't work in ruby.wasm
4
+ unless RUBY_PLATFORM.match?(/wasm/)
5
+ gem "ruby-next-parser", ">= 2.8.0.3"
6
+ gem "unparser", ">= 0.4.7"
7
+ end
5
8
 
6
- require "set"
9
+ require "set" # rubocop:disable Lint/RedundantRequireStatement
7
10
 
8
11
  require "ruby-next"
9
12
 
@@ -62,8 +65,15 @@ module RubyNext
62
65
  end
63
66
 
64
67
  class << self
68
+ attr_reader :include_patterns
69
+ attr_reader :exclude_patterns
70
+
71
+ def watch_dirs
72
+ warn "[DEPRECATED] Use `RubyNext::Language.include_patterns` instead of `RubyNext::Language.watch_dirs`"
73
+ @watch_dirs
74
+ end
75
+
65
76
  attr_accessor :rewriters
66
- attr_reader :watch_dirs
67
77
 
68
78
  attr_accessor :strategy
69
79
 
@@ -95,14 +105,17 @@ module RubyNext
95
105
  end
96
106
 
97
107
  def transform(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, context: TransformContext.new)
108
+ text_rewriters, ast_rewriters = rewriters.partition(&:text?)
109
+
98
110
  retried = 0
99
- new_source = nil
111
+ new_source = text_rewrite(source, rewriters: text_rewriters, using: using, context: context)
112
+
100
113
  begin
101
114
  new_source =
102
115
  if mode == :rewrite
103
- rewrite(source, rewriters: rewriters, using: using, context: context)
116
+ rewrite(new_source, rewriters: ast_rewriters, using: using, context: context)
104
117
  else
105
- regenerate(source, rewriters: rewriters, using: using, context: context)
118
+ regenerate(new_source, rewriters: ast_rewriters, using: using, context: context)
106
119
  end
107
120
  rescue Unparser::UnknownNodeError => err
108
121
  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 +132,17 @@ module RubyNext
119
132
  Core.inject! new_source.dup
120
133
  end
121
134
 
135
+ def target_dir?(dirname)
136
+ # fnmatch? requires a file name, not a folder
137
+ fname = File.join(dirname, "x.rb")
138
+
139
+ include_patterns.any? { |pattern| File.fnmatch?(pattern, fname) } &&
140
+ exclude_patterns.none? { |pattern| File.fnmatch?(pattern, fname) }
141
+ end
142
+
122
143
  def transformable?(path)
123
- watch_dirs.any? { |dir| path.start_with?(dir) }
144
+ include_patterns.any? { |pattern| File.fnmatch?(pattern, path) } &&
145
+ exclude_patterns.none? { |pattern| File.fnmatch?(pattern, path) }
124
146
  end
125
147
 
126
148
  # Rewriters required for the current version
@@ -165,14 +187,36 @@ module RubyNext
165
187
  end
166
188
  end
167
189
 
190
+ def text_rewrite(source, rewriters:, using:, context:)
191
+ rewriters.inject(source) do |src, rewriter|
192
+ rewriter.new(context).rewrite(src)
193
+ end.then do |new_source|
194
+ next source unless context.dirty?
195
+
196
+ new_source
197
+ end
198
+ end
199
+
168
200
  attr_writer :watch_dirs
201
+ attr_writer :include_patterns, :exclude_patterns
169
202
  end
170
203
 
171
204
  self.rewriters = []
172
- self.watch_dirs = %w[app lib spec test].map { |path| File.join(Dir.pwd, path) }
205
+ self.watch_dirs = [].tap do |dirs|
206
+ # For backward compatibility
207
+ dirs.define_singleton_method(:<<) do |dir|
208
+ super(dir)
209
+ RubyNext::Language.include_patterns << File.join(dir, "*.rb")
210
+ end
211
+ end
212
+
213
+ self.include_patterns = %w[app lib spec test].map { |path| File.join(Dir.pwd, path, "*.rb") }
214
+ self.exclude_patterns = %w[vendor/bundle].map { |path| File.join(Dir.pwd, path, "*") }
173
215
  self.mode = ENV.fetch("RUBY_NEXT_TRANSPILE_MODE", "rewrite").to_sym
174
216
 
217
+ require "ruby-next/language/rewriters/abstract"
175
218
  require "ruby-next/language/rewriters/base"
219
+ require "ruby-next/language/rewriters/text"
176
220
 
177
221
  require "ruby-next/language/rewriters/2.1/numeric_literals"
178
222
  rewriters << Rewriters::NumericLiterals
@@ -212,7 +256,7 @@ module RubyNext
212
256
  rewriters << Rewriters::InPattern
213
257
 
214
258
  require "ruby-next/language/rewriters/3.0/endless_method"
215
- RubyNext::Language.rewriters << RubyNext::Language::Rewriters::EndlessMethod
259
+ rewriters << RubyNext::Language::Rewriters::EndlessMethod
216
260
 
217
261
  require "ruby-next/language/rewriters/3.1/oneline_pattern_parensless"
218
262
  rewriters << Rewriters::OnelinePatternParensless
@@ -232,16 +276,19 @@ module RubyNext
232
276
  require "ruby-next/language/rewriters/3.1/shorthand_hash"
233
277
  rewriters << RubyNext::Language::Rewriters::ShorthandHash
234
278
 
279
+ require "ruby-next/language/rewriters/3.2/anonymous_restargs"
280
+ rewriters << RubyNext::Language::Rewriters::AnonymousRestArgs
281
+
235
282
  # Put endless range in the end, 'cause Parser fails to parse it in
236
283
  # pattern matching
237
284
  require "ruby-next/language/rewriters/2.6/endless_range"
238
285
  rewriters << Rewriters::EndlessRange
239
286
 
240
- if ENV["RUBY_NEXT_EDGE"] == "1"
287
+ if RubyNext.edge_syntax?
241
288
  require "ruby-next/language/rewriters/edge"
242
289
  end
243
290
 
244
- if ENV["RUBY_NEXT_PROPOSED"] == "1"
291
+ if RubyNext.proposed_syntax?
245
292
  require "ruby-next/language/rewriters/proposed"
246
293
  end
247
294
  end
data/lib/ruby-next/pry.rb CHANGED
@@ -64,7 +64,7 @@ end)
64
64
  Pry::Code.singleton_class.prepend(Module.new do
65
65
  def complete_expression?(str)
66
66
  silence_stderr do
67
- ::Parser::RubyNext.parse(str)
67
+ ::RubyNext::Language.parser_class.parse(str)
68
68
  end
69
69
 
70
70
  true