toys-core 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -0
  3. data/LICENSE.md +16 -24
  4. data/README.md +307 -59
  5. data/docs/guide.md +44 -4
  6. data/lib/toys-core.rb +58 -49
  7. data/lib/toys/acceptor.rb +672 -0
  8. data/lib/toys/alias.rb +106 -0
  9. data/lib/toys/arg_parser.rb +624 -0
  10. data/lib/toys/cli.rb +422 -181
  11. data/lib/toys/compat.rb +83 -0
  12. data/lib/toys/completion.rb +442 -0
  13. data/lib/toys/context.rb +354 -0
  14. data/lib/toys/core_version.rb +18 -26
  15. data/lib/toys/dsl/flag.rb +213 -56
  16. data/lib/toys/dsl/flag_group.rb +237 -51
  17. data/lib/toys/dsl/positional_arg.rb +210 -0
  18. data/lib/toys/dsl/tool.rb +968 -317
  19. data/lib/toys/errors.rb +46 -28
  20. data/lib/toys/flag.rb +821 -0
  21. data/lib/toys/flag_group.rb +282 -0
  22. data/lib/toys/input_file.rb +18 -26
  23. data/lib/toys/loader.rb +110 -100
  24. data/lib/toys/middleware.rb +24 -31
  25. data/lib/toys/mixin.rb +90 -59
  26. data/lib/toys/module_lookup.rb +125 -0
  27. data/lib/toys/positional_arg.rb +184 -0
  28. data/lib/toys/source_info.rb +192 -0
  29. data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
  30. data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
  31. data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
  32. data/lib/toys/standard_middleware/show_help.rb +130 -113
  33. data/lib/toys/standard_middleware/show_root_version.rb +29 -35
  34. data/lib/toys/standard_mixins/exec.rb +116 -78
  35. data/lib/toys/standard_mixins/fileutils.rb +16 -24
  36. data/lib/toys/standard_mixins/gems.rb +29 -30
  37. data/lib/toys/standard_mixins/highline.rb +34 -41
  38. data/lib/toys/standard_mixins/terminal.rb +72 -26
  39. data/lib/toys/template.rb +51 -35
  40. data/lib/toys/tool.rb +1161 -206
  41. data/lib/toys/utils/completion_engine.rb +171 -0
  42. data/lib/toys/utils/exec.rb +279 -182
  43. data/lib/toys/utils/gems.rb +58 -49
  44. data/lib/toys/utils/help_text.rb +117 -111
  45. data/lib/toys/utils/terminal.rb +69 -62
  46. data/lib/toys/wrappable_string.rb +162 -0
  47. metadata +24 -22
  48. data/lib/toys/definition/acceptor.rb +0 -191
  49. data/lib/toys/definition/alias.rb +0 -112
  50. data/lib/toys/definition/arg.rb +0 -140
  51. data/lib/toys/definition/flag.rb +0 -370
  52. data/lib/toys/definition/flag_group.rb +0 -205
  53. data/lib/toys/definition/source_info.rb +0 -190
  54. data/lib/toys/definition/tool.rb +0 -842
  55. data/lib/toys/dsl/arg.rb +0 -132
  56. data/lib/toys/runner.rb +0 -188
  57. data/lib/toys/standard_middleware.rb +0 -47
  58. data/lib/toys/utils/module_lookup.rb +0 -135
  59. data/lib/toys/utils/wrappable_string.rb +0 -165
@@ -1,38 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2018 Daniel Azuma
3
+ # Copyright 2019 Daniel Azuma
4
4
  #
5
- # All rights reserved.
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
6
11
  #
7
- # Redistribution and use in source and binary forms, with or without
8
- # modification, are permitted provided that the following conditions are met:
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
9
14
  #
10
- # * Redistributions of source code must retain the above copyright notice,
11
- # this list of conditions and the following disclaimer.
12
- # * Redistributions in binary form must reproduce the above copyright notice,
13
- # this list of conditions and the following disclaimer in the documentation
14
- # and/or other materials provided with the distribution.
15
- # * Neither the name of the copyright holder, nor the names of any other
16
- # contributors to this software, may be used to endorse or promote products
17
- # derived from this software without specific prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
- # POSSIBILITY OF SUCH DAMAGE.
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
30
22
  ;
