toys-core 0.12.2 → 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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +4 -1
  5. data/docs/guide.md +1 -1
  6. data/lib/toys/acceptor.rb +10 -1
  7. data/lib/toys/arg_parser.rb +1 -0
  8. data/lib/toys/cli.rb +127 -107
  9. data/lib/toys/compat.rb +54 -3
  10. data/lib/toys/completion.rb +15 -5
  11. data/lib/toys/context.rb +22 -20
  12. data/lib/toys/core.rb +6 -2
  13. data/lib/toys/dsl/base.rb +2 -0
  14. data/lib/toys/dsl/flag.rb +23 -17
  15. data/lib/toys/dsl/flag_group.rb +11 -7
  16. data/lib/toys/dsl/positional_arg.rb +23 -13
  17. data/lib/toys/dsl/tool.rb +10 -6
  18. data/lib/toys/errors.rb +63 -8
  19. data/lib/toys/flag.rb +660 -651
  20. data/lib/toys/flag_group.rb +19 -6
  21. data/lib/toys/input_file.rb +9 -3
  22. data/lib/toys/loader.rb +129 -115
  23. data/lib/toys/middleware.rb +45 -21
  24. data/lib/toys/mixin.rb +8 -6
  25. data/lib/toys/positional_arg.rb +18 -17
  26. data/lib/toys/settings.rb +81 -67
  27. data/lib/toys/source_info.rb +33 -24
  28. data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -0
  29. data/lib/toys/standard_middleware/apply_config.rb +1 -0
  30. data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -0
  31. data/lib/toys/standard_middleware/set_default_descriptions.rb +1 -0
  32. data/lib/toys/standard_middleware/show_help.rb +2 -0
  33. data/lib/toys/standard_middleware/show_root_version.rb +2 -0
  34. data/lib/toys/standard_mixins/bundler.rb +22 -14
  35. data/lib/toys/standard_mixins/exec.rb +31 -20
  36. data/lib/toys/standard_mixins/fileutils.rb +3 -1
  37. data/lib/toys/standard_mixins/gems.rb +21 -17
  38. data/lib/toys/standard_mixins/git_cache.rb +5 -7
  39. data/lib/toys/standard_mixins/highline.rb +8 -8
  40. data/lib/toys/standard_mixins/terminal.rb +5 -5
  41. data/lib/toys/standard_mixins/xdg.rb +5 -5
  42. data/lib/toys/template.rb +9 -7
  43. data/lib/toys/tool_definition.rb +209 -202
  44. data/lib/toys/utils/completion_engine.rb +7 -2
  45. data/lib/toys/utils/exec.rb +158 -127
  46. data/lib/toys/utils/gems.rb +81 -57
  47. data/lib/toys/utils/git_cache.rb +674 -45
  48. data/lib/toys/utils/help_text.rb +27 -3
  49. data/lib/toys/utils/terminal.rb +10 -2
  50. data/lib/toys/wrappable_string.rb +9 -2
  51. data/lib/toys-core.rb +14 -5
  52. metadata +4 -4
data/lib/toys/flag.rb CHANGED
@@ -6,6 +6,367 @@ module Toys
6
6
  # key. The flags within a single Flag definition are synonyms.
7
7
  #
8
8
  class Flag
