toys-core 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)