31
23
 
32
24
  module Toys
33
25
  module Utils
34
26
  ##
35
- # A helper module that activates and installs gems
27
+ # A helper module that activates and installs gems.
28
+ #
29
+ # This class is not loaded by default. Before using it directly, you should
30
+ # `require "toys/utils/gems"`
36
31
  #
37
32
  class Gems
38
33
  ##
@@ -51,6 +46,12 @@ module Toys
51
46
  # Need to add a gem to the bundle.
52
47
  #
53
48
  class GemfileUpdateNeededError < ActivationFailedError
49
+ ##
50
+ # Create a GemfileUpdateNeededError.
51
+ #
52
+ # @param requirements_text [String] Gems and versions missing.
53
+ # @param gemfile_path [String] Path to the offending Gemfile.
54
+ #
54
55
  def initialize(requirements_text, gemfile_path)
55
56
  super("Required gem not available in the bundle: #{requirements_text}.\n" \
56
57
  "Please update your Gemfile #{gemfile_path.inspect}.")
@@ -61,8 +62,9 @@ module Toys
61
62
  # Activate the given gem. If it is not present, attempt to install it (or
62
63
  # inform the user to update the bundle).
63
64
  #
64
- # @param [String] name Name of the gem
65
- # @param [String...] requirements Version requirements
65
+ # @param name [String] Name of the gem
66
+ # @param requirements [String...] Version requirements
67
+ # @return [void]
66
68
  #
67
69
  def self.activate(name, *requirements)
68
70
  new.activate(name, *requirements)
@@ -71,20 +73,22 @@ module Toys
71
73
  ##
72
74
  # Create a new gem activator.
73
75
  #
74
- # @param [IO] input Input IO
75
- # @param [IO] output Output IO
76
- # @param [Boolean] suppress_confirm Suppress the confirmation prompt and
76
+ # @param input [IO] Input IO
77
+ # @param output [IO] Output IO
78
+ # @param suppress_confirm [Boolean] Suppress the confirmation prompt and
77
79
  # just use the given `default_confirm` value. Default is false,
78
80
  # indicating the confirmation prompt appears by default.
79
- # @param [Boolean] default_confirm Default response for the confirmation
81
+ # @param default_confirm [Boolean] Default response for the confirmation
80
82
  # prompt. Default is true.
81
83
  #
82
84
  def initialize(input: $stdin,
83
85
  output: $stderr,
84
86
  suppress_confirm: false,
85
87
  default_confirm: true)
86
- @terminal = Terminal.new(input: input, output: output)
87
- @exec = Exec.new
88
+ require "toys/utils/terminal"
89
+ require "toys/utils/exec"
90
+ @terminal = Utils::Terminal.new(input: input, output: output)
91
+ @exec = Utils::Exec.new
88
92
  @suppress_confirm = suppress_confirm ? true : false
89
93
  @default_confirm = default_confirm ? true : false
90
94
  end
@@ -93,32 +97,37 @@ module Toys
93
97
  # Activate the given gem. If it is not present, attempt to install it (or
94
98
  # inform the user to update the bundle).
95
99
  #
96
- # @param [String] name Name of the gem
97
- # @param [String...] requirements Version requirements
100
+ # @param name [String] Name of the gem
101
+ # @param requirements [String...] Version requirements
102
+ # @return [void]
98
103
  #
99
104
  def activate(name, *requirements)
100
105
  gem(name, *requirements)
101
- rescue ::Gem::LoadError => e1
106
+ rescue ::Gem::LoadError => e
107
+ handle_activation_error(e, name, requirements)
108
+ end
109
+
110
+ private
111
+
112
+ def handle_activation_error(error, name, requirements)
102
113
  is_missing_spec =
103
114
  if defined?(::Gem::MissingSpecError)
104
- e1.is_a?(::Gem::MissingSpecError)
115
+ error.is_a?(::Gem::MissingSpecError)
105
116
  else