9
+ ##
10
+ # Representation of a single flag.
11
+ #
12
+ class Syntax
13
+ # rubocop:disable Style/PerlBackrefs
14
+
15
+ ##
16
+ # Parse flag syntax
17
+ # @param str [String] syntax.
18
+ #
19
+ def initialize(str)
20
+ case str
21
+ when /\A(-([?\w]))\z/
22
+ setup(str, $1, nil, $1, $2, :short, nil, nil, nil, nil)
23
+ when /\A(-([?\w]))(?:( ?)\[|\[( ))(\w+)\]\z/
24
+ setup(str, $1, nil, $1, $2, :short, :value, :optional, $3 || $4, $5)
25
+ when /\A(-([?\w]))( ?)(\w+)\z/
26
+ setup(str, $1, nil, $1, $2, :short, :value, :required, $3, $4)
27
+ when /\A--\[no-\](\w[?\w-]*)\z/
28
+ setup(str, "--#{$1}", "--no-#{$1}", str, $1, :long, :boolean, nil, nil, nil)
29
+ when /\A(--(\w[?\w-]*))\z/
30
+ setup(str, $1, nil, $1, $2, :long, nil, nil, nil, nil)
31
+ when /\A(--(\w[?\w-]*))(?:([= ])\[|\[([= ]))(\w+)\]\z/
32
+ setup(str, $1, nil, $1, $2, :long, :value, :optional, $3 || $4, $5)
33
+ when /\A(--(\w[?\w-]*))([= ])(\w+)\z/
34
+ setup(str, $1, nil, $1, $2, :long, :value, :required, $3, $4)
35
+ else
36
+ raise ToolDefinitionError, "Illegal flag: #{str.inspect}"
37
+ end
38
+ end
39
+
40
+ # rubocop:enable Style/PerlBackrefs
41
+
42
+ ##
43
+ # The original string that was parsed to produce this syntax.
44
+ # @return [String]
45
+ #
46
+ attr_reader :original_str
47
+
48
+ ##
49
+ # The flags (without values) corresponding to this syntax.
50
+ # @return [Array<String>]
51
+ #
52
+ attr_reader :flags
53
+
54
+ ##
55
+ # The flag (without values) corresponding to the normal "positive" form
56
+ # of this flag.
57
+ # @return [String]
58
+ #
59
+ attr_reader :positive_flag
60
+
61
+ ##
62
+ # The flag (without values) corresponding to the "negative" form of this
63
+ # flag, if any. i.e. if the original string was `"--[no-]abc"`, the
64
+ # negative flag is `"--no-abc"`.
65
+ # @return [String] The negative form.
66
+ # @return [nil] if the flag has no negative form.
67
+ #
68
+ attr_reader :negative_flag
69
+
70
+ ##
71
+ # The original string with the value (if any) stripped, but retaining
72
+ # the `[no-]` prefix if present.
73
+ # @return [String]
74
+ #
75
+ attr_reader :str_without_value
76
+
77
+ ##
78
+ # A string used to sort this flag compared with others.
79
+ # @return [String]
80
+ #
81
+ attr_reader :sort_str
82
+
83
+ ##
84
+ # The style of flag (`:long` or `:short`).
85
+ # @return [:long] if this is a long flag (i.e. double hyphen)
86
+ # @return [:short] if this is a short flag (i.e. single hyphen with one
87
+ # character).
88
+ #
89
+ attr_reader :flag_style
90
+
91
+ ##
92
+ # The type of flag (`:boolean` or `:value`)
93
+ # @return [:boolean] if this is a boolean flag (i.e. no value)
94
+ # @return [:value] if this flag takes a value (even if optional)
95
+ #
96
+ attr_reader :flag_type
97
+
98
+ ##
99
+ # The type of value (`:required` or `:optional`)
100
+ # @return [:required] if this flag takes a required value
101
+ # @return [:optional] if this flag takes an optional value
102
+ # @return [nil] if this flag is a boolean flag
103
+ #
104
+ attr_reader :value_type
105
+
106
+ ##
107
+ # The default delimiter used for the value of this flag. This could be
108
+ # `""` or `" "` for a short flag, or `" "` or `"="` for a long flag.
109
+ # @return [String] delimiter
110
+ # @return [nil] if this flag is a boolean flag
111
+ #
112
+ attr_reader :value_delim
113
+
114
+ ##
115
+ # The default "label" for the value. e.g. in `--abc=VAL` the label is
116
+ # `"VAL"`.
117
+ # @return [String] the label
118
+ # @return [nil] if this flag is a boolean flag
119
+ #
120
+ attr_reader :value_label
121
+
122
+ ##
123
+ # A canonical string representing this flag's syntax, normalized to match
124
+ # the type, delimiters, etc. settings of other flag syntaxes. This is
125
+ # generally used in help strings to represent this flag.
126
+ # @return [String]
127
+ #
128
+ attr_reader :canonical_str
129
+
130
+ ##
131
+ # @private
132
+ #
133
+ def configure_canonical(canonical_flag_type, canonical_value_type,
134
+ canonical_value_label, canonical_value_delim)
135
+ return unless flag_type.nil?
136
+ @flag_type = canonical_flag_type
137
+ return unless canonical_flag_type == :value
138
+ @value_type = canonical_value_type
139
+ canonical_value_delim = "" if canonical_value_delim == "=" && flag_style == :short
140
+ canonical_value_delim = "=" if canonical_value_delim == "" && flag_style == :long
141
+ @value_delim = canonical_value_delim
142
+ @value_label = canonical_value_label
143
+ label = @value_type == :optional ? "[#{@value_label}]" : @value_label
144
+ @canonical_str = "#{str_without_value}#{@value_delim}#{label}"
145
+ end
146
+
147
+ private
148
+
149
+ def setup(original_str, positive_flag, negative_flag, str_without_value, sort_str,
150
+ flag_style, flag_type, value_type, value_delim, value_label)
151
+ @original_str = original_str
152
+ @positive_flag = positive_flag
153
+ @negative_flag = negative_flag
154
+ @flags = [positive_flag]
155
+ @flags << negative_flag if negative_flag
156
+ @str_without_value = str_without_value
157
+ @sort_str = sort_str
158
+ @flag_style = flag_style
159
+ @flag_type = flag_type
160
+ @value_type = value_type
161
+ @value_delim = value_delim
162
+ @value_label = value_label ? value_label.upcase : value_label
163
+ @canonical_str = original_str
164
+ end
165
+ end
166
+
167
+ ##
168
+ # The result of looking up a flag by name.
169
+ #
170
+ class Resolution
171
+ ##
172
+ # @private
173
+ #
174
+ def initialize(str)
175
+ @string = str
176
+ @flags = []
177
+ @found_exact = false
178
+ end
179
+
180
+ ##
181
+ # The flag string that was looked up
182
+ # @return [String]
183
+ #
184
+ attr_reader :string
185
+
186
+ ##
187
+ # Whether an exact match of the string was found
188
+ # @return [Boolean]
189
+ #
190
+ def found_exact?
191
+ @found_exact
192
+ end
193
+
194
+ ##
195
+ # The number of matches that were found.
196
+ # @return [Integer]
197
+ #
198
+ def count
199
+ @flags.size
200
+ end
201
+
202
+ ##
203
+ # Whether a single unique match was found.
204
+ # @return [Boolean]
205
+ #
206
+ def found_unique?
207
+ @flags.size == 1
208
+ end
209
+
210
+ ##
211
+ # Whether no matches were found.
212
+ # @return [Boolean]
213
+ #
214
+ def not_found?
215
+ @flags.empty?
216
+ end
217
+
218
+ ##
219
+ # Whether multiple matches were found (i.e. ambiguous input).
220
+ # @return [Boolean]
221
+ #
222
+ def found_multiple?
223
+ @flags.size > 1
224
+ end
225
+
226
+ ##
227
+ # Return the unique {Toys::Flag}, or `nil` if not found or
228
+ # not unique.
229
+ # @return [Toys::Flag,nil]
230
+ #
231
+ def unique_flag
232
+ found_unique? ? @flags.first[0] : nil
233
+ end
234
+
235
+ ##
236
+ # Return the unique {Toys::Flag::Syntax}, or `nil` if not found
237
+ # or not unique.
238
+ # @return [Toys::Flag::Syntax,nil]
239
+ #
240
+ def unique_flag_syntax
241
+ found_unique? ? @flags.first[1] : nil
242
+ end
243
+
244
+ ##
245
+ # Return whether the unique match was a hit on the negative (`--no-*`)
246
+ # case, or `nil` if not found or not unique.
247
+ # @return [Boolean,nil]
248
+ #
249
+ def unique_flag_negative?
250
+ found_unique? ? @flags.first[2] : nil
251
+ end
252
+
253
+ ##
254
+ # Returns an array of the matching full flag strings.
255
+ # @return [Array<String>]
256
+ #
257
+ def matching_flag_strings
258
+ @flags.map do |_flag, flag_syntax, negative|
259
+ negative ? flag_syntax.negative_flag : flag_syntax.positive_flag
260
+ end
261
+ end
262
+
263
+ ##
264
+ # @private
265
+ #
266
+ def add!(flag, flag_syntax, negative, exact)
267
+ @flags = [] if exact && !found_exact?
268
+ if exact || !found_exact?
269
+ @flags << [flag, flag_syntax, negative]
270
+ @found_exact = exact
271
+ end
272
+ self
273
+ end
274
+
275
+ ##
276
+ # @private
277
+ #
278
+ def merge!(other)
279
+ raise "String mismatch" unless string == other.string
280
+ other.instance_variable_get(:@flags).each do |flag, flag_syntax, negative|
281
+ add!(flag, flag_syntax, negative, other.found_exact?)
282
+ end
283
+ self
284
+ end
285
+ end
286
+
287
+ ##
288
+ # A Completion that returns all possible flags associated with a
289
+ # {Toys::Flag}.
290
+ #
291
+ class DefaultCompletion < Completion::Base
292
+ ##
293
+ # Create a completion given configuration options.
294
+ #
295
+ # @param flag [Toys::Flag] The flag definition.
296
+ # @param include_short [Boolean] Whether to include short flags.
297
+ # @param include_long [Boolean] Whether to include long flags.
298
+ # @param include_negative [Boolean] Whether to include `--no-*` forms.
299
+ #
300
+ def initialize(flag:, include_short: true, include_long: true, include_negative: true)
301
+ super()
302
+ @flag = flag
303
+ @include_short = include_short
304
+ @include_long = include_long
305
+ @include_negative = include_negative
306
+ end
307
+
308
+ ##
309
+ # Whether to include short flags
310
+ # @return [Boolean]
311
+ #
312
+ def include_short?
313
+ @include_short
314
+ end
315
+
316
+ ##
317
+ # Whether to include long flags
318
+ # @return [Boolean]
319
+ #
320
+ def include_long?
321
+ @include_long
322
+ end
323
+
324
+ ##
325
+ # Whether to include negative long flags
326
+ # @return [Boolean]
327
+ #
328
+ def include_negative?
329
+ @include_negative
330
+ end
331
+
332
+ ##
333
+ # Returns candidates for the current completion.
334
+ #
335
+ # @param context [Toys::Completion::Context] the current completion
336
+ # context including the string fragment.
337
+ # @return [Array<Toys::Completion::Candidate>] an array of candidates
338
+ #
339
+ def call(context)
340
+ results =
341
+ if @include_short && @include_long && @include_negative
342
+ @flag.effective_flags
343
+ else
344
+ collect_results
345
+ end
346
+ fragment = context.fragment
347
+ results.find_all { |val| val.start_with?(fragment) }
348
+ .map { |str| Completion::Candidate.new(str) }
349
+ end
350
+
351
+ private
352
+
353
+ def collect_results
354
+ results = []
355
+ if @include_short
356
+ results += @flag.short_flag_syntax.map(&:positive_flag)
357
+ end
358
+ if @include_long
359
+ results +=
360
+ if @include_negative
361
+ @flag.long_flag_syntax.flat_map(&:flags)
362
+ else
363
+ @flag.long_flag_syntax.map(&:positive_flag)
364
+ end
365
+ end
366
+ results
367
+ end
368
+ end
369
+
9
370
  ##
10
371
  # The set handler replaces the previous value.
11
372
  # @return [Proc]
@@ -24,30 +385,6 @@ module Toys
24
385
  #
25
386
  DEFAULT_HANDLER = SET_HANDLER
26
387
 
27
- ##
28
- # Create a Flag definition.
29
- # This argument list is subject to change. Use {Toys::Flag.create} instead
30
- # for a more stable interface.
31
- # @private
32
- #
33
- def initialize(key, flags, used_flags, report_collisions, acceptor, handler, default,
34
- flag_completion, value_completion, desc, long_desc, display_name, group)
35
- @group = group
36
- @key = key
37
- @flag_syntax = Array(flags).map { |s| Syntax.new(s) }
38
- @acceptor = Acceptor.create(acceptor)
39
- @handler = resolve_handler(handler)
40
- @desc = WrappableString.make(desc)
41
- @long_desc = WrappableString.make_array(long_desc)
42
- @default = default
43
- @flag_completion = create_flag_completion(flag_completion)
44
- @value_completion = Completion.create(value_completion, **{})
45
- create_default_flag if @flag_syntax.empty?
46
- remove_used_flags(used_flags, report_collisions)
47
- canonicalize
48
- summarize(display_name)
49
- end
50
-
51
388
  ##
52
389
  # Create a flag definition.
53
390
  #
@@ -103,697 +440,369 @@ module Toys
103
440
  end
104
441
 
105
442
  ##
106
- # Returns the flag group containing this flag
107
- # @return [Toys::FlagGroup]
108
- #
109
- attr_reader :group
110
-
111
- ##
112
- # Returns the key.
113
- # @return [Symbol]
114
- #
115
- attr_reader :key
116
-
117
- ##
118
- # Returns an array of Flag::Syntax for the flags.
119
- # @return [Array<Toys::Flag::Syntax>]
120
- #
121
- attr_reader :flag_syntax
122
-
123
- ##
124
- # Returns the effective acceptor.
125
- # @return [Toys::Acceptor::Base]
126
- #
127
- attr_reader :acceptor
128
-
129
- ##
130
- # Returns the default value, which may be `nil`.
131
- # @return [Object]
132
- #
133
- attr_reader :default
134
-
135
- ##
136
- # The short description string.
137
- #
138
- # When reading, this is always returned as a {Toys::WrappableString}.
139
- #
140
- # When setting, the description may be provided as any of the following:
141
- # * A {Toys::WrappableString}.
142
- # * A normal String, which will be transformed into a
143
- # {Toys::WrappableString} using spaces as word delimiters.
144
- # * An Array of String, which will be transformed into a
145
- # {Toys::WrappableString} where each array element represents an
146
- # individual word for wrapping.
147
- #
148
- # @return [Toys::WrappableString]
149
- #
150
- attr_reader :desc
151
-
152
- ##
153
- # The long description strings.
154
- #
155
- # When reading, this is returned as an Array of {Toys::WrappableString}
156
- # representing the lines in the description.
157
- #
158
- # When setting, the description must be provided as an Array where *each
159
- # element* may be any of the following:
160
- # * A {Toys::WrappableString} representing one line.
161
- # * A normal String representing a line. This will be transformed into a
162
- # {Toys::WrappableString} using spaces as word delimiters.
163
- # * An Array of String representing a line. This will be transformed into
164
- # a {Toys::WrappableString} where each array element represents an
165
- # individual word for wrapping.
166
- #
167
- # @return [Array<Toys::WrappableString>]
168
- #
169
- attr_reader :long_desc
170
-
171
- ##
172
- # The handler for setting/updating the value.
173
- # @return [Proc]
174
- #
175
- attr_reader :handler
176
-
177
- ##
178
- # The proc that determines shell completions for the flag.
179
- # @return [Proc,Toys::Completion::Base]
180
- #
181
- attr_reader :flag_completion
182
-
183
- ##
184
- # The proc that determines shell completions for the value.
185
- # @return [Proc,Toys::Completion::Base]
186
- #
187
- attr_reader :value_completion
188
-
189
- ##
190
- # The type of flag.
191
- #
192
- # @return [:boolean] if the flag is a simple boolean switch
193
- # @return [:value] if the flag sets a value
194
- #
195
- attr_reader :flag_type
196
-
197
- ##
198
- # The type of value.
199
- #
200
- # @return [:required] if the flag type is `:value` and the value is
201
- # required.
202
- # @return [:optional] if the flag type is `:value` and the value is
203
- # optional.
204
- # @return [nil] if the flag type is not `:value`.
205
- #
206
- attr_reader :value_type
207
-
208
- ##
209
- # The string label for the value as it should display in help.
210
- # @return [String] The label
211
- # @return [nil] if the flag type is not `:value`.
212
- #
213
- attr_reader :value_label
214
-
215
- ##
216
- # The value delimiter, which may be `""`, `" "`, or `"="`.
217
- #
218
- # @return [String] The delimiter
219
- # @return [nil] if the flag type is not `:value`.
220
- #
221
- attr_reader :value_delim
222
-
223
- ##
224
- # The display name of this flag.
225
- # @return [String]
226
- #
227
- attr_reader :display_name
228
-
229
- ##
230
- # A string that can be used to sort this flag
231
- # @return [String]
232
- #
233
- attr_reader :sort_str
234
-
235
- ##
236
- # An array of Flag::Syntax including only short (single dash) flags.
237
- # @return [Array<Flag::Syntax>]
238
- #
239
- def short_flag_syntax
240
- @short_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == :short }
241
- end
242
-
243
- ##
244
- # An array of Flag::Syntax including only long (double-dash) flags.
245
- # @return [Array<Flag::Syntax>]
246
- #
247
- def long_flag_syntax
248
- @long_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == :long }
249
- end
250
-
251
- ##
252
- # The list of all effective flags used.
253
- # @return [Array<String>]
254
- #
255
- def effective_flags
256
- @effective_flags ||= flag_syntax.flat_map(&:flags)
257
- end
258
-
259
- ##
260
- # Look up the flag by string. Returns an object that indicates whether
261
- # the given string matched this flag, whether the match was unique, and
262
- # other pertinent information.
263
- #
264
- # @param str [String] Flag string to look up
265
- # @return [Toys::Flag::Resolution] Information about the match.
443
+ # Returns the flag group containing this flag
444
+ # @return [Toys::FlagGroup]
266
445
  #
