toys 0.19.1 → 0.20.0
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 +54 -0
- data/builtins/system/test.rb +191 -108
- data/builtins/system/zsh-completion.rb +91 -0
- data/core-docs/toys/core.rb +1 -1
- data/core-docs/toys/errors.rb +4 -3
- data/core-docs/toys/standard_mixins/bundler.rb +17 -3
- data/core-docs/toys/standard_mixins/exec.rb +2 -1
- data/core-docs/toys/utils/completion_engine.rb +33 -5
- data/core-docs/toys/utils/exec.rb +36 -11
- data/docs/guide.md +930 -806
- data/lib/toys/templates/clean.rb +119 -43
- data/lib/toys/templates/minitest.rb +432 -73
- data/lib/toys/templates/rspec.rb +305 -85
- data/lib/toys/version.rb +1 -1
- data/share/zsh-completion-remove.sh +6 -0
- data/share/zsh-completion.sh +25 -0
- metadata +9 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 91bb0564850a2dc5b0742038d542cbb1239b2bcdef5db7f256c8deceac580e82
|
|
4
|
+
data.tar.gz: 894bdc6c036528a156fecdd79ee65ec4d099281552c5fe3f6ec6488627985fb2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 576e41a7ffda908b16b696158bf7baa00b0a9fe065425847e24ee5db01082dbbc61119362de0e14a98f00d5a0edb727ec42f5428ab110b3be5a0cf318041d628
|
|
7
|
+
data.tar.gz: 56e9a65cbc25bd8c40b30885a61399fc7927b047b2f2edffbea0533a642b969734d52303a01637efc9a38e1300d1f6ed3697fa61515a6522d54148511b1d2a1d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### v0.20.0 / 2026-03-09
|
|
4
|
+
|
|
5
|
+
Toys 0.20 is a major release with several new features and a number of fixes, including a few minor breaking changes.
|
|
6
|
+
|
|
7
|
+
Changes in the `:minitest` template:
|
|
8
|
+
|
|
9
|
+
* BREAKING CHANGE: Tools produced by the `:minitest` template default to looking for test files of the forms `*_test.rb` and `test_*.rb` instead of `test*.rb`.
|
|
10
|
+
* NEW: The `:minitest` template supports installing specified gems without using bundler.
|
|
11
|
+
* NEW: Tools produced by the `:minitest` template provide command line arguments for overriding the gem installation or bundler settings.
|
|
12
|
+
* NEW: Tools produced by the `:minitest` template recognize `--include` as an alias for `--name`. This matches recent versions of minitest.
|
|
13
|
+
* NEW: The `:minitest` template generates more comprehensive documentation.
|
|
14
|
+
* NEW: The minitest tool template now requires "minitest/autorun" before loading tests, so tests don't have to do so themselves.
|
|
15
|
+
* BREAKING API CHANGE: Toys::Templates::Minitest::DEFAULT_GEM_VERSION_REQUIREMENTS is now a hash that covers multiple gems rather than just the minitest gem
|
|
16
|
+
* FIXED: The minitest template no longer exceeds command line length limits if the list of test files is extremely long.
|
|
17
|
+
|
|
18
|
+
New functionality in the `:rspec` template:
|
|
19
|
+
|
|
20
|
+
* NEW: The `:rspec` template supports installing specified gems without using bundler.
|
|
21
|
+
* NEW: Tools produced by the `:rspec` template provide command line arguments for overriding the gem installation or bundler settings.
|
|
22
|
+
* NEW: Tools produced by the `:rspec` template recognize the `--example-matches` flag, and can handle multiple `--example` and `--tag` flags.
|
|
23
|
+
* NEW: The `:rspec` template generates more comprehensive documentation.
|
|
24
|
+
* BREAKING API CHANGE: Toys::Templates::Rspec::DEFAULT_GEM_VERSION_REQUIREMENTS is now a hash that covers potentially multiple gems
|
|
25
|
+
|
|
26
|
+
New functionality in the `:clean` template:
|
|
27
|
+
|
|
28
|
+
* The `:clean` template supports specifying certain gitignored files to preserve.
|
|
29
|
+
* The `:clean` template is more robust against concurrent modification and works better with large git repos.
|
|
30
|
+
|
|
31
|
+
New functionality in the `system test` builtin tool:
|
|
32
|
+
|
|
33
|
+
* BREAKING CHANGE: The `system test` builtin looks for test files of the form `*_test.rb` in addition to `test_*.rb`.
|
|
34
|
+
* The `system test` builtin uses bundler to install gems if a Gemfile is present in the `.test` directory.
|
|
35
|
+
* The `system test` builtin supports flags that can specify arbitrary gems to load.
|
|
36
|
+
|
|
37
|
+
Updates to the Exec mixin and library:
|
|
38
|
+
|
|
39
|
+
* NEW: The new `Toys::Utils::Exec::Result#effective_result` method provides a reasonable integer result code even when a process terminates via signal or fails to start at all.
|
|
40
|
+
* BREAKING API CHANGE: `:cli` is no longer a legal config option.
|
|
41
|
+
* BREAKING API CHANGE: An options hash is no longer passed to the proc when executing a proc using a fork.
|
|
42
|
+
* BREAKING API CHANGE: Passing an IO object as an input or output stream no longer closes it afterward.
|
|
43
|
+
* BREAKING API CHANGE: `Toys::Utils::Exec::Controller#result` no longer preemptively (and prematurely) closes the controller input stream
|
|
44
|
+
* FIXED: The `:unsetenv_others` option now works properly when executing a proc using a fork.
|
|
45
|
+
* FIXED: Environment variable values specified as nil are now correctly unset when executing a proc using a fork.
|
|
46
|
+
* FIXED: Fixed a rare concurrency issue if multiple threads concurrently get the result from a controller.
|
|
47
|
+
|
|
48
|
+
Other changes:
|
|
49
|
+
|
|
50
|
+
* NEW: Native tab completion for zsh.
|
|
51
|
+
* NEW: The `:bundler` mixin supports "manual" bundle setup, allowing bundler decisions to be deferred to execution time
|
|
52
|
+
* FIXED: The `:bundler` mixin will not attempt to add the `pathname` gem to generated Gemfiles when running on TruffleRuby. This caused issues because TruffleRuby includes a special version of the gem and cannot install the one from Rubygems.
|
|
53
|
+
* FIXED: `ContextualError` no longer overrides `Exception#cause`, which could confuse TruffleRuby.
|
|
54
|
+
* DOCUMENTATION: Updated user guide to cover zsh completion and manual bundler setup
|
|
55
|
+
* DOCUMENTATION: Some reorganization and cleanup in the user guide
|
|
56
|
+
|
|
3
57
|
### v0.19.1 / 2026-01-06
|
|
4
58
|
|
|
5
59
|
* FIXED: The minitest template and the "system test" builtin now support minitest 6
|
data/builtins/system/test.rb
CHANGED
|
@@ -9,159 +9,242 @@ flag :seed, "-s", "--seed SEED",
|
|
|
9
9
|
flag :warnings, "-w", "--[no-]warnings",
|
|
10
10
|
default: true,
|
|
11
11
|
desc: "Turn on Ruby warnings (defaults to true)"
|
|
12
|
-
flag :
|
|
12
|
+
flag :include_name, "-n", "-i", "--name PATTERN", "--include PATTERN",
|
|
13
13
|
desc: "Filter run on /regexp/ or string."
|
|
14
|
-
flag :
|
|
14
|
+
flag :exclude_name, "-e", "-x", "--exclude PATTERN",
|
|
15
15
|
desc: "Exclude /regexp/ or string from run."
|
|
16
16
|
flag :recursive, "--[no-]recursive", default: true,
|
|
17
17
|
desc: "Recursively test subtools (default is true)"
|
|
18
18
|
flag :tool, "-t TOOL", "--tool TOOL", default: "",
|
|
19
19
|
desc: "Run tests only for tools under the given path"
|
|
20
|
-
flag :minitest_version, "--minitest-version=VERSION",
|
|
21
|
-
desc: "Set the minitest version requirement
|
|
20
|
+
flag :minitest_version, "--minitest-version=VERSION", "--minitest=VERSION",
|
|
21
|
+
desc: "Set the minitest version requirement during runs where no Gemfile is present"
|
|
22
22
|
flag :minitest_focus, "--minitest-focus[=VERSION]",
|
|
23
|
-
desc: "Make minitest-focus available during
|
|
23
|
+
desc: "Make minitest-focus available during runs where no Gemfile is present"
|
|
24
24
|
flag :minitest_mock, "--minitest-mock[=VERSION]",
|
|
25
|
-
desc: "Make minitest-mock available during
|
|
25
|
+
desc: "Make minitest-mock available during runs where no Gemfile is present"
|
|
26
26
|
flag :minitest_rg, "--minitest-rg[=VERSION]",
|
|
27
|
-
desc: "Make minitest-rg available during
|
|
28
|
-
flag :
|
|
27
|
+
desc: "Make minitest-rg available during runs where no Gemfile is present"
|
|
28
|
+
flag :use_gems, "--use-gem=SPEC",
|
|
29
|
+
default: [], handler: :push,
|
|
30
|
+
desc: "Install the given gem with version requirements during runs where no Gemfile is present"
|
|
31
|
+
flag :minitest_compat, "--[no-]minitest-compat", "--[no-]mt-compat",
|
|
29
32
|
desc: "Set MT_COMPAT to retain compatibility with certain old plugins"
|
|
33
|
+
flag :expand_globs, "--globs", "--expand-globs",
|
|
34
|
+
desc: "Expand globs in the test file arguments."
|
|
35
|
+
|
|
36
|
+
remaining_args :tests,
|
|
37
|
+
complete: :file_system,
|
|
38
|
+
desc: "Paths to the tests to run (defaults to all tests)"
|
|
30
39
|
|
|
31
40
|
include :exec
|
|
32
41
|
include :gems
|
|
33
42
|
include :terminal
|
|
34
43
|
|
|
35
44
|
def run
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
exit(result.exit_code)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def load_minitest_gems
|
|
47
|
-
if minitest_focus
|
|
48
|
-
set :minitest_focus, "~>1.4,>=1.4.1" if minitest_focus == true
|
|
49
|
-
gem "minitest-focus", *minitest_focus.split(",")
|
|
50
|
-
require "minitest/focus"
|
|
51
|
-
end
|
|
52
|
-
if minitest_mock
|
|
53
|
-
set :minitest_mock, "~>5.27" if minitest_mock == true
|
|
54
|
-
gem "minitest-mock", *minitest_mock.split(",")
|
|
55
|
-
require "minitest/mock"
|
|
56
|
-
end
|
|
57
|
-
if minitest_rg
|
|
58
|
-
set :minitest_rg, "~>5.4" if minitest_rg == true
|
|
59
|
-
gem "minitest-rg", *minitest_rg.split(",")
|
|
60
|
-
require "minitest/rg"
|
|
45
|
+
setup_mt_compat
|
|
46
|
+
final_code = 0
|
|
47
|
+
jobs = determine_jobs
|
|
48
|
+
if jobs.empty?
|
|
49
|
+
puts "WARNING: No test files found", :yellow, :bold
|
|
50
|
+
exit
|
|
61
51
|
end
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
jobs.each do |job|
|
|
53
|
+
puts "Running #{job.name}", :bold
|
|
54
|
+
result = run_job(job)
|
|
55
|
+
if result.success?
|
|
56
|
+
puts "Succeeded: #{job.name}", :green, :bold
|
|
57
|
+
else
|
|
58
|
+
puts "Failed: #{job.name} (code=#{result.effective_code})", :red, :bold
|
|
59
|
+
final_code = 1
|
|
60
|
+
end
|
|
71
61
|
end
|
|
62
|
+
exit(final_code)
|
|
72
63
|
end
|
|
73
64
|
|
|
74
|
-
def
|
|
65
|
+
def setup_mt_compat
|
|
75
66
|
case minitest_compat
|
|
76
67
|
when true
|
|
77
|
-
|
|
68
|
+
ENV["MT_COMPAT"] = "true"
|
|
78
69
|
when false
|
|
79
|
-
|
|
80
|
-
else
|
|
81
|
-
{}
|
|
70
|
+
ENV.delete("MT_COMPAT")
|
|
82
71
|
end
|
|
83
72
|
end
|
|
84
73
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
args
|
|
89
|
-
args
|
|
90
|
-
args << "--"
|
|
74
|
+
Job = ::Struct.new(:name, :globs, :tests, :gemfile)
|
|
75
|
+
|
|
76
|
+
def run_job(job)
|
|
77
|
+
args = ["system", "test", "_internal"]
|
|
78
|
+
args.concat(verbosity_flags)
|
|
91
79
|
args << "--seed" << seed if seed
|
|
92
|
-
args << "--
|
|
93
|
-
args << "--name" <<
|
|
94
|
-
args << "--exclude" <<
|
|
95
|
-
|
|
80
|
+
args << "--no-warnings" unless warnings
|
|
81
|
+
args << "--name" << include_name if include_name
|
|
82
|
+
args << "--exclude" << exclude_name if exclude_name
|
|
83
|
+
if job.gemfile
|
|
84
|
+
args << "--gemfile-path" << job.gemfile
|
|
85
|
+
else
|
|
86
|
+
add_gem_args(args)
|
|
87
|
+
end
|
|
88
|
+
args << "--globs" if job.globs
|
|
89
|
+
args << "--preload-code" << preload_code
|
|
90
|
+
args.concat(Array(job.globs || job.tests))
|
|
91
|
+
exec_separate_tool(args)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def preload_code
|
|
95
|
+
<<~RUBY
|
|
96
|
+
require "toys"
|
|
97
|
+
require "toys/testing"
|
|
98
|
+
Toys::Testing.toys_custom_paths(#{base_dir.inspect})
|
|
99
|
+
Toys::Testing.toys_include_builtins(false)
|
|
100
|
+
RUBY
|
|
96
101
|
end
|
|
97
102
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if @minitest_focus_version
|
|
103
|
-
code << "gem 'minitest-focus', '= #{@minitest_focus_version}'"
|
|
104
|
-
code << "require 'minitest/focus'"
|
|
103
|
+
def add_gem_args(args)
|
|
104
|
+
use_gems_hash = use_gems.to_h do |spec|
|
|
105
|
+
name, version = spec.strip.split(/\s*,\s*/, 2)
|
|
106
|
+
[name, version]
|
|
105
107
|
end
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
use_gems_hash["minitest"] = minitest_version
|
|
109
|
+
use_gems_hash["minitest-mock"] = minitest_mock if minitest_mock
|
|
110
|
+
use_gems_hash["minitest-focus"] = minitest_focus if minitest_focus
|
|
111
|
+
use_gems_hash["minitest-rg"] = minitest_rg if minitest_rg
|
|
112
|
+
if ::ENV["TOYS_DEV"] == "true"
|
|
113
|
+
args << "--libs" << ::Toys::CORE_LIB_PATH unless use_gems_hash["toys-core"]
|
|
114
|
+
args << "--libs" << ::Toys::LIB_PATH unless use_gems_hash["toys"]
|
|
115
|
+
else
|
|
116
|
+
use_gems_hash["toys"] ||= ::Toys::VERSION
|
|
109
117
|
end
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
use_gems_hash.each do |name, versions|
|
|
119
|
+
versions = nil if versions == true
|
|
120
|
+
args << "--use-gem" << [name, versions].compact.join(",")
|
|
113
121
|
end
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def determine_jobs
|
|
125
|
+
return determine_jobs_from_tests unless tests.empty?
|
|
126
|
+
jobs = []
|
|
127
|
+
job = build_job_under(::File.join(tool_dir, ".test"))
|
|
128
|
+
jobs << job if job
|
|
129
|
+
if recursive
|
|
130
|
+
::Dir.glob("#{tool_dir}/*/**/.test").sort.each do |test_dir|
|
|
131
|
+
job = build_job_under(test_dir)
|
|
132
|
+
jobs << job if job
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
jobs
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def determine_jobs_from_tests
|
|
139
|
+
jobs = []
|
|
140
|
+
paths_by_test_dir = {}
|
|
141
|
+
paths_without_gemfile = []
|
|
142
|
+
preprocess_tests.each do |path|
|
|
143
|
+
test_dir = find_test_dir(path)
|
|
144
|
+
if test_dir
|
|
145
|
+
(paths_by_test_dir[test_dir] ||= []) << path
|
|
146
|
+
else
|
|
147
|
+
paths_without_gemfile << path
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
paths_by_test_dir.each do |test_dir, paths|
|
|
151
|
+
gemfile_path = ::File.join(test_dir, "Gemfile")
|
|
152
|
+
if ::File.file?(gemfile_path)
|
|
153
|
+
jobs << Job.new("specified tests under #{test_dir}", nil, paths, gemfile_path)
|
|
154
|
+
else
|
|
155
|
+
paths_without_gemfile.concat(paths)
|
|
156
|
+
end
|
|
119
157
|
end
|
|
120
|
-
|
|
121
|
-
|
|
158
|
+
unless paths_without_gemfile.empty?
|
|
159
|
+
name_prefix = jobs.empty? ? "" : "remaining "
|
|
160
|
+
jobs << Job.new("#{name_prefix}specified tests", nil, paths_without_gemfile, nil)
|
|
122
161
|
end
|
|
123
|
-
|
|
162
|
+
jobs
|
|
124
163
|
end
|
|
125
164
|
|
|
126
|
-
def
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
165
|
+
def preprocess_tests
|
|
166
|
+
results = []
|
|
167
|
+
tests.each do |elem|
|
|
168
|
+
if expand_globs
|
|
169
|
+
glob_results = ::Dir.glob(elem)
|
|
170
|
+
if glob_results.empty?
|
|
171
|
+
logger.warn("Pattern did not match any test files: #{elem}")
|
|
172
|
+
else
|
|
173
|
+
glob_results.each do |path|
|
|
174
|
+
results << ::File.realpath(path)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
else
|
|
178
|
+
begin
|
|
179
|
+
results << ::File.realpath(elem)
|
|
180
|
+
rescue ::Errno::ENOENT
|
|
181
|
+
logger.error("Unable to find file: #{elem}")
|
|
182
|
+
exit(1)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
134
185
|
end
|
|
135
|
-
|
|
136
|
-
|
|
186
|
+
results
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def find_test_dir(path)
|
|
190
|
+
dir = ::File.dirname(path)
|
|
191
|
+
while dir != path
|
|
192
|
+
return dir if ::File.basename(dir) == ".test"
|
|
193
|
+
path = dir
|
|
194
|
+
dir = ::File.dirname(dir)
|
|
137
195
|
end
|
|
138
|
-
|
|
196
|
+
nil
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def build_job_under(test_path)
|
|
200
|
+
return nil unless ::File.directory?(test_path)
|
|
201
|
+
globs = ["#{test_path}/**/test_*.rb", "#{test_path}/**/*_test.rb"]
|
|
202
|
+
globs.delete_if { |glob| ::Dir.glob(glob).empty? }
|
|
203
|
+
return nil if globs.empty?
|
|
204
|
+
gemfile_path = ::File.join(test_path, "Gemfile")
|
|
205
|
+
gemfile_path = nil unless ::File.file?(gemfile_path)
|
|
206
|
+
Job.new("tests under #{test_path}", globs, nil, gemfile_path)
|
|
139
207
|
end
|
|
140
208
|
|
|
141
209
|
def tool_dir
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
210
|
+
@tool_dir ||= begin
|
|
211
|
+
words = cli.loader.split_path(tool)
|
|
212
|
+
dir = base_dir
|
|
213
|
+
unless words.empty?
|
|
214
|
+
dir = ::File.join(dir, *words)
|
|
215
|
+
unless ::File.directory?(dir)
|
|
216
|
+
logger.warn("No such directory: #{dir}")
|
|
217
|
+
exit
|
|
218
|
+
end
|
|
149
219
|
end
|
|
220
|
+
dir
|
|
150
221
|
end
|
|
151
|
-
dir
|
|
152
222
|
end
|
|
153
223
|
|
|
154
224
|
def base_dir
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
225
|
+
@base_dir ||=
|
|
226
|
+
if directory
|
|
227
|
+
unless ::File.directory?(directory)
|
|
228
|
+
logger.error("Directory not found: #{directory}")
|
|
229
|
+
exit(1)
|
|
230
|
+
end
|
|
231
|
+
::File.realpath(directory)
|
|
232
|
+
else
|
|
233
|
+
dir = ::File.realpath(::Dir.getwd)
|
|
234
|
+
loop do
|
|
235
|
+
candidate = ::File.join(dir, ::Toys::StandardCLI::CONFIG_DIR_NAME)
|
|
236
|
+
break candidate if ::File.directory?(candidate)
|
|
237
|
+
parent = ::File.dirname(dir)
|
|
238
|
+
if parent == dir
|
|
239
|
+
logger.error("Unable to find a Toys directory")
|
|
240
|
+
exit(1)
|
|
241
|
+
end
|
|
242
|
+
dir = parent
|
|
243
|
+
end
|
|
164
244
|
end
|
|
165
|
-
|
|
166
|
-
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
expand :minitest do |mt|
|
|
248
|
+
mt.name = "_internal"
|
|
249
|
+
mt.files = []
|
|
167
250
|
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
desc "Zsh tab completion for Toys"
|
|
4
|
+
|
|
5
|
+
long_desc \
|
|
6
|
+
"Tools that manage tab completion for Toys in the zsh shell.",
|
|
7
|
+
"",
|
|
8
|
+
"To install tab completion for Toys, execute the following line in a zsh shell, or" \
|
|
9
|
+
" include it in an init file such as your .zshrc (after the line that calls compinit):",
|
|
10
|
+
[" $(toys system zsh-completion install)"],
|
|
11
|
+
"",
|
|
12
|
+
"To remove tab completion, execute:",
|
|
13
|
+
[" $(toys system zsh-completion remove)"],
|
|
14
|
+
"",
|
|
15
|
+
"It is also possible to install completions for different executable names if you have" \
|
|
16
|
+
" aliases for Toys. See the help for the \"install\" and \"remove\" tools for details.",
|
|
17
|
+
"",
|
|
18
|
+
"The \"eval\" tool is the actual completion command invoked by zsh when it needs to" \
|
|
19
|
+
" complete a toys command line. You shouldn't need to invoke it directly."
|
|
20
|
+
|
|
21
|
+
tool "install" do
|
|
22
|
+
desc "Install zsh tab completion"
|
|
23
|
+
|
|
24
|
+
long_desc \
|
|
25
|
+
"Outputs a command to set up Toys tab completion in the current zsh shell.",
|
|
26
|
+
"",
|
|
27
|
+
"To use, execute the following line in a zsh shell, or include it in an init file" \
|
|
28
|
+
" such as your .zshrc (after the line that calls compinit):",
|
|
29
|
+
[" $(toys system zsh-completion install)"],
|
|
30
|
+
"",
|
|
31
|
+
"This will associate the toys tab completion logic with the `toys` executable by default." \
|
|
32
|
+
" If you have aliases for the toys executable, pass them as arguments. e.g.",
|
|
33
|
+
[" $(toys system zsh-completion install my-toys-alias another-alias)"]
|
|
34
|
+
|
|
35
|
+
remaining_args :executable_names,
|
|
36
|
+
desc: "Names of executables for which to set up tab completion" \
|
|
37
|
+
" (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})"
|
|
38
|
+
|
|
39
|
+
def run
|
|
40
|
+
require "shellwords"
|
|
41
|
+
path = ::File.join(::File.dirname(::File.dirname(__dir__)), "share", "zsh-completion.sh")
|
|
42
|
+
exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names
|
|
43
|
+
puts Shellwords.join(["source", path] + exes)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
tool "remove" do
|
|
48
|
+
desc "Remove zsh tab completion"
|
|
49
|
+
|
|
50
|
+
long_desc \
|
|
51
|
+
"Outputs a command to remove Toys tab completion from the current zsh shell.",
|
|
52
|
+
"",
|
|
53
|
+
"To use, execute the following line in a zsh shell:",
|
|
54
|
+
[" $(toys system zsh-completion remove)"],
|
|
55
|
+
"",
|
|
56
|
+
"If you have other names or aliases for the toys executable, pass them as arguments. e.g.",
|
|
57
|
+
[" $(toys system zsh-completion remove my-toys-alias another-alias)"]
|
|
58
|
+
|
|
59
|
+
remaining_args :executable_names,
|
|
60
|
+
desc: "Names of executables for which to remove tab completion" \
|
|
61
|
+
" (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})"
|
|
62
|
+
|
|
63
|
+
def run
|
|
64
|
+
require "shellwords"
|
|
65
|
+
path = ::File.join(::File.dirname(::File.dirname(__dir__)), "share", "zsh-completion-remove.sh")
|
|
66
|
+
exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names
|
|
67
|
+
puts Shellwords.join(["source", path] + exes)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
tool "eval" do
|
|
72
|
+
desc "Tab completion command (executed by zsh)"
|
|
73
|
+
|
|
74
|
+
long_desc \
|
|
75
|
+
"Completion command invoked by zsh to complete a toys command line. Generally you do not" \
|
|
76
|
+
" need to invoke this directly. It reads the command line context from the COMP_LINE" \
|
|
77
|
+
" and COMP_POINT environment variables, and outputs completion candidates to stdout in" \
|
|
78
|
+
" two sections separated by a blank line: final completions first, then partial" \
|
|
79
|
+
" completions (such as directory paths)."
|
|
80
|
+
|
|
81
|
+
disable_argument_parsing
|
|
82
|
+
|
|
83
|
+
def run
|
|
84
|
+
require "toys/utils/completion_engine"
|
|
85
|
+
result = ::Toys::Utils::CompletionEngine::Zsh.new(cli).run
|
|
86
|
+
if result > 1
|
|
87
|
+
logger.fatal("This tool must be invoked as a zsh completion command.")
|
|
88
|
+
end
|
|
89
|
+
exit(result)
|
|
90
|
+
end
|
|
91
|
+
end
|
data/core-docs/toys/core.rb
CHANGED
data/core-docs/toys/errors.rb
CHANGED
|
@@ -58,17 +58,18 @@ module Toys
|
|
|
58
58
|
#
|
|
59
59
|
# @private This interface is internal and subject to change without warning.
|
|
60
60
|
#
|
|
61
|
-
def initialize(
|
|
61
|
+
def initialize(underlying_error, banner,
|
|
62
62
|
config_path: nil, config_line: nil,
|
|
63
63
|
tool_name: nil, tool_args: nil)
|
|
64
64
|
# Source available in the toys-core gem
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
##
|
|
68
|
-
# The underlying exception
|
|
68
|
+
# The underlying exception.
|
|
69
|
+
# Generally the same as `Exception#cause`.
|
|
69
70
|
# @return [::StandardError]
|
|
70
71
|
#
|
|
71
|
-
attr_reader :
|
|
72
|
+
attr_reader :underlying_error
|
|
72
73
|
|
|
73
74
|
##
|
|
74
75
|
# An overall banner message
|
|
@@ -22,9 +22,21 @@ module Toys
|
|
|
22
22
|
#
|
|
23
23
|
# The following parameters can be passed when including this mixin:
|
|
24
24
|
#
|
|
25
|
-
# * `:static` (Boolean)
|
|
26
|
-
#
|
|
27
|
-
#
|
|
25
|
+
# * `:static` (Boolean) Has the same effect as passing `:static` to the
|
|
26
|
+
# `:setup` parameter.
|
|
27
|
+
#
|
|
28
|
+
# * `:setup` (:auto,:manual,:static) A symbol indicating when the bundle
|
|
29
|
+
# should be installed. Possible values are:
|
|
30
|
+
#
|
|
31
|
+
# * `:auto` - (Default) Installs the bundle just before the tool runs.
|
|
32
|
+
# * `:static` - Installs the bundle immediately when defining the
|
|
33
|
+
# tool.
|
|
34
|
+
# * `:manual` - Does not install the bundle, but defines the methods
|
|
35
|
+
# `bundler_setup` and `bundler_setup?` in the tool. The tool can
|
|
36
|
+
# call `bundler_setup` to install the bundle, optionally passing
|
|
37
|
+
# any of the remaining keyword arguments below to override the
|
|
38
|
+
# corresponding mixin parameters. The `bundler_setup?` method can
|
|
39
|
+
# be queried to determine whether the bundle has been set up yet.
|
|
28
40
|
#
|
|
29
41
|
# * `:groups` (Array\<String\>) The groups to include in setup.
|
|
30
42
|
#
|
|
@@ -76,7 +88,9 @@ module Toys
|
|
|
76
88
|
# (optional)
|
|
77
89
|
#
|
|
78
90
|
# * `:terminal` (Toys::Utils::Terminal) Terminal to use (optional)
|
|
91
|
+
#
|
|
79
92
|
# * `:input` (IO) Input IO (optional, defaults to STDIN)
|
|
93
|
+
#
|
|
80
94
|
# * `:output` (IO) Output IO (optional, defaults to STDOUT)
|
|
81
95
|
#
|
|
82
96
|
module Bundler
|
|
@@ -109,7 +109,8 @@ module Toys
|
|
|
109
109
|
# an existing File stream. Unlike `Process#spawn`, this works for IO
|
|
110
110
|
# objects that do not have a corresponding file descriptor (such as
|
|
111
111
|
# StringIO objects). In such a case, a thread will be spawned to pipe
|
|
112
|
-
# the IO data through to the child process.
|
|
112
|
+
# the IO data through to the child process. Note that the IO object
|
|
113
|
+
# will _not_ be closed on completion.
|
|
113
114
|
#
|
|
114
115
|
# * **Redirect to a pipe:** You can redirect to a pipe created using
|
|
115
116
|
# `IO.pipe` (i.e. a two-element array of read and write IO objects) by
|
|
@@ -12,11 +12,15 @@ module Toys
|
|
|
12
12
|
##
|
|
13
13
|
# **_Defined in the toys-core gem_**
|
|
14
14
|
#
|
|
15
|
-
#
|
|
15
|
+
# Base class for shell completion engines that use a
|
|
16
|
+
# `COMP_LINE` / `COMP_POINT` protocol.
|
|
17
|
+
#
|
|
18
|
+
# Subclasses must implement the private methods `#shell_name` and
|
|
19
|
+
# `#output_completions`.
|
|
16
20
|
#
|
|
17
|
-
class
|
|
21
|
+
class Base
|
|
18
22
|
##
|
|
19
|
-
# Create a
|
|
23
|
+
# Create a completion engine.
|
|
20
24
|
#
|
|
21
25
|
# @param cli [Toys::CLI] The CLI.
|
|
22
26
|
#
|
|
@@ -27,8 +31,8 @@ module Toys
|
|
|
27
31
|
##
|
|
28
32
|
# Perform completion in the current shell environment, which must
|
|
29
33
|
# include settings for the `COMP_LINE` and `COMP_POINT` environment
|
|
30
|
-
# variables. Prints out completion candidates
|
|
31
|
-
#
|
|
34
|
+
# variables. Prints out completion candidates and returns a status code
|
|
35
|
+
# indicating the result.
|
|
32
36
|
#
|
|
33
37
|
# * **0** for success.
|
|
34
38
|
# * **1** if completion failed.
|
|
@@ -42,6 +46,30 @@ module Toys
|
|
|
42
46
|
end
|
|
43
47
|
end
|
|
44
48
|
|
|
49
|
+
##
|
|
50
|
+
# **_Defined in the toys-core gem_**
|
|
51
|
+
#
|
|
52
|
+
# A completion engine for bash.
|
|
53
|
+
#
|
|
54
|
+
class Bash < Base
|
|
55
|
+
##
|
|
56
|
+
# Create a bash completion engine.
|
|
57
|
+
#
|
|
58
|
+
# @param cli [Toys::CLI] The CLI.
|
|
59
|
+
#
|
|
60
|
+
def initialize(cli)
|
|
61
|
+
# Source available in the toys-core gem
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# **_Defined in the toys-core gem_**
|
|
67
|
+
#
|
|
68
|
+
# A completion engine for zsh.
|
|
69
|
+
#
|
|
70
|
+
class Zsh < Base
|
|
71
|
+
end
|
|
72
|
+
|
|
45
73
|
class << self
|
|
46
74
|
end
|
|
47
75
|
end
|