106
- e1.message.include?("Could not find")
107
- end
108
- if is_missing_spec
109
- install_gem(name, requirements)
110
- begin
111
- gem(name, *requirements)
112
- rescue ::Gem::LoadError => e2
113
- report_error(name, requirements, e2)
117
+ error.message.include?("Could not find")
114
118
  end
115
- else
116
- report_error(name, requirements, e1)
119
+ unless is_missing_spec
120
+ report_error(name, requirements, error)
121
+ return
122
+ end
123
+ install_gem(name, requirements)
124
+ begin
125
+ gem(name, *requirements)
126
+ rescue ::Gem::LoadError => e
127
+ report_error(name, requirements, e)
117
128
  end
118
129
  end
119
130
 
120
- private
121
-
122
131
  def gem_requirements_text(name, requirements)
123
132
  "#{name.inspect}, #{requirements.map(&:inspect).join(', ')}"
124
133
  end
@@ -1,32 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2018 Daniel Azuma
3
+ # Copyright 2019 Daniel Azuma
4
4
  #
5
- # All rights reserved.
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
6
11
  #
7
- # Redistribution and use in source and binary forms, with or without
8
- # modification, are permitted provided that the following conditions are met:
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
9
14
  #
10
- # * Redistributions of source code must retain the above copyright notice,
11
- # this list of conditions and the following disclaimer.
12
- # * Redistributions in binary form must reproduce the above copyright notice,
13
- # this list of conditions and the following disclaimer in the documentation
14
- # and/or other materials provided with the distribution.
15
- # * Neither the name of the copyright holder, nor the names of any other
16
- # contributors to this software, may be used to endorse or promote products
17
- # derived from this software without specific prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
- # POSSIBILITY OF SUCH DAMAGE.
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
30
22
  ;
31
23
 
32
24
  module Toys
@@ -38,6 +30,9 @@ module Toys
38
30
  # flags, and arguments. It is used by middleware that implements help
39
31
  # and related options.
40
32
  #
33
+ # This class is not loaded by default. Before using it directly, you should
34
+ # `require "toys/utils/help_text"`
35
+ #
41
36
  class HelpText
42
37
  ##
43
38
  # Default width of first column
@@ -52,44 +47,49 @@ module Toys
52
47
  DEFAULT_INDENT = 4
53
48
 
54
49
  ##
55
- # Create a usage helper given an executable tool.
50
+ # Create a usage helper given an execution context.
56
51
  #
57
- # @param [Toys::Tool] tool The current tool.
52
+ # @param context [Toys::Context] The current context.
58
53
  # @return [Toys::Utils::HelpText]
59
54
  #
60
- def self.from_tool(tool)
61
- new(tool[Tool::Keys::TOOL_DEFINITION], tool[Tool::Keys::LOADER],
62
- tool[Tool::Keys::BINARY_NAME])
55
+ def self.from_context(context)
56
+ cli = context[Context::Key::CLI]
57
+ new(context[Context::Key::TOOL], cli.loader, cli.executable_name)
63
58
  end
64
59
 
65
60
  ##
66
61
  # Create a usage helper.
67
62
  #
68
- # @param [Toys::Tool] tool The tool for which to generate documentation.
69
- # @param [Toys::Loader] loader A loader that can provide subcommands.
70
- # @param [String] binary_name The name of the binary. e.g. `"toys"`.
63
+ # @param tool [Toys::Tool] The tool to document.
64
+ # @param loader [Toys::Loader] A loader that can provide subcommands.
65
+ # @param executable_name [String] The name of the executable.
66
+ # e.g. `"toys"`.
71
67
  #
72
68
  # @return [Toys::Utils::HelpText]
73
69
  #
74
- def initialize(tool, loader, binary_name)
70
+ def initialize(tool, loader, executable_name)
75
71
  @tool = tool
76
72
  @loader = loader
77
- @binary_name = binary_name
73
+ @executable_name = executable_name
78
74
  end
79
75
 
76
+ ##
77
+ # The Tool being documented.
78
+ # @return [Toys::Tool]
79
+ #
80
80
  attr_reader :tool
81
81
 
82
82
  ##
83
83
  # Generate a short usage string.
84
84
  #
