toys-core 0.11.5 → 0.13.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 +62 -0
- data/LICENSE.md +1 -1
- data/README.md +5 -2
- data/docs/guide.md +1 -1
- data/lib/toys/acceptor.rb +13 -4
- data/lib/toys/arg_parser.rb +7 -7
- data/lib/toys/cli.rb +170 -120
- data/lib/toys/compat.rb +71 -23
- data/lib/toys/completion.rb +18 -6
- data/lib/toys/context.rb +24 -15
- data/lib/toys/core.rb +6 -2
- data/lib/toys/dsl/base.rb +87 -0
- data/lib/toys/dsl/flag.rb +26 -20
- data/lib/toys/dsl/flag_group.rb +18 -14
- data/lib/toys/dsl/internal.rb +206 -0
- data/lib/toys/dsl/positional_arg.rb +26 -16
- data/lib/toys/dsl/tool.rb +180 -218
- data/lib/toys/errors.rb +64 -8
- data/lib/toys/flag.rb +662 -656
- data/lib/toys/flag_group.rb +24 -10
- data/lib/toys/input_file.rb +13 -7
- data/lib/toys/loader.rb +293 -140
- data/lib/toys/middleware.rb +46 -22
- data/lib/toys/mixin.rb +10 -8
- data/lib/toys/positional_arg.rb +21 -20
- data/lib/toys/settings.rb +914 -0
- data/lib/toys/source_info.rb +147 -35
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -0
- data/lib/toys/standard_middleware/apply_config.rb +6 -4
- data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -0
- data/lib/toys/standard_middleware/set_default_descriptions.rb +19 -18
- data/lib/toys/standard_middleware/show_help.rb +19 -5
- data/lib/toys/standard_middleware/show_root_version.rb +2 -0
- data/lib/toys/standard_mixins/bundler.rb +24 -15
- data/lib/toys/standard_mixins/exec.rb +43 -34
- data/lib/toys/standard_mixins/fileutils.rb +3 -1
- data/lib/toys/standard_mixins/gems.rb +21 -17
- data/lib/toys/standard_mixins/git_cache.rb +46 -0
- data/lib/toys/standard_mixins/highline.rb +8 -8
- data/lib/toys/standard_mixins/terminal.rb +5 -5
- data/lib/toys/standard_mixins/xdg.rb +56 -0
- data/lib/toys/template.rb +11 -9
- data/lib/toys/{tool.rb → tool_definition.rb} +292 -226
- data/lib/toys/utils/completion_engine.rb +7 -2
- data/lib/toys/utils/exec.rb +162 -132
- data/lib/toys/utils/gems.rb +85 -60
- data/lib/toys/utils/git_cache.rb +813 -0
- data/lib/toys/utils/help_text.rb +117 -37
- data/lib/toys/utils/terminal.rb +11 -3
- data/lib/toys/utils/xdg.rb +293 -0
- data/lib/toys/wrappable_string.rb +9 -2
- data/lib/toys-core.rb +18 -6
- metadata +14 -7
@@ -4,22 +4,225 @@ require "set"
|
|
4
4
|
|
5
5
|
module Toys
|
6
6
|
##
|
7
|
-
# A
|
7
|
+
# A ToolDefinition describes a single command that can be invoked using Toys.
|
8
8
|
# It has a name, a series of one or more words that you use to identify
|
9
9
|
# the tool on the command line. It also has a set of formal flags and
|
10
10
|
# command line arguments supported, and a block that gets run when the
|
11
11
|
# tool is executed.
|
12
12
|
#
|
13
|
-
class
|
13
|
+
class ToolDefinition
|
14
|
+
##
|
15
|
+
# A Completion that implements the default algorithm for a tool.
|
16
|
+
#
|
17
|
+
class DefaultCompletion < Completion::Base
|
18
|
+
##
|
19
|
+
# Create a completion given configuration options.
|
20
|
+
#
|
21
|
+
# @param complete_subtools [Boolean] Whether to complete subtool names
|
22
|
+
# @param include_hidden_subtools [Boolean] Whether to include hidden
|
23
|
+
# subtools (i.e. those beginning with an underscore)
|
24
|
+
# @param complete_args [Boolean] Whether to complete positional args
|
25
|
+
# @param complete_flags [Boolean] Whether to complete flag names
|
26
|
+
# @param complete_flag_values [Boolean] Whether to complete flag values
|
27
|
+
# @param delegation_target [Array<String>,nil] Delegation target, or
|
28
|
+
# `nil` if none.
|
29
|
+
#
|
30
|
+
def initialize(complete_subtools: true, include_hidden_subtools: false,
|
31
|
+
complete_args: true, complete_flags: true, complete_flag_values: true,
|
32
|
+
delegation_target: nil)
|
33
|
+
super()
|
34
|
+
@complete_subtools = complete_subtools
|
35
|
+
@include_hidden_subtools = include_hidden_subtools
|
36
|
+
@complete_flags = complete_flags
|
37
|
+
@complete_args = complete_args
|
38
|
+
@complete_flag_values = complete_flag_values
|
39
|
+
@delegation_target = delegation_target
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Whether to complete subtool names
|
44
|
+
# @return [Boolean]
|
45
|
+
#
|
46
|
+
def complete_subtools?
|
47
|
+
@complete_subtools
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Whether to include hidden subtools
|
52
|
+
# @return [Boolean]
|
53
|
+
#
|
54
|
+
def include_hidden_subtools?
|
55
|
+
@include_hidden_subtools
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Whether to complete flags
|
60
|
+
# @return [Boolean]
|
61
|
+
#
|
62
|
+
def complete_flags?
|
63
|
+
@complete_flags
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Whether to complete positional args
|
68
|
+
# @return [Boolean]
|
69
|
+
#
|
70
|
+
def complete_args?
|
71
|
+
@complete_args
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Whether to complete flag values
|
76
|
+
# @return [Boolean]
|
77
|
+
#
|
78
|
+
def complete_flag_values?
|
79
|
+
@complete_flag_values
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Delegation target, or nil for none.
|
84
|
+
# @return [Array<String>] if there is a delegation target
|
85
|
+
# @return [nil] if there is no delegation target
|
86
|
+
#
|
87
|
+
attr_accessor :delegation_target
|
88
|
+
|
89
|
+
##
|
90
|
+
# Returns candidates for the current completion.
|
91
|
+
#
|
92
|
+
# @param context [Toys::Completion::Context] the current completion
|
93
|
+
# context including the string fragment.
|
94
|
+
# @return [Array<Toys::Completion::Candidate>] an array of candidates
|
95
|
+
#
|
96
|
+
def call(context)
|
97
|
+
candidates = valued_flag_candidates(context)
|
98
|
+
return candidates if candidates
|
99
|
+
candidates = subtool_or_arg_candidates(context)
|
100
|
+
candidates += plain_flag_candidates(context)
|
101
|
+
candidates += flag_value_candidates(context)
|
102
|
+
if delegation_target
|
103
|
+
delegate_tool = context.cli.loader.lookup_specific(delegation_target)
|
104
|
+
if delegate_tool
|
105
|
+
context = context.with(previous_words: delegation_target)
|
106
|
+
candidates += delegate_tool.completion.call(context)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
candidates
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def valued_flag_candidates(context)
|
115
|
+
return unless @complete_flag_values
|
116
|
+
arg_parser = context.arg_parser
|
117
|
+
return unless arg_parser.flags_allowed?
|
118
|
+
active_flag_def = arg_parser.active_flag_def
|
119
|
+
return if active_flag_def && active_flag_def.value_type == :required
|
120
|
+
match = /\A(--\w[?\w-]*)=(.*)\z/.match(context.fragment_prefix)
|
121
|
+
return unless match
|
122
|
+
flag_value_context = context.with(fragment_prefix: match[2])
|
123
|
+
flag_def = flag_value_context.tool.resolve_flag(match[1]).unique_flag
|
124
|
+
return [] unless flag_def
|
125
|
+
flag_def.value_completion.call(flag_value_context)
|
126
|
+
end
|
127
|
+
|
128
|
+
def subtool_or_arg_candidates(context)
|
129
|
+
return [] if context.arg_parser.active_flag_def
|
130
|
+
return [] if context.arg_parser.flags_allowed? && context.fragment.start_with?("-")
|
131
|
+
subtool_candidates(context) || arg_candidates(context)
|
132
|
+
end
|
133
|
+
|
134
|
+
def subtool_candidates(context)
|
135
|
+
return if !@complete_subtools || !context.args.empty?
|
136
|
+
tool_name, prefix, fragment = analyze_subtool_fragment(context)
|
137
|
+
return unless tool_name
|
138
|
+
subtools = context.cli.loader.list_subtools(tool_name,
|
139
|
+
include_hidden: @include_hidden_subtools)
|
140
|
+
return if subtools.empty?
|
141
|
+
candidates = []
|
142
|
+
subtools.each do |subtool|
|
143
|
+
name = subtool.simple_name
|
144
|
+
candidates << Completion::Candidate.new("#{prefix}#{name}") if name.start_with?(fragment)
|
145
|
+
end
|
146
|
+
candidates
|
147
|
+
end
|
148
|
+
|
149
|
+
def analyze_subtool_fragment(context)
|
150
|
+
tool_name = context.tool.full_name
|
151
|
+
prefix = ""
|
152
|
+
fragment = context.fragment
|
153
|
+
delims = context.cli.extra_delimiters
|
154
|
+
unless context.fragment_prefix.empty?
|
155
|
+
if !context.fragment_prefix.end_with?(":") || !delims.include?(":")
|
156
|
+
return [nil, nil, nil]
|
157
|
+
end
|
158
|
+
tool_name += context.fragment_prefix.split(":")
|
159
|
+
end
|
160
|
+
unless delims.empty?
|
161
|
+
delims_regex = ::Regexp.escape(delims)
|
162
|
+
if (match = /\A((.+)[#{delims_regex}])(.*)\z/.match(fragment))
|
163
|
+
fragment = match[3]
|
164
|
+
tool_name += match[2].split(/[#{delims_regex}]/)
|
165
|
+
prefix = match[1]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
[tool_name, prefix, fragment]
|
169
|
+
end
|
170
|
+
|
171
|
+
def arg_candidates(context)
|
172
|
+
return unless @complete_args
|
173
|
+
arg_def = context.arg_parser.next_arg_def
|
174
|
+
return [] unless arg_def
|
175
|
+
arg_def.completion.call(context)
|
176
|
+
end
|
177
|
+
|
178
|
+
def plain_flag_candidates(context)
|
179
|
+
return [] if !@complete_flags || context[:disable_flags]
|
180
|
+
arg_parser = context.arg_parser
|
181
|
+
return [] unless arg_parser.flags_allowed?
|
182
|
+
flag_def = arg_parser.active_flag_def
|
183
|
+
return [] if flag_def && flag_def.value_type == :required
|
184
|
+
return [] if context.fragment =~ /\A[^-]/ || !context.fragment_prefix.empty?
|
185
|
+
context.tool.flags.flat_map do |flag|
|
186
|
+
flag.flag_completion.call(context)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def flag_value_candidates(context)
|
191
|
+
return unless @complete_flag_values
|
192
|
+
arg_parser = context.arg_parser
|
193
|
+
flag_def = arg_parser.active_flag_def
|
194
|
+
return [] unless flag_def
|
195
|
+
return [] if @complete_flags && arg_parser.flags_allowed? &&
|
196
|
+
flag_def.value_type == :optional && context.fragment.start_with?("-")
|
197
|
+
flag_def.value_completion.call(context)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Tool-based settings class.
|
203
|
+
#
|
204
|
+
# The following settings are supported:
|
205
|
+
#
|
206
|
+
# * `propagate_helper_methods` (_Boolean_) - Whether subtools should
|
207
|
+
# inherit methods defined by parent tools. Defaults to `false`.
|
208
|
+
#
|
209
|
+
class Settings < ::Toys::Settings
|
210
|
+
settings_attr :propagate_helper_methods, default: false
|
211
|
+
end
|
212
|
+
|
14
213
|
##
|
15
214
|
# Create a new tool.
|
16
215
|
# Should be created only from the DSL via the Loader.
|
216
|
+
#
|
17
217
|
# @private
|
18
218
|
#
|
19
|
-
def initialize(
|
219
|
+
def initialize(parent, full_name, priority, source_root, middleware_stack, middleware_lookup,
|
220
|
+
tool_class = nil)
|
20
221
|
@parent = parent
|
222
|
+
@settings = Settings.new(parent: parent&.settings)
|
21
223
|
@full_name = full_name.dup.freeze
|
22
224
|
@priority = priority
|
225
|
+
@source_root = source_root
|
23
226
|
@built_middleware = middleware_stack.build(middleware_lookup)
|
24
227
|
@subtool_middleware_stack = middleware_stack.dup
|
25
228
|
|
@@ -28,22 +231,25 @@ module Toys
|
|
28
231
|
@templates = {}
|
29
232
|
@completions = {}
|
30
233
|
|
31
|
-
|
234
|
+
@precreated_class = tool_class
|
235
|
+
|
236
|
+
reset_definition
|
32
237
|
end
|
33
238
|
|
34
239
|
##
|
35
240
|
# Reset the definition of this tool, deleting all definition data but
|
36
241
|
# leaving named acceptors, mixins, and templates intact.
|
37
242
|
# Should be called only from the DSL.
|
243
|
+
#
|
38
244
|
# @private
|
39
245
|
#
|
40
|
-
def reset_definition
|
41
|
-
@tool_class =
|
246
|
+
def reset_definition
|
247
|
+
@tool_class = @precreated_class || create_class
|
42
248
|
|
43
249
|
@source_info = nil
|
44
250
|
@definition_finished = false
|
45
251
|
|
46
|
-
@desc = WrappableString.new
|
252
|
+
@desc = WrappableString.new
|
47
253
|
@long_desc = []
|
48
254
|
|
49
255
|
@default_data = {}
|
@@ -72,6 +278,13 @@ module Toys
|
|
72
278
|
@completion = DefaultCompletion.new
|
73
279
|
end
|
74
280
|
|
281
|
+
##
|
282
|
+
# Settings for this tool
|
283
|
+
#
|
284
|
+
# @return [Toys::Tool::Settings]
|
285
|
+
#
|
286
|
+
attr_reader :settings
|
287
|
+
|
75
288
|
##
|
76
289
|
# The name of the tool as an array of strings.
|
77
290
|
# This array may not be modified.
|
@@ -87,6 +300,13 @@ module Toys
|
|
87
300
|
#
|
88
301
|
attr_reader :priority
|
89
302
|
|
303
|
+
##
|
304
|
+
# The root source info defining this tool, or nil if there is no source.
|
305
|
+
#
|
306
|
+
# @return [Toys::SourceInfo,nil]
|
307
|
+
#
|
308
|
+
attr_reader :source_root
|
309
|
+
|
90
310
|
##
|
91
311
|
# The tool class.
|
92
312
|
#
|
@@ -217,14 +437,14 @@ module Toys
|
|
217
437
|
#
|
218
438
|
# When reading, this may return an instance of one of the subclasses of
|
219
439
|
# {Toys::Completion::Base}, or a Proc that duck-types it. Generally, this
|
220
|
-
# defaults to a {Toys::
|
221
|
-
# algorithm that finds appropriate completions from flags,
|
222
|
-
# arguments, and subtools.
|
440
|
+
# defaults to a {Toys::ToolDefinition::DefaultCompletion}, providing a
|
441
|
+
# standard algorithm that finds appropriate completions from flags,
|
442
|
+
# positional arguments, and subtools.
|
223
443
|
#
|
224
444
|
# When setting, you may pass any of the following:
|
225
445
|
# * `nil` or `:default` which sets the value to a default instance.
|
226
|
-
# * A Hash of options to pass to the
|
227
|
-
# constructor.
|
446
|
+
# * A Hash of options to pass to the
|
447
|
+
# {Toys::ToolDefinition::DefaultCompletion} constructor.
|
228
448
|
# * Any other form recognized by {Toys::Completion.create}.
|
229
449
|
#
|
230
450
|
# @return [Toys::Completion::Base,Proc]
|
@@ -408,7 +628,7 @@ module Toys
|
|
408
628
|
# Get the named acceptor from this tool or its ancestors.
|
409
629
|
#
|
410
630
|
# @param name [String] The acceptor name.
|
411
|
-
# @return [
|
631
|
+
# @return [Toys::Acceptor::Base] The acceptor.
|
412
632
|
# @return [nil] if no acceptor of the given name is found.
|
413
633
|
#
|
414
634
|
def lookup_acceptor(name)
|
@@ -441,7 +661,7 @@ module Toys
|
|
441
661
|
# Get the named completion from this tool or its ancestors.
|
442
662
|
#
|
443
663
|
# @param name [String] The completion name
|
444
|
-
# @return [
|
664
|
+
# @return [Toys::Completion::Base,Proc] The completion proc.
|
445
665
|
# @return [nil] if no completion of the given name is found.
|
446
666
|
#
|
447
667
|
def lookup_completion(name)
|
@@ -451,11 +671,31 @@ module Toys
|
|
451
671
|
##
|
452
672
|
# Include the given mixin in the tool class.
|
453
673
|
#
|
454
|
-
#
|
674
|
+
# The mixin must be given as a module. You can use {#lookup_mixin} to
|
675
|
+
# resolve named mixins.
|
676
|
+
#
|
677
|
+
# @param mod [Module] The mixin module
|
455
678
|
# @return [self]
|
456
679
|
#
|
457
|
-
def include_mixin(
|
458
|
-
|
680
|
+
def include_mixin(mod, *args, **kwargs)
|
681
|
+
check_definition_state
|
682
|
+
if tool_class.included_modules.include?(mod)
|
683
|
+
raise ToolDefinitionError, "Mixin already included: #{mod.name}"
|
684
|
+
end
|
685
|
+
@includes_modules = true
|
686
|
+
if tool_class.respond_to?(:super_include)
|
687
|
+
tool_class.super_include(mod)
|
688
|
+
else
|
689
|
+
tool_class.include(mod)
|
690
|
+
end
|
691
|
+
if mod.respond_to?(:initializer)
|
692
|
+
callback = mod.initializer
|
693
|
+
add_initializer(callback, *args, **kwargs) if callback
|
694
|
+
end
|
695
|
+
if mod.respond_to?(:inclusion)
|
696
|
+
callback = mod.inclusion
|
697
|
+
tool_class.class_exec(*args, **kwargs, &callback) if callback
|
698
|
+
end
|
459
699
|
self
|
460
700
|
end
|
461
701
|
|
@@ -565,7 +805,7 @@ module Toys
|
|
565
805
|
# completion.
|
566
806
|
#
|
567
807
|
# @param name [String] The name of the completion.
|
568
|
-
# @param completion [Proc,
|
808
|
+
# @param completion [Proc,Toys::Completion::Base,Object] The completion to
|
569
809
|
# add. You can provide either a completion object, or a spec understood
|
570
810
|
# by {Toys::Completion.create}.
|
571
811
|
# @param options [Hash] Additional options to pass to the completion.
|
@@ -669,11 +909,12 @@ module Toys
|
|
669
909
|
#
|
670
910
|
# @param type [Symbol] The type of group. Default is `:optional`.
|
671
911
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
672
|
-
# description for the group. See {Toys::
|
673
|
-
# of allowed formats. Defaults to `"Flags"`.
|
912
|
+
# description for the group. See {Toys::ToolDefinition#desc} for a
|
913
|
+
# description of allowed formats. Defaults to `"Flags"`.
|
674
914
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
675
|
-
# Long description for the flag group. See
|
676
|
-
# a description of allowed
|
915
|
+
# Long description for the flag group. See
|
916
|
+
# {Toys::ToolDefinition#long_desc} for a description of allowed
|
917
|
+
# formats. Defaults to the empty array.
|
677
918
|
# @param name [String,Symbol,nil] The name of the group, or nil for no
|
678
919
|
# name.
|
679
920
|
# @param report_collisions [Boolean] If `true`, raise an exception if a
|
@@ -741,11 +982,11 @@ module Toys
|
|
741
982
|
# this flag. You may provide a group name, a FlagGroup object, or
|
742
983
|
# `nil` which denotes the default group.
|
743
984
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
744
|
-
# description for the flag. See {Toys::
|
745
|
-
# allowed formats. Defaults to the empty string.
|
985
|
+
# description for the flag. See {Toys::ToolDefinition#desc} for a
|
986
|
+
# description of allowed formats. Defaults to the empty string.
|
746
987
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
747
|
-
# Long description for the flag. See {Toys::
|
748
|
-
# description of allowed formats. Defaults to the empty array.
|
988
|
+
# Long description for the flag. See {Toys::ToolDefinition#long_desc}
|
989
|
+
# for a description of allowed formats. Defaults to the empty array.
|
749
990
|
# @param display_name [String] A display name for this flag, used in help
|
750
991
|
# text and error messages.
|
751
992
|
# @return [self]
|
@@ -808,11 +1049,11 @@ module Toys
|
|
808
1049
|
# @param display_name [String] A name to use for display (in help text and
|
809
1050
|
# error reports). Defaults to the key in upper case.
|
810
1051
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
811
|
-
# description for the arg. See {Toys::
|
812
|
-
# allowed formats. Defaults to the empty string.
|
1052
|
+
# description for the arg. See {Toys::ToolDefinition#desc} for a
|
1053
|
+
# description of allowed formats. Defaults to the empty string.
|
813
1054
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
814
|
-
# Long description for the arg. See {Toys::
|
815
|
-
# description of allowed formats. Defaults to the empty array.
|
1055
|
+
# Long description for the arg. See {Toys::ToolDefinition#long_desc}
|
1056
|
+
# for a description of allowed formats. Defaults to the empty array.
|
816
1057
|
# @return [self]
|
817
1058
|
#
|
818
1059
|
def add_required_arg(key, accept: nil, complete: nil, display_name: nil,
|
@@ -846,11 +1087,11 @@ module Toys
|
|
846
1087
|
# @param display_name [String] A name to use for display (in help text and
|
847
1088
|
# error reports). Defaults to the key in upper case.
|
848
1089
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
849
|
-
# description for the arg. See {Toys::
|
850
|
-
# allowed formats. Defaults to the empty string.
|
1090
|
+
# description for the arg. See {Toys::ToolDefinition#desc} for a
|
1091
|
+
# description of allowed formats. Defaults to the empty string.
|
851
1092
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
852
|
-
# Long description for the arg. See {Toys::
|
853
|
-
# description of allowed formats. Defaults to the empty array.
|
1093
|
+
# Long description for the arg. See {Toys::ToolDefinition#long_desc}
|
1094
|
+
# for a description of allowed formats. Defaults to the empty array.
|
854
1095
|
# @return [self]
|
855
1096
|
#
|
856
1097
|
def add_optional_arg(key, default: nil, accept: nil, complete: nil,
|
@@ -884,11 +1125,11 @@ module Toys
|
|
884
1125
|
# @param display_name [String] A name to use for display (in help text and
|
885
1126
|
# error reports). Defaults to the key in upper case.
|
886
1127
|
# @param desc [String,Array<String>,Toys::WrappableString] Short
|
887
|
-
# description for the arg. See {Toys::
|
888
|
-
# allowed formats. Defaults to the empty string.
|
1128
|
+
# description for the arg. See {Toys::ToolDefinition#desc} for a
|
1129
|
+
# description of allowed formats. Defaults to the empty string.
|
889
1130
|
# @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
|
890
|
-
# Long description for the arg. See {Toys::
|
891
|
-
# description of allowed formats. Defaults to the empty array.
|
1131
|
+
# Long description for the arg. See {Toys::ToolDefinition#long_desc}
|
1132
|
+
# for a description of allowed formats. Defaults to the empty array.
|
892
1133
|
# @return [self]
|
893
1134
|
#
|
894
1135
|
def set_remaining_args(key, default: [], accept: nil, complete: nil,
|
@@ -910,7 +1151,9 @@ module Toys
|
|
910
1151
|
#
|
911
1152
|
def run_handler=(proc)
|
912
1153
|
check_definition_state(is_method: true)
|
913
|
-
|
1154
|
+
tool_class.class_eval do
|
1155
|
+
define_method(:run, &proc)
|
1156
|
+
end
|
914
1157
|
end
|
915
1158
|
|
916
1159
|
##
|
@@ -966,7 +1209,7 @@ module Toys
|
|
966
1209
|
end
|
967
1210
|
|
968
1211
|
##
|
969
|
-
# Set the completion strategy for this
|
1212
|
+
# Set the completion strategy for this ToolDefinition.
|
970
1213
|
#
|
971
1214
|
# See {#completion} for details.
|
972
1215
|
#
|
@@ -1032,6 +1275,7 @@ module Toys
|
|
1032
1275
|
|
1033
1276
|
##
|
1034
1277
|
# Lookup the custom context directory in this tool and its ancestors.
|
1278
|
+
#
|
1035
1279
|
# @private
|
1036
1280
|
#
|
1037
1281
|
def lookup_custom_context_directory
|
@@ -1040,6 +1284,7 @@ module Toys
|
|
1040
1284
|
|
1041
1285
|
##
|
1042
1286
|
# Mark this tool as having at least one module included.
|
1287
|
+
#
|
1043
1288
|
# @private
|
1044
1289
|
#
|
1045
1290
|
def mark_includes_modules
|
@@ -1051,12 +1296,13 @@ module Toys
|
|
1051
1296
|
##
|
1052
1297
|
# Complete definition and run middleware configs. Should be called from
|
1053
1298
|
# the Loader only.
|
1299
|
+
#
|
1054
1300
|
# @private
|
1055
1301
|
#
|
1056
1302
|
def finish_definition(loader)
|
1057
1303
|
unless @definition_finished
|
1058
1304
|
ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
|
1059
|
-
config_proc = proc {}
|
1305
|
+
config_proc = proc { nil }
|
1060
1306
|
@built_middleware.reverse_each do |middleware|
|
1061
1307
|
config_proc = make_config_proc(middleware, loader, config_proc)
|
1062
1308
|
end
|
@@ -1072,6 +1318,7 @@ module Toys
|
|
1072
1318
|
|
1073
1319
|
##
|
1074
1320
|
# Run all initializers against a context. Called from the Runner.
|
1321
|
+
#
|
1075
1322
|
# @private
|
1076
1323
|
#
|
1077
1324
|
def run_initializers(context)
|
@@ -1083,6 +1330,7 @@ module Toys
|
|
1083
1330
|
##
|
1084
1331
|
# Check that the tool can still be defined. Should be called internally
|
1085
1332
|
# or from the DSL only.
|
1333
|
+
#
|
1086
1334
|
# @private
|
1087
1335
|
#
|
1088
1336
|
def check_definition_state(is_arg: false, is_method: false)
|
@@ -1101,194 +1349,12 @@ module Toys
|
|
1101
1349
|
self
|
1102
1350
|
end
|
1103
1351
|
|
1104
|
-
|
1105
|
-
# A Completion that implements the default algorithm for a tool.
|
1106
|
-
#
|
1107
|
-
class DefaultCompletion < Completion::Base
|
1108
|
-
##
|
1109
|
-
# Create a completion given configuration options.
|
1110
|
-
#
|
1111
|
-
# @param complete_subtools [Boolean] Whether to complete subtool names
|
1112
|
-
# @param include_hidden_subtools [Boolean] Whether to include hidden
|
1113
|
-
# subtools (i.e. those beginning with an underscore)
|
1114
|
-
# @param complete_args [Boolean] Whether to complete positional args
|
1115
|
-
# @param complete_flags [Boolean] Whether to complete flag names
|
1116
|
-
# @param complete_flag_values [Boolean] Whether to complete flag values
|
1117
|
-
# @param delegation_target [Array<String>,nil] Delegation target, or
|
1118
|
-
# `nil` if none.
|
1119
|
-
#
|
1120
|
-
def initialize(complete_subtools: true, include_hidden_subtools: false,
|
1121
|
-
complete_args: true, complete_flags: true, complete_flag_values: true,
|
1122
|
-
delegation_target: nil)
|
1123
|
-
@complete_subtools = complete_subtools
|
1124
|
-
@include_hidden_subtools = include_hidden_subtools
|
1125
|
-
@complete_flags = complete_flags
|
1126
|
-
@complete_args = complete_args
|
1127
|
-
@complete_flag_values = complete_flag_values
|
1128
|
-
@delegation_target = delegation_target
|
1129
|
-
end
|
1130
|
-
|
1131
|
-
##
|
1132
|
-
# Whether to complete subtool names
|
1133
|
-
# @return [Boolean]
|
1134
|
-
#
|
1135
|
-
def complete_subtools?
|
1136
|
-
@complete_subtools
|
1137
|
-
end
|
1138
|
-
|
1139
|
-
##
|
1140
|
-
# Whether to include hidden subtools
|
1141
|
-
# @return [Boolean]
|
1142
|
-
#
|
1143
|
-
def include_hidden_subtools?
|
1144
|
-
@include_hidden_subtools
|
1145
|
-
end
|
1146
|
-
|
1147
|
-
##
|
1148
|
-
# Whether to complete flags
|
1149
|
-
# @return [Boolean]
|
1150
|
-
#
|
1151
|
-
def complete_flags?
|
1152
|
-
@complete_flags
|
1153
|
-
end
|
1154
|
-
|
1155
|
-
##
|
1156
|
-
# Whether to complete positional args
|
1157
|
-
# @return [Boolean]
|
1158
|
-
#
|
1159
|
-
def complete_args?
|
1160
|
-
@complete_args
|
1161
|
-
end
|
1162
|
-
|
1163
|
-
##
|
1164
|
-
# Whether to complete flag values
|
1165
|
-
# @return [Boolean]
|
1166
|
-
#
|
1167
|
-
def complete_flag_values?
|
1168
|
-
@complete_flag_values
|
1169
|
-
end
|
1170
|
-
|
1171
|
-
##
|
1172
|
-
# Delegation target, or nil for none.
|
1173
|
-
# @return [Array<String>] if there is a delegation target
|
1174
|
-
# @return [nil] if there is no delegation target
|
1175
|
-
#
|
1176
|
-
attr_accessor :delegation_target
|
1177
|
-
|
1178
|
-
##
|
1179
|
-
# Returns candidates for the current completion.
|
1180
|
-
#
|
1181
|
-
# @param context [Toys::Completion::Context] the current completion
|
1182
|
-
# context including the string fragment.
|
1183
|
-
# @return [Array<Toys::Completion::Candidate>] an array of candidates
|
1184
|
-
#
|
1185
|
-
def call(context)
|
1186
|
-
candidates = valued_flag_candidates(context)
|
1187
|
-
return candidates if candidates
|
1188
|
-
candidates = subtool_or_arg_candidates(context)
|
1189
|
-
candidates += plain_flag_candidates(context)
|
1190
|
-
candidates += flag_value_candidates(context)
|
1191
|
-
if delegation_target
|
1192
|
-
delegate_tool = context.cli.loader.lookup_specific(delegation_target)
|
1193
|
-
if delegate_tool
|
1194
|
-
context = context.with(previous_words: delegation_target)
|
1195
|
-
candidates += delegate_tool.completion.call(context)
|
1196
|
-
end
|
1197
|
-
end
|
1198
|
-
candidates
|
1199
|
-
end
|
1200
|
-
|
1201
|
-
private
|
1202
|
-
|
1203
|
-
def valued_flag_candidates(context)
|
1204
|
-
return unless @complete_flag_values
|
1205
|
-
arg_parser = context.arg_parser
|
1206
|
-
return unless arg_parser.flags_allowed?
|
1207
|
-
active_flag_def = arg_parser.active_flag_def
|
1208
|
-
return if active_flag_def && active_flag_def.value_type == :required
|
1209
|
-
match = /\A(--\w[\?\w-]*)=(.*)\z/.match(context.fragment_prefix)
|
1210
|
-
return unless match
|
1211
|
-
flag_value_context = context.with(fragment_prefix: match[2])
|
1212
|
-
flag_def = flag_value_context.tool.resolve_flag(match[1]).unique_flag
|
1213
|
-
return [] unless flag_def
|
1214
|
-
flag_def.value_completion.call(flag_value_context)
|
1215
|
-
end
|
1216
|
-
|
1217
|
-
def subtool_or_arg_candidates(context)
|
1218
|
-
return [] if context.arg_parser.active_flag_def
|
1219
|
-
return [] if context.arg_parser.flags_allowed? && context.fragment.start_with?("-")
|
1220
|
-
subtool_candidates(context) || arg_candidates(context)
|
1221
|
-
end
|
1222
|
-
|
1223
|
-
def subtool_candidates(context)
|
1224
|
-
return if !@complete_subtools || !context.args.empty?
|
1225
|
-
tool_name, prefix, fragment = analyze_subtool_fragment(context)
|
1226
|
-
return unless tool_name
|
1227
|
-
subtools = context.cli.loader.list_subtools(tool_name,
|
1228
|
-
include_hidden: @include_hidden_subtools)
|
1229
|
-
return if subtools.empty?
|
1230
|
-
candidates = []
|
1231
|
-
subtools.each do |subtool|
|
1232
|
-
name = subtool.simple_name
|
1233
|
-
candidates << Completion::Candidate.new("#{prefix}#{name}") if name.start_with?(fragment)
|
1234
|
-
end
|
1235
|
-
candidates
|
1236
|
-
end
|
1237
|
-
|
1238
|
-
def analyze_subtool_fragment(context)
|
1239
|
-
tool_name = context.tool.full_name
|
1240
|
-
prefix = ""
|
1241
|
-
fragment = context.fragment
|
1242
|
-
delims = context.cli.extra_delimiters
|
1243
|
-
unless context.fragment_prefix.empty?
|
1244
|
-
if !context.fragment_prefix.end_with?(":") || !delims.include?(":")
|
1245
|
-
return [nil, nil, nil]
|
1246
|
-
end
|
1247
|
-
tool_name += context.fragment_prefix.split(":")
|
1248
|
-
end
|
1249
|
-
unless delims.empty?
|
1250
|
-
delims_regex = ::Regexp.escape(delims)
|
1251
|
-
if (match = /\A((.+)[#{delims_regex}])(.*)\z/.match(fragment))
|
1252
|
-
fragment = match[3]
|
1253
|
-
tool_name += match[2].split(/[#{delims_regex}]/)
|
1254
|
-
prefix = match[1]
|
1255
|
-
end
|
1256
|
-
end
|
1257
|
-
[tool_name, prefix, fragment]
|
1258
|
-
end
|
1259
|
-
|
1260
|
-
def arg_candidates(context)
|
1261
|
-
return unless @complete_args
|
1262
|
-
arg_def = context.arg_parser.next_arg_def
|
1263
|
-
return [] unless arg_def
|
1264
|
-
arg_def.completion.call(context)
|
1265
|
-
end
|
1266
|
-
|
1267
|
-
def plain_flag_candidates(context)
|
1268
|
-
return [] if !@complete_flags || context[:disable_flags]
|
1269
|
-
arg_parser = context.arg_parser
|
1270
|
-
return [] unless arg_parser.flags_allowed?
|
1271
|
-
flag_def = arg_parser.active_flag_def
|
1272
|
-
return [] if flag_def && flag_def.value_type == :required
|
1273
|
-
return [] if context.fragment =~ /\A[^-]/ || !context.fragment_prefix.empty?
|
1274
|
-
context.tool.flags.flat_map do |flag|
|
1275
|
-
flag.flag_completion.call(context)
|
1276
|
-
end
|
1277
|
-
end
|
1352
|
+
private
|
1278
1353
|
|
1279
|
-
|
1280
|
-
|
1281
|
-
arg_parser = context.arg_parser
|
1282
|
-
flag_def = arg_parser.active_flag_def
|
1283
|
-
return [] unless flag_def
|
1284
|
-
return [] if @complete_flags && arg_parser.flags_allowed? &&
|
1285
|
-
flag_def.value_type == :optional && context.fragment.start_with?("-")
|
1286
|
-
flag_def.value_completion.call(context)
|
1287
|
-
end
|
1354
|
+
def create_class
|
1355
|
+
::Class.new(@parent&.settings&.propagate_helper_methods ? @parent.tool_class : ::Toys::Context)
|
1288
1356
|
end
|
1289
1357
|
|
1290
|
-
private
|
1291
|
-
|
1292
1358
|
def make_config_proc(middleware, loader, next_config)
|
1293
1359
|
if middleware.respond_to?(:config)
|
1294
1360
|
proc { middleware.config(self, loader, &next_config) }
|