ruby-next-core 0.9.0.pre → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/README.md +29 -8
- data/bin/transform +9 -1
- data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +198 -0
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +125 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +107 -0
- data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
- data/lib/ruby-next.rb +8 -6
- data/lib/ruby-next/cli.rb +2 -2
- data/lib/ruby-next/commands/core_ext.rb +2 -2
- data/lib/ruby-next/commands/nextify.rb +64 -22
- data/lib/ruby-next/core.rb +31 -20
- data/lib/ruby-next/core/array/deconstruct.rb +9 -9
- data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
- data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
- data/lib/ruby-next/core/enumerable/filter.rb +8 -8
- data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
- data/lib/ruby-next/core/enumerable/tally.rb +7 -7
- data/lib/ruby-next/core/enumerator/produce.rb +12 -12
- data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
- data/lib/ruby-next/core/hash/except.rb +11 -0
- data/lib/ruby-next/core/hash/merge.rb +8 -8
- data/lib/ruby-next/core/kernel/then.rb +2 -2
- data/lib/ruby-next/core/proc/compose.rb +11 -11
- data/lib/ruby-next/core/string/split.rb +6 -6
- data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
- data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
- data/lib/ruby-next/core/symbol/end_with.rb +4 -4
- data/lib/ruby-next/core/symbol/start_with.rb +4 -4
- data/lib/ruby-next/core/time/ceil.rb +6 -5
- data/lib/ruby-next/core/time/floor.rb +4 -4
- data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
- data/lib/ruby-next/core_ext.rb +1 -1
- data/lib/ruby-next/language.rb +32 -6
- data/lib/ruby-next/language/eval.rb +10 -8
- data/lib/ruby-next/language/proposed.rb +3 -0
- data/lib/ruby-next/language/rewriters/args_forward.rb +24 -20
- data/lib/ruby-next/language/rewriters/base.rb +6 -2
- data/lib/ruby-next/language/rewriters/endless_method.rb +26 -3
- data/lib/ruby-next/language/rewriters/endless_range.rb +1 -0
- data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
- data/lib/ruby-next/language/rewriters/method_reference.rb +2 -1
- data/lib/ruby-next/language/rewriters/numbered_params.rb +1 -0
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +105 -13
- data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +74 -11
- data/lib/ruby-next/language/rewriters/runtime.rb +6 -0
- data/lib/ruby-next/language/rewriters/runtime/dir.rb +32 -0
- data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
- data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
- data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
- data/lib/ruby-next/language/runtime.rb +3 -2
- data/lib/ruby-next/logging.rb +1 -1
- data/lib/ruby-next/rubocop.rb +15 -9
- data/lib/ruby-next/setup_self.rb +22 -0
- data/lib/ruby-next/utils.rb +30 -0
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next.rb +8 -4
- metadata +25 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b97da767efae69a9faa9ed2b4c4a3daf9c38a5b70768502b013692b7a3c04a50
|
4
|
+
data.tar.gz: 05ba2f4c4677c40d46a451a68a94e434b54d4e9daa9a2af51cbd508ef0a198ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef678d9b8ed431a8a8f005b00c445da56096c39a3c9ffc1ce88708c4b1280e630cb5c58a63a154381c384b54968ec19b3c88a4a49665b3503bd5df62f454a168
|
7
|
+
data.tar.gz: a25ebf4e0c3456213a1f682d27df031a6add5bc117800026d8b0f9b48cacb49e72a4b97115f9b1b1acf058ca3f6974dfa94402498a58a19ac532916eb9a81199
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,51 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.10.1 (2020-09-09)
|
6
|
+
|
7
|
+
- Fix regression when `nextify` produces incorrect files for 2.7. ([@palkan][])
|
8
|
+
|
9
|
+
## 0.10.0 (2020-09-02)
|
10
|
+
|
11
|
+
- Add proposed shorthand Hash syntax. ([@palkan][])
|
12
|
+
|
13
|
+
You can try it: `x = 1; y = 2; data = {x, y}`.
|
14
|
+
|
15
|
+
- Add leading argument support to args forwarding. ([@palkan][])
|
16
|
+
|
17
|
+
`def a(...) b(1, ...); end`.
|
18
|
+
|
19
|
+
- Add `Hash#except`. ([@palkan][])
|
20
|
+
|
21
|
+
`{a: 1, b: 2}.except(:a) == {b: 2}`
|
22
|
+
|
23
|
+
- Add find pattern support. ([@palkan][])
|
24
|
+
|
25
|
+
Now you can do: `[0, 1, 2] in [*, 1 => a, *c]`.
|
26
|
+
|
27
|
+
- Add Ruby 2.2 support. ([@palkan][])
|
28
|
+
|
29
|
+
With support for safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`).
|
30
|
+
|
31
|
+
## 0.9.2 (2020-06-24)
|
32
|
+
|
33
|
+
- Support passing rewriters to CLI. ([@sl4vr][])
|
34
|
+
|
35
|
+
Use `nextify --list-rewriters` to view all available rewriters.
|
36
|
+
Use `nextify` with `--rewrite=REWRITERS...` option to specify which particular rewriters to use.
|
37
|
+
|
38
|
+
## 0.9.1 (2020-06-05)
|
39
|
+
|
40
|
+
- Keep `ruby-next` version in sync with `ruby-next-core`. ([@palkan][])
|
41
|
+
|
42
|
+
Require `ruby-next-core` of the same version as `ruby-next`.
|
43
|
+
|
44
|
+
## 0.9.0 (2020-06-04)
|
45
|
+
|
46
|
+
- Add Ruby 2.3 support. ([@palkan][])
|
47
|
+
|
48
|
+
- Remove stale transpiled files when running `ruby-next nextify`. ([@palkan][])
|
49
|
+
|
5
50
|
- Add Ruby 2.4 support. ([@palkan][])
|
6
51
|
|
7
52
|
APIs for <2.5 must be backported via [backports][] gem. Refinements are not supported.
|
@@ -183,3 +228,4 @@ p a #=> 1
|
|
183
228
|
|
184
229
|
[@palkan]: https://github.com/palkan
|
185
230
|
[backports]: https://github.com/marcandre/backports
|
231
|
+
[@sl4vr]: https://github.com/sl4vr
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
[![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/ruby-next-cli-rewriters.html#task)
|
1
2
|
[![Gem Version](https://badge.fury.io/rb/ruby-next.svg)](https://rubygems.org/gems/ruby-next) [![Build](https://github.com/ruby-next/ruby-next/workflows/Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions)
|
2
|
-
[![JRuby Build](https://github.com/ruby-next/ruby-next/workflows/JRuby%20Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions)
|
3
|
+
[![JRuby Build](https://github.com/ruby-next/ruby-next/workflows/JRuby%20Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions?query=workflow%3A%22TruffleRuby+Build%22)
|
4
|
+
[![TruffleRuby Build](https://github.com/ruby-next/ruby-next/workflows/TruffleRuby%20Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions?query=workflow%3A%22TruffleRuby+Build%22)
|
3
5
|
|
4
6
|
# Ruby Next
|
5
7
|
|
@@ -35,6 +37,8 @@ Read more about the motivation behind the Ruby Next in this post: [Ruby Next: Ma
|
|
35
37
|
- Ruby gems
|
36
38
|
- [anyway_config](https://github.com/palkan/anyway_config)
|
37
39
|
- [graphql-fragment_cache](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache)
|
40
|
+
- Rails applications
|
41
|
+
- [anycable_rails_demo](https://github.com/anycable/anycable_rails_demo)
|
38
42
|
- mruby
|
39
43
|
- [ACLI](https://github.com/palkan/acli)
|
40
44
|
|
@@ -66,7 +70,7 @@ Core provides **polyfills** for Ruby core classes APIs via Refinements (default
|
|
66
70
|
Language is responsible for **transpiling** edge Ruby syntax into older versions. It could be done
|
67
71
|
programmatically or via CLI. It also could be done in runtime.
|
68
72
|
|
69
|
-
Currently, Ruby Next supports Ruby versions 2.
|
73
|
+
Currently, Ruby Next supports Ruby versions 2.2+, including JRuby 9.2.8+ and TruffleRuby 20.1+ (with some limitations). Support for EOL versions (<2.5) slightly differs though ([see below](#using-with-eol-rubies)).
|
70
74
|
|
71
75
|
Please, [open an issue](https://github.com/ruby-next/ruby-next/issues/new/choose) or join the discussion in the existing ones if you would like us to support older Ruby versions.
|
72
76
|
|
@@ -137,6 +141,8 @@ The following _rule of thumb_ is recommended when choosing between refinements a
|
|
137
141
|
- Using core extensions could be considered for application development (no need to think about `using RubyNext`); this approach could potentially lead to conflicts with dependencies (if these dependencies are not using refinements 🙂)
|
138
142
|
- Use core extensions if refinements are not supported by your platform
|
139
143
|
|
144
|
+
**NOTE:** _Edge_ APIs (i.e., from the Ruby's master branch) are included by default.
|
145
|
+
|
140
146
|
[**The list of supported APIs.**][features_core]
|
141
147
|
|
142
148
|
## Transpiling
|
@@ -197,12 +203,15 @@ It has the following interface:
|
|
197
203
|
```sh
|
198
204
|
$ ruby-next nextify
|
199
205
|
Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
|
200
|
-
-o, --output=OUTPUT Specify output directory or file or stdout
|
206
|
+
-o, --output=OUTPUT Specify output directory or file or stdout
|
201
207
|
--min-version=VERSION Specify the minimum Ruby version to support
|
202
208
|
--single-version Only create one version of a file (for the earliest Ruby version)
|
203
|
-
--
|
209
|
+
--edge Enable edge (master) Ruby features
|
210
|
+
--proposed Enable proposed/experimental Ruby features
|
204
211
|
--transpile-mode=MODE Transpiler mode (ast or rewrite). Default: ast
|
205
212
|
--[no-]refine Do not inject `using RubyNext`
|
213
|
+
--list-rewriters List available rewriters
|
214
|
+
--rewrite=REWRITERS... Specify particular Ruby features to rewrite
|
206
215
|
-h, --help Print help
|
207
216
|
-V Turn on verbose mode
|
208
217
|
--dry-run Print verbose output without generating files
|
@@ -390,6 +399,12 @@ RUBYOPT="-ruby-next" ruby my_ruby_script.rb
|
|
390
399
|
ruby -ruby-next -e "puts [2, 4, 5].tally"
|
391
400
|
```
|
392
401
|
|
402
|
+
**NOTE:** running Ruby scripts directly or executing code via `-e` option is not supported in TruffleRuby. You can still use `-ruby-next` to transpile required files, e.g.:
|
403
|
+
|
404
|
+
```sh
|
405
|
+
ruby -ruby-next -r my_ruby_script.rb -e "puts my_method"
|
406
|
+
```
|
407
|
+
|
393
408
|
## Logging and debugging
|
394
409
|
|
395
410
|
Ruby Next prints some debugging information when fails to load a file in the runtime mode (and fallbacks to the built-in loading mechanism).
|
@@ -414,6 +429,8 @@ You must set `TargetRubyVersion: next` to make RuboCop use a Ruby Next parser.
|
|
414
429
|
|
415
430
|
Alternatively, you can load the patch from the command line by running: `rubocop -r ruby-next/rubocop ...`.
|
416
431
|
|
432
|
+
We recommend using the latest RuboCop version, 'cause it has support for new nodes built-in.
|
433
|
+
|
417
434
|
Also, when pre-transpiling source code with `ruby-next nextify`, we suggest ignoring the transpiled files:
|
418
435
|
|
419
436
|
```yml
|
@@ -426,9 +443,7 @@ AllCops:
|
|
426
443
|
|
427
444
|
## Using with EOL Rubies
|
428
445
|
|
429
|
-
|
430
|
-
|
431
|
-
We currently provide experimental support for Ruby 2.4. Work on older Rubies (down to 2.2) is in progress.
|
446
|
+
We currently provide support for Ruby 2.2, 2.3 and 2.4.
|
432
447
|
|
433
448
|
Ruby Next itself relies on 2.5 features and contains polyfills only for version 2.5+ (and that won't change).
|
434
449
|
Thus, to make it work with <2.5 we need to backport some APIs ourselves.
|
@@ -458,6 +473,8 @@ RUBY_NEXT_CORE_STRATEGY=backports ruby-next nextify lib/
|
|
458
473
|
|
459
474
|
**NOTE:** Make sure you have `backports` gem installed globally or added to your bundle (if you're using `bundle exec ruby-next ...`).
|
460
475
|
|
476
|
+
**NOTE:** For Ruby 2.2, safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`) support is provided.
|
477
|
+
|
461
478
|
## Proposed and edge features
|
462
479
|
|
463
480
|
Ruby Next aims to bring edge and proposed features to Ruby community before they (hopefully) reach an official Ruby release.
|
@@ -491,12 +508,16 @@ require "ruby-next/language/runtime"
|
|
491
508
|
|
492
509
|
- "Endless" method definition (`def foo() = 42`) ([#16746](https://bugs.ruby-lang.org/issues/16746)).
|
493
510
|
|
494
|
-
- Right-hand assignment (`13.divmod(5) => a,b`) ([#15921](https://bugs.ruby-lang.org/issues/15921))
|
511
|
+
- Right-hand assignment (`13.divmod(5) => a,b`) ([#15921](https://bugs.ruby-lang.org/issues/15921)).
|
512
|
+
|
513
|
+
- Find pattern (`[0, 1, 2] in [*, 1 => a, *c]`) ([#16828](https://bugs.ruby-lang.org/issues/16828)).
|
495
514
|
|
496
515
|
### Supported proposed features
|
497
516
|
|
498
517
|
- _Method reference_ operator (`.:`) ([#13581](https://bugs.ruby-lang.org/issues/13581)).
|
499
518
|
|
519
|
+
- Shorthand Hash notation (`data = {x, y}`) ([#15236](https://bugs.ruby-lang.org/issues/15236)).
|
520
|
+
|
500
521
|
## Contributing
|
501
522
|
|
502
523
|
Bug reports and pull requests are welcome on GitHub at [https://github.com/ruby-next/ruby-next](ttps://github.com/ruby-next/ruby-next).
|
data/bin/transform
CHANGED
@@ -9,6 +9,7 @@ ENV["RUBY_NEXT_EDGE"] = "1"
|
|
9
9
|
ENV["RUBY_NEXT_PROPOSED"] = "1"
|
10
10
|
|
11
11
|
require "ruby-next/language"
|
12
|
+
require "ruby-next/language/rewriters/runtime"
|
12
13
|
|
13
14
|
contents =
|
14
15
|
if File.exist?(ARGV[0])
|
@@ -17,4 +18,11 @@ contents =
|
|
17
18
|
ARGV[0]
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
+
opts =
|
22
|
+
if ARGV[1] && ARGV[1] == "--current"
|
23
|
+
{rewriters: RubyNext::Language.current_rewriters}
|
24
|
+
else
|
25
|
+
{}
|
26
|
+
end
|
27
|
+
|
28
|
+
puts RubyNext::Language.transform(contents, **opts)
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "pathname"
|
5
|
+
|
6
|
+
module RubyNext
|
7
|
+
module Commands
|
8
|
+
class CoreExt < Base
|
9
|
+
using RubyNext
|
10
|
+
|
11
|
+
attr_reader :out_path, :min_version, :names, :list, :filter, :original_command
|
12
|
+
alias list? list
|
13
|
+
|
14
|
+
def run
|
15
|
+
log "Select core extensions for Ruby v#{min_version}" \
|
16
|
+
"#{filter ? " and matching #{filter.inspect}" : ""}"
|
17
|
+
|
18
|
+
matching_patches.then do |patches|
|
19
|
+
next print_list(patches) if list?
|
20
|
+
generate_core_ext(patches)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse!(args)
|
25
|
+
print_help = false
|
26
|
+
@min_version = MIN_SUPPORTED_VERSION
|
27
|
+
@original_command = "ruby-next core_ext #{args.join(" ")}"
|
28
|
+
@names = []
|
29
|
+
@list = false
|
30
|
+
@out_path = File.join(Dir.pwd, "core_ext.rb")
|
31
|
+
|
32
|
+
optparser = base_parser do |opts|
|
33
|
+
opts.banner = "Usage: ruby-next core_ext [options]"
|
34
|
+
|
35
|
+
opts.on("-o", "--output=OUTPUT", "Specify output file or stdout (default: ./core_ext.rb)") do |val|
|
36
|
+
@out_path = val
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-l", "--list", "List all available extensions") do
|
40
|
+
@list = true
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("--min-version=VERSION", "Specify the minimum Ruby version to support") do |val|
|
44
|
+
@min_version = Gem::Version.new(val)
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("-n", "--name=NAME", "Filter extensions by name") do |val|
|
48
|
+
names << val
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on("-h", "--help", "Print help") do
|
52
|
+
print_help = true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
optparser.parse!(args)
|
57
|
+
|
58
|
+
if print_help
|
59
|
+
$stdout.puts optparser.help
|
60
|
+
exit 0
|
61
|
+
end
|
62
|
+
|
63
|
+
@filter = /(#{names.join("|")})/i unless names.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def matching_patches
|
69
|
+
RubyNext::Core.patches.extensions
|
70
|
+
.values
|
71
|
+
.flatten
|
72
|
+
.select do |patch|
|
73
|
+
next if min_version && Gem::Version.new(patch.version) <= min_version
|
74
|
+
next if filter && !filter.match?(patch.name)
|
75
|
+
true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_list(patches)
|
80
|
+
grouped_patches = patches.group_by(&:version).sort_by(&:first)
|
81
|
+
grouped_patches.each do |(group, patches)|
|
82
|
+
$stdout.puts "#{group} extensions:\n"
|
83
|
+
$stdout.puts patches.sort_by(&:name).map { |patch| " - #{patch.name}" }.join("\n")
|
84
|
+
$stdout.puts "\n"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def generate_core_ext(patches)
|
89
|
+
grouped_patches = patches.group_by(&:mod).sort_by { |(mod, patch)| mod.singleton_class? ? mod.inspect : mod.name }
|
90
|
+
|
91
|
+
buffer = []
|
92
|
+
|
93
|
+
buffer << "# frozen_string_literal: true\n"
|
94
|
+
|
95
|
+
buffer << generation_meta
|
96
|
+
|
97
|
+
grouped_patches.each do |mod, patches|
|
98
|
+
singleton = mod.singleton_class?
|
99
|
+
extend_name = singleton ? patches.first.singleton.name : mod.name
|
100
|
+
prepend_name = singleton ? "#{patches.first.singleton.name}.singleton_class" : mod.name
|
101
|
+
|
102
|
+
prepended, extended = patches.partition(&:prepend?)
|
103
|
+
|
104
|
+
prepended.map do |patch|
|
105
|
+
name = "RubyNext::Core::#{patch.name}"
|
106
|
+
|
107
|
+
buffer << <<-RUBY
|
108
|
+
module #{name}
|
109
|
+
#{indent_and_trim(patch.body)}
|
110
|
+
end
|
111
|
+
#{prepend_name}.prepend #{name}
|
112
|
+
RUBY
|
113
|
+
|
114
|
+
name
|
115
|
+
end
|
116
|
+
|
117
|
+
class_or_module = mod.is_a?(Class) ? "class" : "module"
|
118
|
+
|
119
|
+
buffer << "#{class_or_module} #{extend_name}"
|
120
|
+
|
121
|
+
buffer << " class << self" if singleton
|
122
|
+
|
123
|
+
indent_size = singleton ? 4 : 2
|
124
|
+
|
125
|
+
buffer << extended.map do |patch|
|
126
|
+
indent_and_trim(patch.body, indent_size)
|
127
|
+
end.join("\n\n")
|
128
|
+
|
129
|
+
buffer << " end" if singleton
|
130
|
+
|
131
|
+
buffer << "end\n"
|
132
|
+
end
|
133
|
+
|
134
|
+
contents = buffer.join("\n")
|
135
|
+
|
136
|
+
return $stdout.puts(contents) if out_path == "stdout"
|
137
|
+
|
138
|
+
unless CLI.dry_run?
|
139
|
+
FileUtils.mkdir_p File.dirname(out_path)
|
140
|
+
File.write(out_path, contents)
|
141
|
+
end
|
142
|
+
|
143
|
+
log "Generated: #{out_path}"
|
144
|
+
end
|
145
|
+
|
146
|
+
def generation_meta
|
147
|
+
<<-MSG
|
148
|
+
# Generated by Ruby Next v#{RubyNext::VERSION} using the following command:
|
149
|
+
#
|
150
|
+
# #{original_command}
|
151
|
+
#
|
152
|
+
MSG
|
153
|
+
end
|
154
|
+
|
155
|
+
def indent_and_trim(src, size = 2)
|
156
|
+
new_src = src.dup
|
157
|
+
# indent code using <size> spaces
|
158
|
+
new_src.gsub!(/^/, " " * size)
|
159
|
+
# remove empty lines
|
160
|
+
new_src.gsub!(/^\s+$/, "")
|
161
|
+
# remove traling blank lines
|
162
|
+
new_src.delete_suffix!("\n")
|
163
|
+
new_src
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "pathname"
|
5
|
+
|
6
|
+
require "ruby-next/language"
|
7
|
+
|
8
|
+
module RubyNext
|
9
|
+
module Commands
|
10
|
+
class Nextify < Base
|
11
|
+
using RubyNext
|
12
|
+
|
13
|
+
attr_reader :lib_path, :paths, :out_path, :min_version, :single_version, :specified_rewriters
|
14
|
+
|
15
|
+
def run
|
16
|
+
log "RubyNext core strategy: #{RubyNext::Core.strategy}"
|
17
|
+
log "RubyNext transpile mode: #{RubyNext::Language.mode}"
|
18
|
+
|
19
|
+
remove_rbnext!
|
20
|
+
|
21
|
+
@min_version ||= MIN_SUPPORTED_VERSION
|
22
|
+
|
23
|
+
paths.each do |path|
|
24
|
+
contents = File.read(path)
|
25
|
+
transpile path, contents
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse!(args)
|
30
|
+
print_help = false
|
31
|
+
print_rewriters = false
|
32
|
+
rewriter_names = []
|
33
|
+
@single_version = false
|
34
|
+
|
35
|
+
optparser = base_parser do |opts|
|
36
|
+
opts.banner = "Usage: ruby-next nextify DIRECTORY_OR_FILE [options]"
|
37
|
+
|
38
|
+
opts.on("-o", "--output=OUTPUT", "Specify output directory or file or stdout") do |val|
|
39
|
+
@out_path = val
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("--min-version=VERSION", "Specify the minimum Ruby version to support") do |val|
|
43
|
+
@min_version = Gem::Version.new(val)
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on("--single-version", "Only create one version of a file (for the earliest Ruby version)") do
|
47
|
+
@single_version = true
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on("--edge", "Enable edge (master) Ruby features") do |val|
|
51
|
+
require "ruby-next/language/edge"
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on("--proposed", "Enable proposed/experimental Ruby features") do |val|
|
55
|
+
require "ruby-next/language/proposed"
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on(
|
59
|
+
"--transpile-mode=MODE",
|
60
|
+
"Transpiler mode (ast or rewrite). Default: ast"
|
61
|
+
) do |val|
|
62
|
+
Language.mode = val.to_sym
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on("--[no-]refine", "Do not inject `using RubyNext`") do |val|
|
66
|
+
Core.strategy = :core_ext unless val
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on("--list-rewriters", "List available rewriters") do |val|
|
70
|
+
print_rewriters = true
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on("--rewrite=REWRITERS...", "Specify particular Ruby features to rewrite") do |val|
|
74
|
+
rewriter_names << val
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.on("-h", "--help", "Print help") do
|
78
|
+
print_help = true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
optparser.parse!(args)
|
83
|
+
|
84
|
+
@lib_path = args[0]
|
85
|
+
|
86
|
+
if print_help
|
87
|
+
$stdout.puts optparser.help
|
88
|
+
exit 0
|
89
|
+
end
|
90
|
+
|
91
|
+
if print_rewriters
|
92
|
+
Language.rewriters.each do |rewriter|
|
93
|
+
$stdout.puts "#{rewriter::NAME} (\"#{rewriter::SYNTAX_PROBE}\")"
|
94
|
+
end
|
95
|
+
exit 0
|
96
|
+
end
|
97
|
+
|
98
|
+
unless ((!lib_path.nil?) || nil) && lib_path.then(&File.method(:exist?))
|
99
|
+
$stdout.puts "Path not found: #{lib_path}"
|
100
|
+
$stdout.puts optparser.help
|
101
|
+
exit 2
|
102
|
+
end
|
103
|
+
|
104
|
+
if rewriter_names.any? && min_version
|
105
|
+
$stdout.puts "--rewrite cannot be used with --min-version simultaneously"
|
106
|
+
exit 2
|
107
|
+
end
|
108
|
+
|
109
|
+
@specified_rewriters =
|
110
|
+
if rewriter_names.any?
|
111
|
+
begin
|
112
|
+
Language.select_rewriters(*rewriter_names)
|
113
|
+
rescue Language::RewriterNotFoundError => error
|
114
|
+
$stdout.puts error.message
|
115
|
+
$stdout.puts "Try --list-rewriters to see list of available rewriters"
|
116
|
+
exit 2
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
@paths =
|
121
|
+
if File.directory?(lib_path)
|
122
|
+
Dir[File.join(lib_path, "**/*.rb")]
|
123
|
+
elsif File.file?(lib_path)
|
124
|
+
[lib_path].tap do |_|
|
125
|
+
@lib_path = File.dirname(lib_path)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def transpile(path, contents, version: min_version)
|
133
|
+
rewriters = specified_rewriters || Language.rewriters.select { |rw| rw.unsupported_version?(version) }
|
134
|
+
|
135
|
+
context = Language::TransformContext.new
|
136
|
+
|
137
|
+
new_contents = Language.transform contents, context: context, rewriters: rewriters
|
138
|
+
|
139
|
+
return unless context.dirty?
|
140
|
+
|
141
|
+
versions = context.sorted_versions
|
142
|
+
version = versions.shift
|
143
|
+
|
144
|
+
# First, store already transpiled contents in the minimum required version dir
|
145
|
+
save new_contents, path, version
|
146
|
+
|
147
|
+
return if versions.empty? || single_version?
|
148
|
+
|
149
|
+
# Then, generate the source code for the next version
|
150
|
+
transpile path, contents, version: version
|
151
|
+
end
|
152
|
+
|
153
|
+
def save(contents, path, version)
|
154
|
+
return $stdout.puts(contents) if stdout?
|
155
|
+
|
156
|
+
paths = [Pathname.new(path).relative_path_from(Pathname.new(lib_path))]
|
157
|
+
|
158
|
+
paths.unshift(version.segments[0..1].join(".")) unless single_version?
|
159
|
+
|
160
|
+
next_path =
|
161
|
+
if next_dir_path.end_with?(".rb")
|
162
|
+
out_path
|
163
|
+
else
|
164
|
+
File.join(next_dir_path, *paths)
|
165
|
+
end
|
166
|
+
|
167
|
+
unless CLI.dry_run?
|
168
|
+
FileUtils.mkdir_p File.dirname(next_path)
|
169
|
+
|
170
|
+
File.write(next_path, contents)
|
171
|
+
end
|
172
|
+
|
173
|
+
log "Generated: #{next_path}"
|
174
|
+
end
|
175
|
+
|
176
|
+
def remove_rbnext!
|
177
|
+
return if CLI.dry_run? || stdout?
|
178
|
+
|
179
|
+
return unless File.directory?(next_dir_path)
|
180
|
+
|
181
|
+
log "Remove old files: #{next_dir_path}"
|
182
|
+
FileUtils.rm_r(next_dir_path)
|
183
|
+
end
|
184
|
+
|
185
|
+
def next_dir_path
|
186
|
+
@next_dir_path ||= (out_path || File.join(lib_path, RUBY_NEXT_DIR))
|
187
|
+
end
|
188
|
+
|
189
|
+
def stdout?
|
190
|
+
out_path == "stdout"
|
191
|
+
end
|
192
|
+
|
193
|
+
def single_version?
|
194
|
+
single_version || specified_rewriters
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|