85
- # @param [Boolean] recursive If true, and the tool is a namespace,
85
+ # @param recursive [Boolean] If true, and the tool is a namespace,
86
86
  # display all subtools recursively. Defaults to false.
87
- # @param [Boolean] include_hidden Include hidden subtools (i.e. whose
87
+ # @param include_hidden [Boolean] Include hidden subtools (i.e. whose
88
88
  # names begin with underscore.) Default is false.
89
- # @param [Integer] left_column_width Width of the first column. Default
89
+ # @param left_column_width [Integer] Width of the first column. Default
90
90
  # is {DEFAULT_LEFT_COLUMN_WIDTH}.
91
- # @param [Integer] indent Indent width. Default is {DEFAULT_INDENT}.
92
- # @param [Integer,nil] wrap_width Overall width to wrap to. Default is
91
+ # @param indent [Integer] Indent width. Default is {DEFAULT_INDENT}.
92
+ # @param wrap_width [Integer,nil] Overall width to wrap to. Default is
93
93
  # `nil` indicating no wrapping.
94
94
  #
95
95
  # @return [String] A usage string.
@@ -99,7 +99,7 @@ module Toys
99
99
  left_column_width ||= DEFAULT_LEFT_COLUMN_WIDTH
100
100
  indent ||= DEFAULT_INDENT
101
101
  subtools = find_subtools(recursive, nil, include_hidden)