267
- def resolve(str)
268
- resolution = Resolution.new(str)
269
- flag_syntax.each do |fs|
270
- if fs.positive_flag == str
271
- resolution.add!(self, fs, false, true)
272
- elsif fs.negative_flag == str
273
- resolution.add!(self, fs, true, true)
274
- elsif fs.positive_flag.start_with?(str)
275
- resolution.add!(self, fs, false, false)
276
- elsif fs.negative_flag.to_s.start_with?(str)
277
- resolution.add!(self, fs, true, false)
278
- end
279
- end
280
- resolution
281
- end
446
+ attr_reader :group
282
447
 
283
448
  ##
284
- # A list of canonical flag syntax strings.
285
- #
286
- # @return [Array<String>]
449
+ # Returns the key.
450
+ # @return [Symbol]
287
451
  #
288
- def canonical_syntax_strings
289
- @canonical_syntax_strings ||= flag_syntax.map(&:canonical_str)
290
- end
452
+ attr_reader :key
291
453
 
292
454
  ##
293
- # Whether this flag is active--that is, it has a nonempty flags list.
294
- #
295
- # @return [Boolean]
455
+ # Returns an array of Flag::Syntax for the flags.
456
+ # @return [Array<Toys::Flag::Syntax>]
296
457
  #
297
- def active?
298
- !effective_flags.empty?
299
- end
458
+ attr_reader :flag_syntax
300
459
 
