ruby-next-core 0.15.3 → 1.0.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.
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