102
- assembler = UsageStringAssembler.new(@tool, @binary_name, subtools,
102
+ assembler = UsageStringAssembler.new(@tool, @executable_name, subtools,
103
103
  indent, left_column_width, wrap_width)
104
104
  assembler.result
105
105
  end
@@ -107,20 +107,20 @@ module Toys
107
107
  ##
108
108
  # Generate a long help string.
109
109
  #
110
- # @param [Boolean] recursive If true, and the tool is a namespace,
110
+ # @param recursive [Boolean] If true, and the tool is a namespace,
111
111
  # display all subtools recursively. Defaults to false.
112
- # @param [String,nil] search An optional string to search for when
112
+ # @param search [String,nil] An optional string to search for when
113
113
  # listing subtools. Defaults to `nil` which finds all subtools.
114
- # @param [Boolean] include_hidden Include hidden subtools (i.e. whose
114
+ # @param include_hidden [Boolean] Include hidden subtools (i.e. whose
115
115
  # names begin with underscore.) Default is false.
116
- # @param [Boolean] show_source_path If true, shows the source path
116
+ # @param show_source_path [Boolean] If true, shows the source path
117
117
  # section. Defaults to false.
118
- # @param [Integer] indent Indent width. Default is {DEFAULT_INDENT}.
119
- # @param [Integer] indent2 Second indent width. Default is
118
+ # @param indent [Integer] Indent width. Default is {DEFAULT_INDENT}.
119
+ # @param indent2 [Integer] Second indent width. Default is
120
120
  # {DEFAULT_INDENT}.
121
- # @param [Integer,nil] wrap_width Wrap width of the column, or `nil` to
121
+ # @param wrap_width [Integer,nil] Wrap width of the column, or `nil` to
122
122
  # disable wrap. Default is `nil`.
123
- # @param [Boolean] styled Output ansi styles. Default is `true`.
123
+ # @param styled [Boolean] Output ansi styles. Default is `true`.
124
124
  #
125
125
  # @return [String] A usage string.
126
126
  #
@@ -130,24 +130,24 @@ module Toys
130
130
  indent ||= DEFAULT_INDENT
131
131
  indent2 ||= DEFAULT_INDENT
132
132
  subtools = find_subtools(recursive, search, include_hidden)
133
- assembler = HelpStringAssembler.new(@tool, @binary_name, subtools, search, show_source_path,
134
- indent, indent2, wrap_width, styled)
133
+ assembler = HelpStringAssembler.new(@tool, @executable_name, subtools, search,
134
+ show_source_path, indent, indent2, wrap_width, styled)
135
135
  assembler.result
136
136
  end
137
137
 
138
138
  ##
139
139
  # Generate a subtool list string.
140
140
  #
141
- # @param [Boolean] recursive If true, and the tool is a namespace,
141
+ # @param recursive [Boolean] If true, and the tool is a namespace,
142
142
  # display all subtools recursively. Defaults to false.
143
- # @param [String,nil] search An optional string to search for when
143
+ # @param search [String,nil] An optional string to search for when
144
144
  # listing subtools. Defaults to `nil` which finds all subtools.
145
- # @param [Boolean] include_hidden Include hidden subtools (i.e. whose
145
+ # @param include_hidden [Boolean] Include hidden subtools (i.e. whose
146
146
  # names begin with underscore.) Default is false.
147
- # @param [Integer] indent Indent width. Default is {DEFAULT_INDENT}.
148
- # @param [Integer,nil] wrap_width Wrap width of the column, or `nil` to
147
+ # @param indent [Integer] Indent width. Default is {DEFAULT_INDENT}.
148
+ # @param wrap_width [Integer,nil] Wrap width of the column, or `nil` to
149
149
  # disable wrap. Default is `nil`.
150
- # @param [Boolean] styled Output ansi styles. Default is `true`.
150
+ # @param styled [Boolean] Output ansi styles. Default is `true`.
151
151
  #
152
152
  # @return [String] A usage string.
153
153
  #
@@ -174,10 +174,10 @@ module Toys
174
174
 
175
175
  ## @private
176
176
  class UsageStringAssembler
177
- def initialize(tool, binary_name, subtools,
177
+ def initialize(tool, executable_name, subtools,
178
178
  indent, left_column_width, wrap_width)
179
179
  @tool = tool
180
- @binary_name = binary_name
180
+ @executable_name = executable_name
181
181
  @subtools = subtools
182
182
  @indent = indent
183
183
  @left_column_width = left_column_width
@@ -212,41 +212,43 @@ module Toys
212
212
  end
213
213
 
214
214
  def tool_synopsis
215
- synopsis = [@binary_name] + @tool.full_name
216
- synopsis << "[FLAGS...]" unless @tool.flag_definitions.empty?
217
- @tool.arg_definitions.each do |arg_info|
215
+ synopsis = [@executable_name] + @tool.full_name
216
+ synopsis << "[FLAGS...]" unless @tool.flags.empty?
217
+ @tool.positional_args.each do |arg_info|
218
218
  synopsis << arg_name(arg_info)
219
219
  end
220
220
  synopsis.join(" ")
221
221
  end
222
222
 
223
223
  def namespace_synopsis
224
- ([@binary_name] + @tool.full_name + ["TOOL", "[ARGUMENTS...]"]).join(" ")
224
+ ([@executable_name] + @tool.full_name + ["TOOL", "[ARGUMENTS...]"]).join(" ")
225
225
  end
226
226
 
227
227
  def add_flag_group_sections
228
228
  @tool.flag_groups.each do |group|
229
229
  next if group.empty?
230
230
  @lines << ""
231
- @lines << group.desc.to_s + ":"
232
- group.flag_definitions.each do |flag|
231
+ desc_str = group.desc.to_s
232
+ desc_str = "Flags" if desc_str.empty?
233
+ @lines << desc_str + ":"
234
+ group.flags.each do |flag|
233
235
  add_flag(flag)
234
236
  end
235
237
  end
236
238
  end
237
239
 
238
240
  def add_flag(flag)
239
- flags = flag.single_flag_syntax + flag.double_flag_syntax
241
+ flags = flag.short_flag_syntax + flag.long_flag_syntax
240
242
  last_index = flags.size - 1
241
243
  flags_str = flags.each_with_index.map do |fs, i|
242
244
  i == last_index ? fs.canonical_str : fs.str_without_value
243
245
  end.join(", ")
244
- flags_str = " #{flags_str}" if flag.single_flag_syntax.empty?
246
+ flags_str = " #{flags_str}" if flag.short_flag_syntax.empty?
245
247
  add_right_column_desc(flags_str, wrap_desc(flag.desc))
246
248
  end
247
249
 
248
250
  def add_positional_arguments_section
249
- args_to_display = @tool.arg_definitions
251
+ args_to_display = @tool.positional_args
250
252
  return if args_to_display.empty?
251
253
  @lines << ""
252
254
  @lines << "Positional arguments:"
@@ -263,7 +265,7 @@ module Toys
263
265
  @subtools.each do |subtool|
264
266
  tool_name = subtool.full_name.slice(name_len..-1).join(" ")
265
267
  desc =
266
- if subtool.is_a?(Definition::Alias)
268
+ if subtool.is_a?(Alias)
267
269
  ["(Alias of #{subtool.display_target})"]
268
270
  else
269
271
  wrap_desc(subtool.desc)
@@ -298,7 +300,7 @@ module Toys
298
300
  end
299
301
 
300
302
  def wrap_desc(desc)
301
- Utils::WrappableString.wrap_lines(desc, @right_column_wrap_width)
303
+ WrappableString.wrap_lines(desc, @right_column_wrap_width)
302
304
  end
303
305
 
304
306
  def indent_str(str)
@@ -308,10 +310,11 @@ module Toys
308
310
 
309
311
  ## @private
310
312
  class HelpStringAssembler
311
- def initialize(tool, binary_name, subtools, search_term, show_source_path,
313
+ def initialize(tool, executable_name, subtools, search_term, show_source_path,
312
314
  indent, indent2, wrap_width, styled)
315
+ require "toys/utils/terminal"
313
316
  @tool = tool
314
- @binary_name = binary_name
317
+ @executable_name = executable_name
315
318
  @subtools = subtools
316
319
  @search_term = search_term
317
320
  @show_source_path = show_source_path
@@ -339,17 +342,17 @@ module Toys
339
342
 
340
343
  def add_name_section
341
344
  @lines << bold("NAME")
342
- name_str = ([@binary_name] + @tool.full_name).join(" ")
345
+ name_str = ([@executable_name] + @tool.full_name).join(" ")
343
346
  add_prefix_with_desc(name_str, @tool.desc)
344
347
  end
345
348
 
346
349
  def add_prefix_with_desc(prefix, desc)
347
350
  if desc.empty?
348
351
  @lines << indent_str(prefix)
349
- elsif !desc.is_a?(Utils::WrappableString)
352
+ elsif !desc.is_a?(WrappableString)
350
353
  @lines << indent_str("#{prefix} - #{desc}")
351
354
  else
352
- desc = wrap_indent_indent2(Utils::WrappableString.new(["#{prefix} -"] + desc.fragments))
355
+ desc = wrap_indent_indent2(WrappableString.new(["#{prefix} -"] + desc.fragments))
353
356
  @lines << indent_str(desc[0])
354
357
  desc[1..-1].each do |line|
355
358
  @lines << indent2_str(line)
@@ -374,36 +377,36 @@ module Toys
374
377
  end
375
378
 
376
379
  def tool_synopsis
377
- synopsis = [full_binary_name]
380
+ synopsis = [full_executable_name]
378
381
  @tool.flag_groups.each do |flag_group|
379
382
  case flag_group
380
- when Definition::FlagGroup::Required
383
+ when FlagGroup::Required
381
384
  add_required_group_to_synopsis(flag_group, synopsis)
382
- when Definition::FlagGroup::ExactlyOne
385
+ when FlagGroup::ExactlyOne
383
386
  add_exactly_one_group_to_synopsis(flag_group, synopsis)
384
- when Definition::FlagGroup::AtMostOne
387
+ when FlagGroup::AtMostOne
385
388
  add_at_most_one_group_to_synopsis(flag_group, synopsis)
386
- when Definition::FlagGroup::AtLeastOne
389
+ when FlagGroup::AtLeastOne
387
390
  add_at_least_one_group_to_synopsis(flag_group, synopsis)
388
391
  else
389
392
  add_ordinary_group_to_synopsis(flag_group, synopsis)
390
393
  end
391
394
  end
392
- @tool.arg_definitions.each do |arg_info|
395
+ @tool.positional_args.each do |arg_info|
393
396
  synopsis << arg_name(arg_info)
394
397
  end
395
- wrap_indent_indent2(Utils::WrappableString.new(synopsis))
398
+ wrap_indent_indent2(WrappableString.new(synopsis))
396
399
  end
397
400
 
398
401
  def add_ordinary_group_to_synopsis(flag_group, synopsis)
399
- flag_group.flag_definitions.each do |flag_def|
400
- synopsis << "[#{flag_spec_string(flag_def, true)}]"
402
+ flag_group.flags.each do |flag|
403
+ synopsis << "[#{flag_spec_string(flag, true)}]"
401
404
  end
402
405
  end
403
406
 
404
407
  def add_required_group_to_synopsis(flag_group, synopsis)
405
- flag_group.flag_definitions.each do |flag_def|
406
- synopsis << "(#{flag_spec_string(flag_def, true)})"
408
+ flag_group.flags.each do |flag|
409
+ synopsis << "(#{flag_spec_string(flag, true)})"
407
410
  end
408
411
  end
409
412
 
@@ -411,13 +414,13 @@ module Toys
411
414
  return if flag_group.empty?
412
415
  synopsis << "("
413
416
  first = true
414
- flag_group.flag_definitions.each do |flag_def|
417
+ flag_group.flags.each do |flag|
415
418
  if first
416
419
  first = false
417
420
  else
418
421
  synopsis << "|"
419
422
  end
420
- synopsis << flag_spec_string(flag_def, true)
423
+ synopsis << flag_spec_string(flag, true)
421
424
  end
422
425
  synopsis << ")"
423
426
  end
@@ -426,13 +429,13 @@ module Toys
426
429
  return if flag_group.empty?
427
430
  synopsis << "["
428
431
  first = true
429
- flag_group.flag_definitions.each do |flag_def|
432
+ flag_group.flags.each do |flag|
430
433
  if first
431
434
  first = false
432
435
  else
433
436
  synopsis << "|"
434
437
  end
435
- synopsis << flag_spec_string(flag_def, true)
438
+ synopsis << flag_spec_string(flag, true)
436
439
  end
437
440
  synopsis << "]"
438
441
  end
@@ -440,19 +443,19 @@ module Toys
440
443
  def add_at_least_one_group_to_synopsis(flag_group, synopsis)
441
444
  return if flag_group.empty?
442
445
  synopsis << "("
443
- flag_group.flag_definitions.each do |flag_def|
444
- synopsis << "[#{flag_spec_string(flag_def, true)}]"
446
+ flag_group.flags.each do |flag|
447
+ synopsis << "[#{flag_spec_string(flag, true)}]"
445
448
  end
446
449
  synopsis << ")"
447
450
  end
448
451
 
449
452
  def namespace_synopsis
450
- synopsis = [full_binary_name, underline("TOOL"), "[#{underline('ARGUMENTS')}...]"]
451
- wrap_indent_indent2(Utils::WrappableString.new(synopsis))
453
+ synopsis = [full_executable_name, underline("TOOL"), "[#{underline('ARGUMENTS')}...]"]
454
+ wrap_indent_indent2(WrappableString.new(synopsis))
452
455
  end
453
456
 
454
- def full_binary_name
455
- bold(([@binary_name] + @tool.full_name).join(" "))
457
+ def full_executable_name
458
+ bold(([@executable_name] + @tool.full_name).join(" "))
456
459
  end
457
460
 
458
461
  def add_source_section
@@ -476,7 +479,9 @@ module Toys
476
479
  @tool.flag_groups.each do |group|
477
480
  next if group.empty?
478
481
  @lines << ""
479
- @lines << bold(group.desc.to_s.upcase)
482
+ desc_str = group.desc.to_s.upcase
483
+ desc_str = "FLAGS" if desc_str.empty?
484
+ @lines << bold(desc_str)
480
485
  precede_with_blank = false
481
486
  unless group.long_desc.empty?
482
487
  wrap_indent(group.long_desc).each do |line|
@@ -484,7 +489,7 @@ module Toys
484
489
  end
485
490
  precede_with_blank = true
486
491
  end
487
- group.flag_definitions.each do |flag|
492
+ group.flags.each do |flag|
488
493
  add_indented_section(flag_spec_string(flag), flag, precede_with_blank)
489
494
  precede_with_blank = true
490
495
  end
@@ -505,7 +510,7 @@ module Toys
505
510
  end
506
511
 
507
512
  def add_positional_arguments_section
508
- args_to_display = @tool.arg_definitions
513
+ args_to_display = @tool.positional_args
509
514
  return if args_to_display.empty?
510
515
  @lines << ""
511
516
  @lines << bold("POSITIONAL ARGUMENTS")
@@ -528,7 +533,7 @@ module Toys
528
533
  @subtools.each do |subtool|
529
534
  tool_name = subtool.full_name.slice(name_len..-1).join(" ")
530
535
  desc =
531
- if subtool.is_a?(Definition::Alias)
536
+ if subtool.is_a?(Alias)
532
537
  "(Alias of #{subtool.display_target})"
533
538
  else
534
539
  subtool.desc
@@ -562,19 +567,19 @@ module Toys
562
567
  end
563
568
 
564
569
  def wrap_indent(input)
565
- return Utils::WrappableString.wrap_lines(input, nil) unless @wrap_width
566
- Utils::WrappableString.wrap_lines(input, @wrap_width - @indent)
570
+ return WrappableString.wrap_lines(input, nil) unless @wrap_width
571
+ WrappableString.wrap_lines(input, @wrap_width - @indent)
567
572
  end
568
573
 
569
574
  def wrap_indent2(input)
570
- return Utils::WrappableString.wrap_lines(input, nil) unless @wrap_width
571
- Utils::WrappableString.wrap_lines(input, @wrap_width - @indent - @indent2)
575
+ return WrappableString.wrap_lines(input, nil) unless @wrap_width
576
+ WrappableString.wrap_lines(input, @wrap_width - @indent - @indent2)
572
577
  end
573
578
 
574
579
  def wrap_indent_indent2(input)
575
- return Utils::WrappableString.wrap_lines(input, nil) unless @wrap_width
576
- Utils::WrappableString.wrap_lines(input, @wrap_width - @indent,
577
- @wrap_width - @indent - @indent2)
580
+ return WrappableString.wrap_lines(input, nil) unless @wrap_width
581
+ WrappableString.wrap_lines(input, @wrap_width - @indent,
582
+ @wrap_width - @indent - @indent2)
578
583
  end
579
584
 
580
585
  def bold(str)
@@ -597,6 +602,7 @@ module Toys
597
602
  ## @private
598
603
  class ListStringAssembler
599
604
  def initialize(tool, subtools, recursive, search_term, indent, wrap_width, styled)
605
+ require "toys/utils/terminal"
600
606
  @tool = tool
601
607
  @subtools = subtools
602
608
  @recursive = recursive
@@ -637,7 +643,7 @@ module Toys
637
643
  @subtools.each do |subtool|
638
644
  tool_name = subtool.full_name.slice(name_len..-1).join(" ")
639
645
  desc =
640
- if subtool.is_a?(Definition::Alias)
646
+ if subtool.is_a?(Alias)
641
647
  "(Alias of #{subtool.display_target})"
642
648
  else
643
649
  subtool.desc
@@ -649,10 +655,10 @@ module Toys
649
655
  def add_prefix_with_desc(prefix, desc)
650
656
  if desc.empty?
651
657
  @lines << prefix
652
- elsif !desc.is_a?(Utils::WrappableString)
658
+ elsif !desc.is_a?(WrappableString)
653
659
  @lines << "#{prefix} - #{desc}"
654
660
  else
655
- desc = wrap_indent(Utils::WrappableString.new(["#{prefix} -"] + desc.fragments))
661
+ desc = wrap_indent(WrappableString.new(["#{prefix} -"] + desc.fragments))
656
662
  @lines << desc[0]
657
663
  desc[1..-1].each do |line|
658
664
  @lines << indent_str(line)
@@ -661,8 +667,8 @@ module Toys
661
667
  end
662
668
 
663
669
  def wrap_indent(input)
664
- return Utils::WrappableString.wrap_lines(input, nil) unless @wrap_width
665
- Utils::WrappableString.wrap_lines(input, @wrap_width, @wrap_width - @indent)
670
+ return WrappableString.wrap_lines(input, nil) unless @wrap_width
671
+ WrappableString.wrap_lines(input, @wrap_width, @wrap_width - @indent)
666
672
  end
667
673
 
668
674
  def bold(str)