301
460
  ##
302
- # Set the short description string.
303
- #
304
- # See {#desc} for details.
305
- #
306
- # @param desc [Toys::WrappableString,String,Array<String>]
461
+ # Returns the effective acceptor.
462
+ # @return [Toys::Acceptor::Base]
307
463
  #
308
- def desc=(desc)
309
- @desc = WrappableString.make(desc)
310
- end
464
+ attr_reader :acceptor
311
465
 
312
466
  ##
313
- # Set the long description strings.
314
- #
315
- # See {#long_desc} for details.
316
- #
317
- # @param long_desc [Array<Toys::WrappableString,String,Array<String>>]
467
+ # Returns the default value, which may be `nil`.
468
+ # @return [Object]
318
469
  #
319
- def long_desc=(long_desc)
320
- @long_desc = WrappableString.make_array(long_desc)
321
- end
470
+ attr_reader :default
322
471
 
323
472
  ##
324
- # Append long description strings.
473
+ # The short description string.
325
474
  #
326
- # You must pass an array of lines in the long description. See {#long_desc}
327
- # for details on how each line may be represented.
475
+ # When reading, this is always returned as a {Toys::WrappableString}.
328
476
  #
329
- # @param long_desc [Array<Toys::WrappableString,String,Array<String>>]
330
- # @return [self]
477
+ # When setting, the description may be provided as any of the following:
478
+ # * A {Toys::WrappableString}.
479
+ # * A normal String, which will be transformed into a
480
+ # {Toys::WrappableString} using spaces as word delimiters.
481
+ # * An Array of String, which will be transformed into a
482
+ # {Toys::WrappableString} where each array element represents an
483
+ # individual word for wrapping.
331
484
  #
