toys-core 0.12.2 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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