toys-core 0.7.0 → 0.8.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 +98 -0
- data/LICENSE.md +16 -24
- data/README.md +307 -59
- data/docs/guide.md +44 -4
- data/lib/toys-core.rb +58 -49
- data/lib/toys/acceptor.rb +672 -0
- data/lib/toys/alias.rb +106 -0
- data/lib/toys/arg_parser.rb +624 -0
- data/lib/toys/cli.rb +422 -181
- data/lib/toys/compat.rb +83 -0
- data/lib/toys/completion.rb +442 -0
- data/lib/toys/context.rb +354 -0
- data/lib/toys/core_version.rb +18 -26
- data/lib/toys/dsl/flag.rb +213 -56
- data/lib/toys/dsl/flag_group.rb +237 -51
- data/lib/toys/dsl/positional_arg.rb +210 -0
- data/lib/toys/dsl/tool.rb +968 -317
- data/lib/toys/errors.rb +46 -28
- data/lib/toys/flag.rb +821 -0
- data/lib/toys/flag_group.rb +282 -0
- data/lib/toys/input_file.rb +18 -26
- data/lib/toys/loader.rb +110 -100
- data/lib/toys/middleware.rb +24 -31
- data/lib/toys/mixin.rb +90 -59
- data/lib/toys/module_lookup.rb +125 -0
- data/lib/toys/positional_arg.rb +184 -0
- data/lib/toys/source_info.rb +192 -0
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
- data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
- data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
- data/lib/toys/standard_middleware/show_help.rb +130 -113
- data/lib/toys/standard_middleware/show_root_version.rb +29 -35
- data/lib/toys/standard_mixins/exec.rb +116 -78
- data/lib/toys/standard_mixins/fileutils.rb +16 -24
- data/lib/toys/standard_mixins/gems.rb +29 -30
- data/lib/toys/standard_mixins/highline.rb +34 -41
- data/lib/toys/standard_mixins/terminal.rb +72 -26
- data/lib/toys/template.rb +51 -35
- data/lib/toys/tool.rb +1161 -206
- data/lib/toys/utils/completion_engine.rb +171 -0
- data/lib/toys/utils/exec.rb +279 -182
- data/lib/toys/utils/gems.rb +58 -49
- data/lib/toys/utils/help_text.rb +117 -111
- data/lib/toys/utils/terminal.rb +69 -62
- data/lib/toys/wrappable_string.rb +162 -0
- metadata +24 -22
- data/lib/toys/definition/acceptor.rb +0 -191
- data/lib/toys/definition/alias.rb +0 -112
- data/lib/toys/definition/arg.rb +0 -140
- data/lib/toys/definition/flag.rb +0 -370
- data/lib/toys/definition/flag_group.rb +0 -205
- data/lib/toys/definition/source_info.rb +0 -190
- data/lib/toys/definition/tool.rb +0 -842
- data/lib/toys/dsl/arg.rb +0 -132
- data/lib/toys/runner.rb +0 -188
- data/lib/toys/standard_middleware.rb +0 -47
- data/lib/toys/utils/module_lookup.rb +0 -135
- data/lib/toys/utils/wrappable_string.rb +0 -165
data/lib/toys/compat.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
22
|
+
;
|
23
|
+
|
24
|
+
module Toys
|
25
|
+
##
|
26
|
+
# Compatibility wrappers for older Ruby versions.
|
27
|
+
# @private
|
28
|
+
#
|
29
|
+
module Compat
|
30
|
+
## @private
|
31
|
+
CURRENT_VERSION = ::Gem::Version.new(::RUBY_VERSION)
|
32
|
+
|
33
|
+
## @private
|
34
|
+
def self.check_minimum_version(version)
|
35
|
+
CURRENT_VERSION >= ::Gem::Version.new(version)
|
36
|
+
end
|
37
|
+
|
38
|
+
if check_minimum_version("2.4.0")
|
39
|
+
## @private
|
40
|
+
def self.suggestions(word, list)
|
41
|
+
::DidYouMean::SpellChecker.new(dictionary: list).correct(word)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
## @private
|
45
|
+
def self.suggestions(_word, _list)
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
if check_minimum_version("2.4.0")
|
51
|
+
## @private
|
52
|
+
def self.merge_clones(hash, orig)
|
53
|
+
orig.each { |k, v| hash[k] = v.clone }
|
54
|
+
hash
|
55
|
+
end
|
56
|
+
else
|
57
|
+
## @private
|
58
|
+
def self.merge_clones(hash, orig)
|
59
|
+
orig.each do |k, v|
|
60
|
+
hash[k] =
|
61
|
+
begin
|
62
|
+
v.clone
|
63
|
+
rescue ::TypeError
|
64
|
+
v
|
65
|
+
end
|
66
|
+
end
|
67
|
+
hash
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if check_minimum_version("2.5.0")
|
72
|
+
## @private
|
73
|
+
def self.glob_in_dir(glob, dir)
|
74
|
+
::Dir.glob(glob, base: dir)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
## @private
|
78
|
+
def self.glob_in_dir(glob, dir)
|
79
|
+
::Dir.chdir(dir) { ::Dir.glob(glob) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,442 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
22
|
+
;
|
23
|
+
|
24
|
+
module Toys
|
25
|
+
##
|
26
|
+
# A Completion is a callable Proc that determines candidates for shell tab
|
27
|
+
# completion. You pass a {Toys::Completion::Context} object (which includes
|
28
|
+
# the current string fragment and other information) and it returns an array
|
29
|
+
# of candidates, represented by {Toys::Completion::Candidate} objects, for
|
30
|
+
# completing the fragment.
|
31
|
+
#
|
32
|
+
# A useful method here is the class method {Toys::Completion.create} which
|
33
|
+
# takes a variety of inputs and returns a suitable completion Proc.
|
34
|
+
#
|
35
|
+
module Completion
|
36
|
+
##
|
37
|
+
# The context in which to determine completion candidates.
|
38
|
+
#
|
39
|
+
class Context
|
40
|
+
##
|
41
|
+
# Create a completion context
|
42
|
+
#
|
43
|
+
# @param cli [Toys::CLI] The CLI being run. Required.
|
44
|
+
# @param previous_words [Array<String>] Array of complete strings that
|
45
|
+
# appeared prior to the fragment to complete.
|
46
|
+
# @param fragment_prefix [String] A prefix in the fragment that does not
|
47
|
+
# participate in completion. (e.g. "key=")
|
48
|
+
# @param fragment [String] The string fragment to complete.
|
49
|
+
# @param params [Hash] Miscellaneous context data
|
50
|
+
#
|
51
|
+
def initialize(cli:, previous_words: [], fragment_prefix: "", fragment: "", **params)
|
52
|
+
@cli = cli
|
53
|
+
@previous_words = previous_words
|
54
|
+
@fragment_prefix = fragment_prefix
|
55
|
+
@fragment = fragment
|
56
|
+
extra_params = {
|
57
|
+
cli: cli, previous_words: previous_words, fragment_prefix: fragment_prefix,
|
58
|
+
fragment: fragment
|
59
|
+
}
|
60
|
+
@params = params.merge(extra_params)
|
61
|
+
@tool = nil
|
62
|
+
@args = nil
|
63
|
+
@arg_parser = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Create a new completion context with the given modifications.
|
68
|
+
#
|
69
|
+
# @param delta_params [Hash] Replace context data.
|
70
|
+
# @return [Toys::Completion::Context]
|
71
|
+
#
|
72
|
+
def with(**delta_params)
|
73
|
+
Context.new(@params.merge(delta_params))
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# The CLI being run.
|
78
|
+
# @return [Toys::CLI]
|
79
|
+
#
|
80
|
+
attr_reader :cli
|
81
|
+
|
82
|
+
##
|
83
|
+
# All previous words.
|
84
|
+
# @return [Array<String>]
|
85
|
+
#
|
86
|
+
attr_reader :previous_words
|
87
|
+
|
88
|
+
##
|
89
|
+
# A non-completed prefix for the current fragment.
|
90
|
+
# @return [String]
|
91
|
+
#
|
92
|
+
attr_reader :fragment_prefix
|
93
|
+
|
94
|
+
##
|
95
|
+
# The current string fragment to complete
|
96
|
+
# @return [String]
|
97
|
+
#
|
98
|
+
attr_reader :fragment
|
99
|
+
|
100
|
+
##
|
101
|
+
# Get data for arbitrary key.
|
102
|
+
# @param [Symbol] key
|
103
|
+
# @return [Object]
|
104
|
+
#
|
105
|
+
def [](key)
|
106
|
+
@params[key]
|
107
|
+
end
|
108
|
+
alias get []
|
109
|
+
|
110
|
+
##
|
111
|
+
# The tool being invoked, which should control the completion.
|
112
|
+
# @return [Toys::Tool]
|
113
|
+
#
|
114
|
+
def tool
|
115
|
+
lookup_tool
|
116
|
+
@tool
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# An array of complete arguments passed to the tool, prior to the
|
121
|
+
# fragment to complete.
|
122
|
+
# @return [Array<String>]
|
123
|
+
#
|
124
|
+
def args
|
125
|
+
lookup_tool
|
126
|
+
@args
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Current ArgParser indicating the status of argument parsing up to
|
131
|
+
# this point.
|
132
|
+
#
|
133
|
+
# @return [Toys::ArgParser]
|
134
|
+
#
|
135
|
+
def arg_parser
|
136
|
+
lookup_tool
|
137
|
+
@arg_parser ||= ArgParser.new(@cli, @tool).parse(@args)
|
138
|
+
end
|
139
|
+
|
140
|
+
## @private
|
141
|
+
def inspect
|
142
|
+
"<Toys::Completion::Context previous=#{previous_words.inspect}" \
|
143
|
+
" prefix=#{fragment_prefix.inspect} fragment=#{fragment.inspect}>"
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def lookup_tool
|
149
|
+
@tool, @args = @cli.loader.lookup(@previous_words) unless @tool
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# A candidate for completing a string fragment.
|
155
|
+
#
|
156
|
+
# A candidate includes a string representing the potential completed
|
157
|
+
# word, as well as a flag indicating whether it is a *partial* completion
|
158
|
+
# (i.e. a prefix that could still be added to) versus a *final* word.
|
159
|
+
# Generally, tab completion systems should add a trailing space after a
|
160
|
+
# final completion but not after a partial completion.
|
161
|
+
#
|
162
|
+
class Candidate
|
163
|
+
include ::Comparable
|
164
|
+
|
165
|
+
##
|
166
|
+
# Create a new candidate
|
167
|
+
# @param string [String] The candidate string
|
168
|
+
# @param partial [Boolean] Whether the candidate is partial. Defaults
|
169
|
+
# to `false`.
|
170
|
+
#
|
171
|
+
def initialize(string, partial: false)
|
172
|
+
@string = string.to_s
|
173
|
+
@partial = partial ? true : false
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Get the candidate string.
|
178
|
+
# @return [String]
|
179
|
+
#
|
180
|
+
attr_reader :string
|
181
|
+
alias to_s string
|
182
|
+
|
183
|
+
##
|
184
|
+
# Determine whether the candidate is partial completion.
|
185
|
+
# @return [Boolean]
|
186
|
+
#
|
187
|
+
def partial?
|
188
|
+
@partial
|
189
|
+
end
|
190
|
+
|
191
|
+
##
|
192
|
+
# Determine whether the candidate is a final completion.
|
193
|
+
# @return [Boolean]
|
194
|
+
#
|
195
|
+
def final?
|
196
|
+
!@partial
|
197
|
+
end
|
198
|
+
|
199
|
+
## @private
|
200
|
+
def eql?(other)
|
201
|
+
other.is_a?(Candidate) && other.string.eql?(string) && other.partial? == @partial
|
202
|
+
end
|
203
|
+
|
204
|
+
## @private
|
205
|
+
def <=>(other)
|
206
|
+
string <=> other.string
|
207
|
+
end
|
208
|
+
|
209
|
+
## @private
|
210
|
+
def hash
|
211
|
+
string.hash
|
212
|
+
end
|
213
|
+
|
214
|
+
##
|
215
|
+
# Create an array of candidates given an array of strings.
|
216
|
+
#
|
217
|
+
# @param array [Array<String>]
|
218
|
+
# @return [Array<Toys::Completion::Candidate]
|
219
|
+
#
|
220
|
+
def self.new_multi(array, partial: false)
|
221
|
+
array.map { |s| new(s, partial: partial) }
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
##
|
226
|
+
# A base class that returns no completions.
|
227
|
+
#
|
228
|
+
# Completions *may* but do not need to subclass this base class. They
|
229
|
+
# merely need to duck-type `Proc` by implementing the `call` method.
|
230
|
+
#
|
231
|
+
class Base
|
232
|
+
##
|
233
|
+
# Returns candidates for the current completion.
|
234
|
+
# This default implementation returns an empty list.
|
235
|
+
#
|
236
|
+
# @param context [Toys::Completion::Context] The current completion
|
237
|
+
# context including the string fragment.
|
238
|
+
# @return [Array<Toys::Completion::Candidate>] An array of candidates
|
239
|
+
#
|
240
|
+
def call(context) # rubocop:disable Lint/UnusedMethodArgument
|
241
|
+
[]
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# A Completion that returns candidates from the local file system.
|
247
|
+
#
|
248
|
+
class FileSystem < Base
|
249
|
+
##
|
250
|
+
# Create a completion that gets candidates from names in the local file
|
251
|
+
# system.
|
252
|
+
#
|
253
|
+
# @param cwd [String] Working directory (defaults to the current dir).
|
254
|
+
# @param omit_files [Boolean] Omit files from candidates
|
255
|
+
# @param omit_directories [Boolean] Omit directories from candidates
|
256
|
+
# @param prefix_constraint [String,Regexp] Constraint on the fragment
|
257
|
+
# prefix. Defaults to requiring the prefix be empty.
|
258
|
+
#
|
259
|
+
def initialize(cwd: nil, omit_files: false, omit_directories: false, prefix_constraint: "")
|
260
|
+
@cwd = cwd || ::Dir.pwd
|
261
|
+
@include_files = !omit_files
|
262
|
+
@include_directories = !omit_directories
|
263
|
+
@prefix_constraint = prefix_constraint
|
264
|
+
end
|
265
|
+
|
266
|
+
##
|
267
|
+
# Whether files are included in the completion candidates.
|
268
|
+
# @return [Boolean]
|
269
|
+
#
|
270
|
+
attr_reader :include_files
|
271
|
+
|
272
|
+
##
|
273
|
+
# Whether directories are included in the completion candidates.
|
274
|
+
# @return [Boolean]
|
275
|
+
#
|
276
|
+
attr_reader :include_directories
|
277
|
+
|
278
|
+
##
|
279
|
+
# Constraint on the fragment prefix.
|
280
|
+
# @return [String,Regexp]
|
281
|
+
#
|
282
|
+
attr_reader :prefix_constraint
|
283
|
+
|
284
|
+
##
|
285
|
+
# Path to the starting directory.
|
286
|
+
# @return [String]
|
287
|
+
#
|
288
|
+
attr_reader :cwd
|
289
|
+
|
290
|
+
##
|
291
|
+
# Returns candidates for the current completion.
|
292
|
+
#
|
293
|
+
# @param context [Toys::Completion::Context] the current completion
|
294
|
+
# context including the string fragment.
|
295
|
+
# @return [Array<Toys::Completion::Candidate>] an array of candidates
|
296
|
+
#
|
297
|
+
def call(context)
|
298
|
+
return [] unless @prefix_constraint === context.fragment_prefix
|
299
|
+
substring = context.fragment
|
300
|
+
prefix, name =
|
301
|
+
if substring.empty? || substring.end_with?("/")
|
302
|
+
[substring, ""]
|
303
|
+
else
|
304
|
+
::File.split(substring)
|
305
|
+
end
|
306
|
+
dir = ::File.expand_path(prefix, @cwd)
|
307
|
+
return [] unless ::File.directory?(dir)
|
308
|
+
prefix = nil if [".", ""].include?(prefix)
|
309
|
+
omits = [".", ".."]
|
310
|
+
children = Compat.glob_in_dir(name, dir).find_all do |child|
|
311
|
+
!omits.include?(child)
|
312
|
+
end
|
313
|
+
children += ::Dir.entries(dir).find_all do |child|
|
314
|
+
child.start_with?(name) && !omits.include?(child)
|
315
|
+
end
|
316
|
+
generate_candidates(children.uniq.sort, prefix, dir)
|
317
|
+
end
|
318
|
+
|
319
|
+
private
|
320
|
+
|
321
|
+
def generate_candidates(children, prefix, dir)
|
322
|
+
children.flat_map do |child|
|
323
|
+
path = ::File.join(dir, child)
|
324
|
+
str = prefix ? ::File.join(prefix, child) : child
|
325
|
+
if ::File.file?(path)
|
326
|
+
@include_files ? [Candidate.new(str)] : []
|
327
|
+
elsif ::File.directory?(path)
|
328
|
+
if @include_directories
|
329
|
+
[Candidate.new("#{str}/", partial: true)]
|
330
|
+
else
|
331
|
+
[]
|
332
|
+
end
|
333
|
+
else
|
334
|
+
[]
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
##
|
341
|
+
# A Completion whose candidates come from a static list of strings.
|
342
|
+
#
|
343
|
+
class Enum < Base
|
344
|
+
##
|
345
|
+
# Create a completion from a list of values.
|
346
|
+
#
|
347
|
+
# @param values [Array<String>]
|
348
|
+
# @param prefix_constraint [String,Regexp] Constraint on the fragment
|
349
|
+
# prefix. Defaults to requiring the prefix be empty.
|
350
|
+
#
|
351
|
+
def initialize(values, prefix_constraint: "")
|
352
|
+
@values = values.flatten.map { |v| Candidate.new(v) }.sort
|
353
|
+
@prefix_constraint = prefix_constraint
|
354
|
+
end
|
355
|
+
|
356
|
+
##
|
357
|
+
# The array of completion candidates.
|
358
|
+
# @return [Array<String>]
|
359
|
+
#
|
360
|
+
attr_reader :values
|
361
|
+
|
362
|
+
##
|
363
|
+
# Constraint on the fragment prefix.
|
364
|
+
# @return [String,Regexp]
|
365
|
+
#
|
366
|
+
attr_reader :prefix_constraint
|
367
|
+
|
368
|
+
##
|
369
|
+
# Returns candidates for the current completion.
|
370
|
+
#
|
371
|
+
# @param context [Toys::Completion::Context] the current completion
|
372
|
+
# context including the string fragment.
|
373
|
+
# @return [Array<Toys::Completion::Candidate>] an array of candidates
|
374
|
+
#
|
375
|
+
def call(context)
|
376
|
+
return [] unless @prefix_constraint === context.fragment_prefix
|
377
|
+
fragment = context.fragment
|
378
|
+
@values.find_all { |val| val.string.start_with?(fragment) }
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
##
|
383
|
+
# An instance of the empty completion that returns no candidates.
|
384
|
+
# @return [Toys:::Completion::Base]
|
385
|
+
#
|
386
|
+
EMPTY = Base.new
|
387
|
+
|
388
|
+
##
|
389
|
+
# Create a completion Proc from a variety of specification formats. The
|
390
|
+
# completion is constructed from the given specification object and/or the
|
391
|
+
# given block. Additionally, some completions can take a hash of options.
|
392
|
+
#
|
393
|
+
# Recognized specs include:
|
394
|
+
#
|
395
|
+
# * `:empty`: Returns the empty completion. Any block or options are
|
396
|
+
# ignored.
|
397
|
+
#
|
398
|
+
# * `:file_system`: Returns a completion that searches the current
|
399
|
+
# directory for file and directory names. You may also pass any of the
|
400
|
+
# options recognized by {Toys::Completion::FileSystem#initialize}. The
|
401
|
+
# block is ignored.
|
402
|
+
#
|
403
|
+
# * An **Array** of strings. Returns a completion that uses those values
|
404
|
+
# as candidates. You may also pass any of the options recognized by
|
405
|
+
# {Toys::Completion::Enum#initialize}. The block is ignored.
|
406
|
+
#
|
407
|
+
# * A **function**, either passed as a Proc (where the block is ignored)
|
408
|
+
# or as a block (if the spec is nil). The function must behave as a
|
409
|
+
# completion object, taking {Toys::Completion::Context} as the sole
|
410
|
+
# argument, and returning an array of {Toys::Completion::Candidate}.
|
411
|
+
#
|
412
|
+
# * `:default` and `nil` indicate the **default completion**. For this
|
413
|
+
# method, the default is the empty completion (i.e. these are synonyms
|
414
|
+
# for `:empty`). However, other completion resolution methods might
|
415
|
+
# have a different default.
|
416
|
+
#
|
417
|
+
# @param spec [Object] See the description for recognized values.
|
418
|
+
# @param options [Hash] Additional options to pass to the completion.
|
419
|
+
# @param block [Proc] See the description for recognized forms.
|
420
|
+
# @return [Toys::Completion::Base,Proc]
|
421
|
+
#
|
422
|
+
def self.create(spec = nil, **options, &block)
|
423
|
+
spec ||= block
|
424
|
+
case spec
|
425
|
+
when nil, :empty, :default
|
426
|
+
EMPTY
|
427
|
+
when ::Proc, Base
|
428
|
+
spec
|
429
|
+
when ::Array
|
430
|
+
Enum.new(spec, options)
|
431
|
+
when :file_system
|
432
|
+
FileSystem.new(options)
|
433
|
+
else
|
434
|
+
if spec.respond_to?(:call)
|
435
|
+
spec
|
436
|
+
else
|
437
|
+
raise ToolDefinitionError, "Illegal completion spec: #{spec.inspect}"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|