332
- def append_long_desc(long_desc)
333
- @long_desc.concat(WrappableString.make_array(long_desc))
334
- self
335
- end
336
-
337
- private
338
-
339
- def resolve_handler(handler)
340
- case handler
341
- when ::Proc
342
- handler
343
- when nil, :default
344
- DEFAULT_HANDLER
345
- when :set
346
- SET_HANDLER
347
- when :push, :append
348
- PUSH_HANDLER
349
- else
350
- raise ToolDefinitionError, "Unknown handler: #{handler.inspect}"
351
- end
352
- end
353
-
354
- def create_flag_completion(spec)
355
- spec =
356
- case spec
357
- when nil, :default
358
- {"": DefaultCompletion, flag: self}
359
- when ::Hash
360
- spec[:""].nil? ? spec.merge({"": DefaultCompletion, flag: self}) : spec
361
- else
362
- spec
363
- end
364
- Completion.create(spec, **{})
365
- end
366
-
367
- def create_default_flag
368
- key_str = key.to_s
369
- flag_str =
370
- if key_str.length == 1
371
- "-#{key_str}" if key_str =~ /[a-zA-Z0-9?]/
372
- elsif key_str.length > 1
373
- key_str = key_str.downcase.tr("_", "-").gsub(/[^a-z0-9-]/, "").sub(/^-+/, "")
374
- "--#{key_str}" unless key_str.empty?
375
- end
376
- if flag_str
377
- needs_val = @value_completion != Completion::EMPTY ||
378
- ![::Object, ::TrueClass, ::FalseClass].include?(@acceptor.well_known_spec) ||
379
- ![nil, true, false].include?(@default)
380
- flag_str = "#{flag_str} VALUE" if needs_val
381
- @flag_syntax << Syntax.new(flag_str)
382
- end
383
- end
384
-
385
- def remove_used_flags(used_flags, report_collisions)
386
- return if !used_flags && !report_collisions
387
- @flag_syntax.select! do |fs|
388
- fs.flags.all? do |f|
389
- collision = used_flags&.include?(f)
390
- if collision && report_collisions
391
- raise ToolDefinitionError,
392
- "Cannot use flag #{f.inspect} because it is already assigned or reserved."
393
- end
394
- !collision
395
- end
396
- end
397
- used_flags&.concat(effective_flags.uniq)
398
- end
399
-
400
- def canonicalize
401
- @flag_type = nil
402
- @value_type = nil
403
- @value_label = nil
404
- @value_delim = " "
405
- short_flag_syntax.reverse_each do |flag|
406
- analyze_flag_syntax(flag)
407
- end
408
- long_flag_syntax.reverse_each do |flag|
409
- analyze_flag_syntax(flag)
410
- end
411
- @flag_type ||= :boolean
412
- @value_type ||= :required if @flag_type == :value
413
- flag_syntax.each do |flag|
414
- flag.configure_canonical(@flag_type, @value_type, @value_label, @value_delim)
415
- end
416
- end
417
-
418
- def analyze_flag_syntax(flag)
419
- return if flag.flag_type.nil?
420
- if !@flag_type.nil? && @flag_type != flag.flag_type
421
- raise ToolDefinitionError, "Cannot have both value and boolean flags for #{key.inspect}"
422
- end
423
- @flag_type = flag.flag_type
424
- return unless @flag_type == :value
425
- if !@value_type.nil? && @value_type != flag.value_type
426
- raise ToolDefinitionError,
427
- "Cannot have both required and optional values for flag #{key.inspect}"
428
- end
429
- @value_type = flag.value_type
430
- @value_label = flag.value_label
431
- @value_delim = flag.value_delim
432
- end
433
-
434
- def summarize(name)
435
- @display_name =
436
- name ||
437
- long_flag_syntax.first&.canonical_str ||
438
- short_flag_syntax.first&.canonical_str ||
439
- key.to_s
440
- @sort_str =
441
- long_flag_syntax.first&.sort_str ||
442
- short_flag_syntax.first&.sort_str ||
443
- ""
444
- end
485
+ # @return [Toys::WrappableString]
486
+ #
487
+ attr_reader :desc
445
488
 
446
489
  ##
447
- # Representation of a single flag.
490
+ # The long description strings.
448
491
  #
