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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +98 -0
- data/LICENSE.md +16 -24
- data/README.md +307 -59
- data/docs/guide.md +44 -4
- data/lib/toys-core.rb +58 -49
- data/lib/toys/acceptor.rb +672 -0
- data/lib/toys/alias.rb +106 -0
- data/lib/toys/arg_parser.rb +624 -0
- data/lib/toys/cli.rb +422 -181
- data/lib/toys/compat.rb +83 -0
- data/lib/toys/completion.rb +442 -0
- data/lib/toys/context.rb +354 -0
- data/lib/toys/core_version.rb +18 -26
- data/lib/toys/dsl/flag.rb +213 -56
- data/lib/toys/dsl/flag_group.rb +237 -51
- data/lib/toys/dsl/positional_arg.rb +210 -0
- data/lib/toys/dsl/tool.rb +968 -317
- data/lib/toys/errors.rb +46 -28
- data/lib/toys/flag.rb +821 -0
- data/lib/toys/flag_group.rb +282 -0
- data/lib/toys/input_file.rb +18 -26
- data/lib/toys/loader.rb +110 -100
- data/lib/toys/middleware.rb +24 -31
- data/lib/toys/mixin.rb +90 -59
- data/lib/toys/module_lookup.rb +125 -0
- data/lib/toys/positional_arg.rb +184 -0
- data/lib/toys/source_info.rb +192 -0
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
- data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
- data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
- data/lib/toys/standard_middleware/show_help.rb +130 -113
- data/lib/toys/standard_middleware/show_root_version.rb +29 -35
- data/lib/toys/standard_mixins/exec.rb +116 -78
- data/lib/toys/standard_mixins/fileutils.rb +16 -24
- data/lib/toys/standard_mixins/gems.rb +29 -30
- data/lib/toys/standard_mixins/highline.rb +34 -41
- data/lib/toys/standard_mixins/terminal.rb +72 -26
- data/lib/toys/template.rb +51 -35
- data/lib/toys/tool.rb +1161 -206
- data/lib/toys/utils/completion_engine.rb +171 -0
- data/lib/toys/utils/exec.rb +279 -182
- data/lib/toys/utils/gems.rb +58 -49
- data/lib/toys/utils/help_text.rb +117 -111
- data/lib/toys/utils/terminal.rb +69 -62
- data/lib/toys/wrappable_string.rb +162 -0
- metadata +24 -22
- data/lib/toys/definition/acceptor.rb +0 -191
- data/lib/toys/definition/alias.rb +0 -112
- data/lib/toys/definition/arg.rb +0 -140
- data/lib/toys/definition/flag.rb +0 -370
- data/lib/toys/definition/flag_group.rb +0 -205
- data/lib/toys/definition/source_info.rb +0 -190
- data/lib/toys/definition/tool.rb +0 -842
- data/lib/toys/dsl/arg.rb +0 -132
- data/lib/toys/runner.rb +0 -188
- data/lib/toys/standard_middleware.rb +0 -47
- data/lib/toys/utils/module_lookup.rb +0 -135
- data/lib/toys/utils/wrappable_string.rb +0 -165
data/lib/toys/utils/gems.rb
CHANGED
@@ -1,38 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
4
|
#
|
5
|
-
#
|
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
|
-
#
|
8
|
-
#
|
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
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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]
|
65
|
-
# @param [String...]
|
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]
|
75
|
-
# @param [IO]
|
76
|
-
# @param [Boolean]
|
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]
|
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
|
-
|
87
|
-
|
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]
|
97
|
-
# @param [String...]
|
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 =>
|
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
|
-
|
115
|
+
error.is_a?(::Gem::MissingSpecError)
|
105
116
|
else
|
106
|
-
|
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
|
-
|
116
|
-
report_error(name, requirements,
|
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
|
data/lib/toys/utils/help_text.rb
CHANGED
@@ -1,32 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
4
|
#
|
5
|
-
#
|
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
|
-
#
|
8
|
-
#
|
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
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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
|
50
|
+
# Create a usage helper given an execution context.
|
56
51
|
#
|
57
|
-
# @param [Toys::
|
52
|
+
# @param context [Toys::Context] The current context.
|
58
53
|
# @return [Toys::Utils::HelpText]
|
59
54
|
#
|
60
|
-
def self.
|
61
|
-
|
62
|
-
|
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]
|
69
|
-
# @param [Toys::Loader]
|
70
|
-
# @param [String]
|
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,
|
70
|
+
def initialize(tool, loader, executable_name)
|
75
71
|
@tool = tool
|
76
72
|
@loader = loader
|
77
|
-
@
|
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]
|
85
|
+
# @param recursive [Boolean] If true, and the tool is a namespace,
|
86
86
|
# display all subtools recursively. Defaults to false.
|
87
|
-
# @param [Boolean]
|
87
|
+
# @param include_hidden [Boolean] Include hidden subtools (i.e. whose
|
88
88
|
# names begin with underscore.) Default is false.
|
89
|
-
# @param [Integer]
|
89
|
+
# @param left_column_width [Integer] Width of the first column. Default
|
90
90
|
# is {DEFAULT_LEFT_COLUMN_WIDTH}.
|
91
|
-
# @param [Integer]
|
92
|
-
# @param [Integer,nil]
|
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, @
|
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]
|
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]
|
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]
|
114
|
+
# @param include_hidden [Boolean] Include hidden subtools (i.e. whose
|
115
115
|
# names begin with underscore.) Default is false.
|
116
|
-
# @param [Boolean]
|
116
|
+
# @param show_source_path [Boolean] If true, shows the source path
|
117
117
|
# section. Defaults to false.
|
118
|
-
# @param [Integer]
|
119
|
-
# @param [Integer]
|
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]
|
121
|
+
# @param wrap_width [Integer,nil] Wrap width of the column, or `nil` to
|
122
122
|
# disable wrap. Default is `nil`.
|
123
|
-
# @param [Boolean]
|
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, @
|
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]
|
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]
|
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]
|
145
|
+
# @param include_hidden [Boolean] Include hidden subtools (i.e. whose
|
146
146
|
# names begin with underscore.) Default is false.
|
147
|
-
# @param [Integer]
|
148
|
-
# @param [Integer,nil]
|
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]
|
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,
|
177
|
+
def initialize(tool, executable_name, subtools,
|
178
178
|
indent, left_column_width, wrap_width)
|
179
179
|
@tool = tool
|
180
|
-
@
|
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 = [@
|
216
|
-
synopsis << "[FLAGS...]" unless @tool.
|
217
|
-
@tool.
|
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
|
-
([@
|
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
|
-
|
232
|
-
|
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.
|
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.
|
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.
|
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?(
|
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
|
-
|
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,
|
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
|
-
@
|
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 = ([@
|
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?(
|
352
|
+
elsif !desc.is_a?(WrappableString)
|
350
353
|
@lines << indent_str("#{prefix} - #{desc}")
|
351
354
|
else
|
352
|
-
desc = wrap_indent_indent2(
|
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 = [
|
380
|
+
synopsis = [full_executable_name]
|
378
381
|
@tool.flag_groups.each do |flag_group|
|
379
382
|
case flag_group
|
380
|
-
when
|
383
|
+
when FlagGroup::Required
|
381
384
|
add_required_group_to_synopsis(flag_group, synopsis)
|
382
|
-
when
|
385
|
+
when FlagGroup::ExactlyOne
|
383
386
|
add_exactly_one_group_to_synopsis(flag_group, synopsis)
|
384
|
-
when
|
387
|
+
when FlagGroup::AtMostOne
|
385
388
|
add_at_most_one_group_to_synopsis(flag_group, synopsis)
|
386
|
-
when
|
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.
|
395
|
+
@tool.positional_args.each do |arg_info|
|
393
396
|
synopsis << arg_name(arg_info)
|
394
397
|
end
|
395
|
-
wrap_indent_indent2(
|
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.
|
400
|
-
synopsis << "[#{flag_spec_string(
|
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.
|
406
|
-
synopsis << "(#{flag_spec_string(
|
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.
|
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(
|
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.
|
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(
|
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.
|
444
|
-
synopsis << "[#{flag_spec_string(
|
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 = [
|
451
|
-
wrap_indent_indent2(
|
453
|
+
synopsis = [full_executable_name, underline("TOOL"), "[#{underline('ARGUMENTS')}...]"]
|
454
|
+
wrap_indent_indent2(WrappableString.new(synopsis))
|
452
455
|
end
|
453
456
|
|
454
|
-
def
|
455
|
-
bold(([@
|
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
|
-
|
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.
|
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.
|
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?(
|
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
|
566
|
-
|
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
|
571
|
-
|
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
|
576
|
-
|
577
|
-
|
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?(
|
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?(
|
658
|
+
elsif !desc.is_a?(WrappableString)
|
653
659
|
@lines << "#{prefix} - #{desc}"
|
654
660
|
else
|
655
|
-
desc = wrap_indent(
|
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
|
665
|
-
|
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)
|