449
- class Syntax
450
- # rubocop:disable Style/PerlBackrefs
451
-
452
- ##
453
- # Parse flag syntax
454
- # @param str [String] syntax.
455
- #
456
- def initialize(str)
457
- case str
458
- when /\A(-([?\w]))\z/
459
- setup(str, $1, nil, $1, $2, :short, nil, nil, nil, nil)
460
- when /\A(-([?\w]))(?:( ?)\[|\[( ))(\w+)\]\z/
461
- setup(str, $1, nil, $1, $2, :short, :value, :optional, $3 || $4, $5)
462
- when /\A(-([?\w]))( ?)(\w+)\z/
463
- setup(str, $1, nil, $1, $2, :short, :value, :required, $3, $4)
464
- when /\A--\[no-\](\w[?\w-]*)\z/
465
- setup(str, "--#{$1}", "--no-#{$1}", str, $1, :long, :boolean, nil, nil, nil)
466
- when /\A(--(\w[?\w-]*))\z/
467
- setup(str, $1, nil, $1, $2, :long, nil, nil, nil, nil)
468
- when /\A(--(\w[?\w-]*))(?:([= ])\[|\[([= ]))(\w+)\]\z/
469
- setup(str, $1, nil, $1, $2, :long, :value, :optional, $3 || $4, $5)
470
- when /\A(--(\w[?\w-]*))([= ])(\w+)\z/
471
- setup(str, $1, nil, $1, $2, :long, :value, :required, $3, $4)
472
- else
473
- raise ToolDefinitionError, "Illegal flag: #{str.inspect}"
474
- end
475
- end
476
-
477
- # rubocop:enable Style/PerlBackrefs
478
-
479
- ##
480
- # The original string that was parsed to produce this syntax.
481
- # @return [String]
482
- #
483
- attr_reader :original_str
484
-
485
- ##
486
- # The flags (without values) corresponding to this syntax.
487
- # @return [Array<String>]
488
- #
489
- attr_reader :flags
490
-
491
- ##
492
- # The flag (without values) corresponding to the normal "positive" form
493
- # of this flag.
494
- # @return [String]
495
- #
496
- attr_reader :positive_flag
492
+ # When reading, this is returned as an Array of {Toys::WrappableString}
493
+ # representing the lines in the description.
494
+ #
495
+ # When setting, the description must be provided as an Array where *each
496
+ # element* may be any of the following:
497
+ # * A {Toys::WrappableString} representing one line.
498
+ # * A normal String representing a line. This will be transformed into a
499
+ # {Toys::WrappableString} using spaces as word delimiters.
500
+ # * An Array of String representing a line. This will be transformed into
501
+ # a {Toys::WrappableString} where each array element represents an
502
+ # individual word for wrapping.
503
+ #
504
+ # @return [Array<Toys::WrappableString>]
505
+ #
506
+ attr_reader :long_desc
497
507
 
498
- ##
499
- # The flag (without values) corresponding to the "negative" form of this
500
- # flag, if any. i.e. if the original string was `"--[no-]abc"`, the
501
- # negative flag is `"--no-abc"`.
502
- # @return [String] The negative form.
503
- # @return [nil] if the flag has no negative form.
504
- #
505
- attr_reader :negative_flag
508
+ ##
509
+ # The handler for setting/updating the value.
510
+ # @return [Proc]
511
+ #
512
+ attr_reader :handler
506
513
 
507
- ##
508
- # The original string with the value (if any) stripped, but retaining
509
- # the `[no-]` prefix if present.
510
- # @return [String]
511
- #
512
- attr_reader :str_without_value
514
+ ##
515
+ # The proc that determines shell completions for the flag.
516
+ # @return [Proc,Toys::Completion::Base]
517
+ #
518
+ attr_reader :flag_completion
513
519
 
514
- ##
515
- # A string used to sort this flag compared with others.
516
- # @return [String]
517
- #
518
- attr_reader :sort_str
520
+ ##
521
+ # The proc that determines shell completions for the value.
522
+ # @return [Proc,Toys::Completion::Base]
523
+ #
524
+ attr_reader :value_completion
519
525
 
520
- ##
521
- # The style of flag (`:long` or `:short`).
522
- # @return [:long] if this is a long flag (i.e. double hyphen)
523
- # @return [:short] if this is a short flag (i.e. single hyphen with one
524
- # character).
525
- #
526
- attr_reader :flag_style
526
+ ##
527
+ # The type of flag.
528
+ #
529
+ # @return [:boolean] if the flag is a simple boolean switch
530
+ # @return [:value] if the flag sets a value
531
+ #
532
+ attr_reader :flag_type
527
533
 
528
- ##
529
- # The type of flag (`:boolean` or `:value`)
530
- # @return [:boolean] if this is a boolean flag (i.e. no value)
531
- # @return [:value] if this flag takes a value (even if optional)
532
- #
533
- attr_reader :flag_type
534
+ ##
535
+ # The type of value.
536
+ #
537
+ # @return [:required] if the flag type is `:value` and the value is
538
+ # required.
539
+ # @return [:optional] if the flag type is `:value` and the value is
540
+ # optional.
541
+ # @return [nil] if the flag type is not `:value`.
542
+ #
543
+ attr_reader :value_type
534
544
 
535
- ##
536
- # The type of value (`:required` or `:optional`)
537
- # @return [:required] if this flag takes a required value
538
- # @return [:optional] if this flag takes an optional value
539
- # @return [nil] if this flag is a boolean flag
540
- #
541
- attr_reader :value_type
545
+ ##
546
+ # The string label for the value as it should display in help.
547
+ # @return [String] The label
548
+ # @return [nil] if the flag type is not `:value`.
549
+ #
550
+ attr_reader :value_label
542
551
 
543
- ##
544
- # The default delimiter used for the value of this flag. This could be
545
- # `""` or `" "` for a short flag, or `" "` or `"="` for a long flag.
546
- # @return [String] delimiter
547
- # @return [nil] if this flag is a boolean flag
548
- #
549
- attr_reader :value_delim
552
+ ##
553
+ # The value delimiter, which may be `""`, `" "`, or `"="`.
554
+ #
555
+ # @return [String] The delimiter
556
+ # @return [nil] if the flag type is not `:value`.
557
+ #
558
+ attr_reader :value_delim
550
559
 
551
- ##
552
- # The default "label" for the value. e.g. in `--abc=VAL` the label is
553
- # `"VAL"`.
554
- # @return [String] the label
555
- # @return [nil] if this flag is a boolean flag
556
- #
557
- attr_reader :value_label
560
+ ##
561
+ # The display name of this flag.
562
+ # @return [String]
563
+ #
564
+ attr_reader :display_name
558
565
 
559
- ##
560
- # A canonical string representing this flag's syntax, normalized to match
561
- # the type, delimiters, etc. settings of other flag syntaxes. This is
562
- # generally used in help strings to represent this flag.
563
- # @return [String]
564
- #
565
- attr_reader :canonical_str
566
+ ##
567
+ # A string that can be used to sort this flag
568
+ # @return [String]
569
+ #
570
+ attr_reader :sort_str
566
571
 
567
- ## @private
568
- def configure_canonical(canonical_flag_type, canonical_value_type,
569
- canonical_value_label, canonical_value_delim)
570
- return unless flag_type.nil?
571
- @flag_type = canonical_flag_type
572
- return unless canonical_flag_type == :value
573
- @value_type = canonical_value_type
574
- canonical_value_delim = "" if canonical_value_delim == "=" && flag_style == :short
575
- canonical_value_delim = "=" if canonical_value_delim == "" && flag_style == :long
576
- @value_delim = canonical_value_delim
577
- @value_label = canonical_value_label
578
- label = @value_type == :optional ? "[#{@value_label}]" : @value_label
579
- @canonical_str = "#{str_without_value}#{@value_delim}#{label}"
580
- end
572
+ ##
573
+ # An array of Flag::Syntax including only short (single dash) flags.
574
+ # @return [Array<Flag::Syntax>]
575
+ #
576
+ def short_flag_syntax
577
+ @short_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == :short }
578
+ end
581
579
 
582
- private
580
+ ##
581
+ # An array of Flag::Syntax including only long (double-dash) flags.
582
+ # @return [Array<Flag::Syntax>]
583
+ #
584
+ def long_flag_syntax
585
+ @long_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == :long }
586
+ end
583
587
 
584
- def setup(original_str, positive_flag, negative_flag, str_without_value, sort_str,
585
- flag_style, flag_type, value_type, value_delim, value_label)
586
- @original_str = original_str
587
- @positive_flag = positive_flag
588
- @negative_flag = negative_flag
589
- @flags = [positive_flag]
590
- @flags << negative_flag if negative_flag
591
- @str_without_value = str_without_value
592
- @sort_str = sort_str
593
- @flag_style = flag_style
594
- @flag_type = flag_type
595
- @value_type = value_type
596
- @value_delim = value_delim
597
- @value_label = value_label ? value_label.upcase : value_label
598
- @canonical_str = original_str
599
- end
588
+ ##
589
+ # The list of all effective flags used.
590
+ # @return [Array<String>]
591
+ #
592
+ def effective_flags
593
+ @effective_flags ||= flag_syntax.flat_map(&:flags)
600
594
  end
601
595
 
602
596
  ##
603
- # The result of looking up a flag by name.
597
+ # Look up the flag by string. Returns an object that indicates whether
598
+ # the given string matched this flag, whether the match was unique, and
599
+ # other pertinent information.
604
600
  #
605
- class Resolution
606
- ## @private
607
- def initialize(str)
608
- @string = str
609
- @flags = []
610
- @found_exact = false
601
+ # @param str [String] Flag string to look up
602
+ # @return [Toys::Flag::Resolution] Information about the match.
603
+ #
604
+ def resolve(str)
605
+ resolution = Resolution.new(str)
606
+ flag_syntax.each do |fs|
607
+ if fs.positive_flag == str
608
+ resolution.add!(self, fs, false, true)
609
+ elsif fs.negative_flag == str
610
+ resolution.add!(self, fs, true, true)
611
+ elsif fs.positive_flag.start_with?(str)
612
+ resolution.add!(self, fs, false, false)
613
+ elsif fs.negative_flag.to_s.start_with?(str)
614
+ resolution.add!(self, fs, true, false)
615
+ end
611
616
  end
617
+ resolution
618
+ end
612
619
 
613
- ##
614
- # The flag string that was looked up
615
- # @return [String]
616
- #
617
- attr_reader :string
618
-
619
- ##
620
- # Whether an exact match of the string was found
621
- # @return [Boolean]
622
- #
623
- def found_exact?
624
- @found_exact
625
- end
620
+ ##
621
+ # A list of canonical flag syntax strings.
622
+ #
623
+ # @return [Array<String>]
624
+ #
625
+ def canonical_syntax_strings
626
+ @canonical_syntax_strings ||= flag_syntax.map(&:canonical_str)
627
+ end
626
628
 
627
- ##
628
- # The number of matches that were found.
629
- # @return [Integer]
630
- #
631
- def count
632
- @flags.size
633
- end
629
+ ##
630
+ # Whether this flag is active--that is, it has a nonempty flags list.
631
+ #
632
+ # @return [Boolean]
633
+ #
634
+ def active?
635
+ !effective_flags.empty?
636
+ end
634
637
 
635
- ##
636
- # Whether a single unique match was found.
637
- # @return [Boolean]
638
- #
639
- def found_unique?
640
- @flags.size == 1
641
- end
638
+ ##
639
+ # Set the short description string.
640
+ #
641
+ # See {#desc} for details.
642
+ #
643
+ # @param desc [Toys::WrappableString,String,Array<String>]
644
+ #
645
+ def desc=(desc)
646
+ @desc = WrappableString.make(desc)
647
+ end
642
648
 
643
- ##
644
- # Whether no matches were found.
645
- # @return [Boolean]
646
- #
647
- def not_found?
648
- @flags.empty?
649
- end
649
+ ##
650
+ # Set the long description strings.
651
+ #
652
+ # See {#long_desc} for details.
653
+ #
654
+ # @param long_desc [Array<Toys::WrappableString,String,Array<String>>]
655
+ #
656
+ def long_desc=(long_desc)
657
+ @long_desc = WrappableString.make_array(long_desc)
658
+ end
650
659
 
651
- ##
652
- # Whether multiple matches were found (i.e. ambiguous input).
653
- # @return [Boolean]
654
- #
655
- def found_multiple?
656
- @flags.size > 1
657
- end
660
+ ##
661
+ # Append long description strings.
662
+ #
663
+ # You must pass an array of lines in the long description. See {#long_desc}
664
+ # for details on how each line may be represented.
665
+ #
666
+ # @param long_desc [Array<Toys::WrappableString,String,Array<String>>]
667
+ # @return [self]
668
+ #
669
+ def append_long_desc(long_desc)
670
+ @long_desc.concat(WrappableString.make_array(long_desc))
671
+ self
672
+ end
658
673
 
659
- ##
660
- # Return the unique {Toys::Flag}, or `nil` if not found or
661
- # not unique.
662
- # @return [Toys::Flag,nil]
663
- #
664
- def unique_flag
665
- found_unique? ? @flags.first[0] : nil
666
- end
674
+ ##
675
+ # Create a Flag definition.
676
+ # This argument list is subject to change. Use {Toys::Flag.create} instead
677
+ # for a more stable interface.
678
+ #
679
+ # @private
680
+ #
681
+ def initialize(key, flags, used_flags, report_collisions, acceptor, handler, default,
682
+ flag_completion, value_completion, desc, long_desc, display_name, group)
683
+ @group = group
684
+ @key = key
685
+ @flag_syntax = Array(flags).map { |s| Syntax.new(s) }
686
+ @acceptor = Acceptor.create(acceptor)
687
+ @handler = resolve_handler(handler)
688
+ @desc = WrappableString.make(desc)
689
+ @long_desc = WrappableString.make_array(long_desc)
690
+ @default = default
691
+ @flag_completion = create_flag_completion(flag_completion)
692
+ @value_completion = Completion.create(value_completion, **{})
693
+ create_default_flag if @flag_syntax.empty?
694
+ remove_used_flags(used_flags, report_collisions)
695
+ canonicalize
696
+ summarize(display_name)
697
+ end
667
698
 
668
- ##
669
- # Return the unique {Toys::Flag::Syntax}, or `nil` if not found
670
- # or not unique.
671
- # @return [Toys::Flag::Syntax,nil]
672
- #
673
- def unique_flag_syntax
674
- found_unique? ? @flags.first[1] : nil
675
- end
699
+ private
676
700
 
677
- ##
678
- # Return whether the unique match was a hit on the negative (`--no-*`)
679
- # case, or `nil` if not found or not unique.
680
- # @return [Boolean,nil]
681
- #
682
- def unique_flag_negative?
683
- found_unique? ? @flags.first[2] : nil
701
+ def resolve_handler(handler)
702
+ case handler
703
+ when ::Proc
704
+ handler
705
+ when nil, :default
706
+ DEFAULT_HANDLER
707
+ when :set
708
+ SET_HANDLER
709
+ when :push, :append
710
+ PUSH_HANDLER
711
+ else
712
+ raise ToolDefinitionError, "Unknown handler: #{handler.inspect}"
684
713
  end
714
+ end
685
715
 
686
- ##
687
- # Returns an array of the matching full flag strings.
688
- # @return [Array<String>]
689
- #
690
- def matching_flag_strings
691
- @flags.map do |_flag, flag_syntax, negative|
692
- negative ? flag_syntax.negative_flag : flag_syntax.positive_flag
716
+ def create_flag_completion(spec)
717
+ spec =
718
+ case spec
719
+ when nil, :default
720
+ {"": DefaultCompletion, flag: self}
721
+ when ::Hash
722
+ spec[:""].nil? ? spec.merge({"": DefaultCompletion, flag: self}) : spec
723
+ else
724
+ spec
693
725
  end
694
- end
726
+ Completion.create(spec, **{})
727
+ end
695
728
 
696
- ## @private
697
- def add!(flag, flag_syntax, negative, exact)
698
- @flags = [] if exact && !found_exact?
699
- if exact || !found_exact?
700
- @flags << [flag, flag_syntax, negative]
701
- @found_exact = exact
729
+ def create_default_flag
730
+ key_str = key.to_s
731
+ flag_str =
732
+ if key_str.length == 1
733
+ "-#{key_str}" if key_str =~ /[a-zA-Z0-9?]/
734
+ elsif key_str.length > 1
735
+ key_str = key_str.downcase.tr("_", "-").gsub(/[^a-z0-9-]/, "").sub(/^-+/, "")
736
+ "--#{key_str}" unless key_str.empty?
702
737
  end
703
- self
738
+ if flag_str
739
+ needs_val = @value_completion != Completion::EMPTY ||
740
+ ![::Object, ::TrueClass, ::FalseClass].include?(@acceptor.well_known_spec) ||
741
+ ![nil, true, false].include?(@default)
742
+ flag_str = "#{flag_str} VALUE" if needs_val
743
+ @flag_syntax << Syntax.new(flag_str)
704
744
  end
745
+ end
705
746
 
706
- ## @private
707
- def merge!(other)
708
- raise "String mismatch" unless string == other.string
709
- other.instance_variable_get(:@flags).each do |flag, flag_syntax, negative|
710
- add!(flag, flag_syntax, negative, other.found_exact?)
747
+ def remove_used_flags(used_flags, report_collisions)
748
+ return if !used_flags && !report_collisions
749
+ @flag_syntax.select! do |fs|
750
+ fs.flags.all? do |f|
751
+ collision = used_flags&.include?(f)
752
+ if collision && report_collisions
753
+ raise ToolDefinitionError,
754
+ "Cannot use flag #{f.inspect} because it is already assigned or reserved."
755
+ end
756
+ !collision
711
757
  end
712
- self
713
758
  end
759
+ used_flags&.concat(effective_flags.uniq)
714
760
  end
715
761
 
716
- ##
717
- # A Completion that returns all possible flags associated with a
718
- # {Toys::Flag}.
719
- #
720
- class DefaultCompletion < Completion::Base
721
- ##
722
- # Create a completion given configuration options.
723
- #
724
- # @param flag [Toys::Flag] The flag definition.
725
- # @param include_short [Boolean] Whether to include short flags.
726
- # @param include_long [Boolean] Whether to include long flags.
727
- # @param include_negative [Boolean] Whether to include `--no-*` forms.
728
- #
729
- def initialize(flag:, include_short: true, include_long: true, include_negative: true)
730
- super()
731
- @flag = flag
732
- @include_short = include_short
733
- @include_long = include_long
734
- @include_negative = include_negative
762
+ def canonicalize
763
+ @flag_type = nil
764
+ @value_type = nil
765
+ @value_label = nil
766
+ @value_delim = " "
767
+ short_flag_syntax.reverse_each do |flag|
768
+ analyze_flag_syntax(flag)
735
769
  end
736
-
737
- ##
738
- # Whether to include short flags
739
- # @return [Boolean]
740
- #
741
- def include_short?
742
- @include_short
770
+ long_flag_syntax.reverse_each do |flag|
771
+ analyze_flag_syntax(flag)
743
772
  end
744
-
745
- ##
746
- # Whether to include long flags
747
- # @return [Boolean]
748
- #
749
- def include_long?
750
- @include_long
773
+ @flag_type ||= :boolean
774
+ @value_type ||= :required if @flag_type == :value
775
+ flag_syntax.each do |flag|
776
+ flag.configure_canonical(@flag_type, @value_type, @value_label, @value_delim)
751
777
  end
778
+ end
752
779
 
753
- ##
754
- # Whether to include negative long flags
755
- # @return [Boolean]
756
- #
757
- def include_negative?
758
- @include_negative
780
+ def analyze_flag_syntax(flag)
781
+ return if flag.flag_type.nil?
782
+ if !@flag_type.nil? && @flag_type != flag.flag_type
783
+ raise ToolDefinitionError, "Cannot have both value and boolean flags for #{key.inspect}"
759
784
  end
760
-
761
- ##
762
- # Returns candidates for the current completion.
763
- #
764
- # @param context [Toys::Completion::Context] the current completion
765
- # context including the string fragment.
766
- # @return [Array<Toys::Completion::Candidate>] an array of candidates
767
- #
768
- def call(context)
769
- results =
770
- if @include_short && @include_long && @include_negative
771
- @flag.effective_flags
772
- else
773
- collect_results
774
- end
775
- fragment = context.fragment
776
- results.find_all { |val| val.start_with?(fragment) }
777
- .map { |str| Completion::Candidate.new(str) }
785
+ @flag_type = flag.flag_type
786
+ return unless @flag_type == :value
787
+ if !@value_type.nil? && @value_type != flag.value_type
788
+ raise ToolDefinitionError,
789
+ "Cannot have both required and optional values for flag #{key.inspect}"
778
790
  end
791
+ @value_type = flag.value_type
792
+ @value_label = flag.value_label
793
+ @value_delim = flag.value_delim
794
+ end
779
795
 
780
- private
781
-
782
- def collect_results
783
- results = []
784
- if @include_short
785
- results += @flag.short_flag_syntax.map(&:positive_flag)
786
- end
787
- if @include_long
788
- results +=
789
- if @include_negative
790
- @flag.long_flag_syntax.flat_map(&:flags)
791
- else
792
- @flag.long_flag_syntax.map(&:positive_flag)
793
- end
794
- end
795
- results
796
- end
796
+ def summarize(name)
797
+ @display_name =
798
+ name ||
799
+ long_flag_syntax.first&.canonical_str ||
800
+ short_flag_syntax.first&.canonical_str ||
801
+ key.to_s
802
+ @sort_str =
803
+ long_flag_syntax.first&.sort_str ||
804
+ short_flag_syntax.first&.sort_str ||
805
+ ""
797
806
  end
798
807
  end
799
808
  end