toys-core 0.8.1 → 0.9.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 +24 -0
- data/README.md +13 -10
- data/docs/guide.md +1 -1
- data/lib/toys-core.rb +1 -2
- data/lib/toys/acceptor.rb +35 -10
- data/lib/toys/arg_parser.rb +6 -6
- data/lib/toys/cli.rb +10 -6
- data/lib/toys/compat.rb +8 -0
- data/lib/toys/completion.rb +24 -6
- data/lib/toys/context.rb +8 -0
- data/lib/toys/{core_version.rb → core.rb} +11 -3
- data/lib/toys/dsl/flag.rb +7 -20
- data/lib/toys/dsl/positional_arg.rb +7 -20
- data/lib/toys/dsl/tool.rb +79 -20
- data/lib/toys/errors.rb +3 -3
- data/lib/toys/flag.rb +12 -10
- data/lib/toys/loader.rb +95 -106
- data/lib/toys/positional_arg.rb +1 -1
- data/lib/toys/standard_middleware/set_default_descriptions.rb +22 -7
- data/lib/toys/standard_middleware/show_help.rb +3 -3
- data/lib/toys/standard_mixins/exec.rb +33 -16
- data/lib/toys/standard_mixins/gems.rb +1 -1
- data/lib/toys/standard_mixins/terminal.rb +1 -1
- data/lib/toys/tool.rb +93 -24
- data/lib/toys/utils/help_text.rb +51 -38
- data/lib/toys/utils/terminal.rb +2 -2
- metadata +8 -9
- data/lib/toys/alias.rb +0 -106
data/lib/toys/positional_arg.rb
CHANGED
@@ -37,7 +37,7 @@ module Toys
|
|
37
37
|
@type = type
|
38
38
|
@acceptor = Acceptor.create(acceptor)
|
39
39
|
@default = default
|
40
|
-
@completion = Completion.create(completion)
|
40
|
+
@completion = Completion.create(completion, **{})
|
41
41
|
@desc = WrappableString.make(desc)
|
42
42
|
@long_desc = WrappableString.make_array(long_desc)
|
43
43
|
@display_name = display_name || key.to_s.tr("-", "_").gsub(/\W/, "").upcase
|
@@ -40,6 +40,12 @@ module Toys
|
|
40
40
|
#
|
41
41
|
DEFAULT_TOOL_DESC = "(No tool description available)"
|
42
42
|
|
43
|
+
##
|
44
|
+
# The default description for delegating tools.
|
45
|
+
# @return [String]
|
46
|
+
#
|
47
|
+
DEFAULT_DELEGATE_DESC = '(Delegates to "%<target>s")'
|
48
|
+
|
43
49
|
##
|
44
50
|
# The default description for namespaces.
|
45
51
|
# @return [String]
|
@@ -58,7 +64,7 @@ module Toys
|
|
58
64
|
#
|
59
65
|
DEFAULT_ROOT_LONG_DESC = [
|
60
66
|
"This command line tool was built using the toys-core gem. See" \
|
61
|
-
" https://
|
67
|
+
" https://dazuma.github.io/toys/gems/toys-core for more info.",
|
62
68
|
"To replace this message, set the description and long description" \
|
63
69
|
" of the root tool, or configure the SetDefaultDescriptions" \
|
64
70
|
" middleware.",
|
@@ -70,33 +76,39 @@ module Toys
|
|
70
76
|
# @param default_tool_desc [String,nil] The default short description for
|
71
77
|
# runnable tools, or `nil` not to set one. Defaults to
|
72
78
|
# {DEFAULT_TOOL_DESC}.
|
73
|
-
# @param default_tool_long_desc [String
|
74
|
-
# for runnable tools, or `nil` not to set one. Defaults
|
79
|
+
# @param default_tool_long_desc [Array<String>,nil] The default long
|
80
|
+
# description for runnable tools, or `nil` not to set one. Defaults
|
81
|
+
# to `nil`.
|
75
82
|
# @param default_namespace_desc [String,nil] The default short
|
76
83
|
# description for non-runnable tools, or `nil` not to set one.
|
77
84
|
# Defaults to {DEFAULT_TOOL_DESC}.
|
78
|
-
# @param default_namespace_long_desc [String
|
85
|
+
# @param default_namespace_long_desc [Array<String>,nil] The default long
|
79
86
|
# description for non-runnable tools, or `nil` not to set one.
|
80
87
|
# Defaults to `nil`.
|
81
88
|
# @param default_root_desc [String,nil] The default short description for
|
82
89
|
# the root tool, or `nil` not to set one. Defaults to
|
83
90
|
# {DEFAULT_ROOT_DESC}.
|
84
|
-
# @param default_root_long_desc [String
|
85
|
-
# for the root tool, or `nil` not to set one. Defaults to
|
91
|
+
# @param default_root_long_desc [Array<String>,nil] The default long
|
92
|
+
# description for the root tool, or `nil` not to set one. Defaults to
|
86
93
|
# {DEFAULT_ROOT_LONG_DESC}.
|
94
|
+
# @param default_delegate_desc [String,nil] The default short description
|
95
|
+
# for delegate tools, or `nil` not to set one. May include an sprintf
|
96
|
+
# field for the `target` name. Defaults to {DEFAULT_DELEGATE_DESC}.
|
87
97
|
#
|
88
98
|
def initialize(default_tool_desc: DEFAULT_TOOL_DESC,
|
89
99
|
default_tool_long_desc: nil,
|
90
100
|
default_namespace_desc: DEFAULT_NAMESPACE_DESC,
|
91
101
|
default_namespace_long_desc: nil,
|
92
102
|
default_root_desc: DEFAULT_ROOT_DESC,
|
93
|
-
default_root_long_desc: DEFAULT_ROOT_LONG_DESC
|
103
|
+
default_root_long_desc: DEFAULT_ROOT_LONG_DESC,
|
104
|
+
default_delegate_desc: DEFAULT_DELEGATE_DESC)
|
94
105
|
@default_tool_desc = default_tool_desc
|
95
106
|
@default_tool_long_desc = default_tool_long_desc
|
96
107
|
@default_namespace_desc = default_namespace_desc
|
97
108
|
@default_namespace_long_desc = default_namespace_long_desc
|
98
109
|
@default_root_desc = default_root_desc
|
99
110
|
@default_root_long_desc = default_root_long_desc
|
111
|
+
@default_delegate_desc = default_delegate_desc
|
100
112
|
end
|
101
113
|
|
102
114
|
##
|
@@ -137,6 +149,9 @@ module Toys
|
|
137
149
|
def generate_tool_desc(tool, data)
|
138
150
|
if tool.root?
|
139
151
|
@default_root_desc
|
152
|
+
elsif tool.delegate_target
|
153
|
+
params = {target: tool.delegate_target.join(" ")}
|
154
|
+
format(@default_delegate_desc, params)
|
140
155
|
elsif !tool.runnable? && data[:loader].has_subtools?(tool.full_name)
|
141
156
|
@default_namespace_desc
|
142
157
|
else
|
@@ -216,7 +216,7 @@ module Toys
|
|
216
216
|
@show_source_path = show_source_path
|
217
217
|
@stream = stream
|
218
218
|
@styled_output = styled_output
|
219
|
-
@use_less = use_less
|
219
|
+
@use_less = use_less && !Compat::IS_JRUBY
|
220
220
|
end
|
221
221
|
|
222
222
|
##
|
@@ -294,7 +294,7 @@ module Toys
|
|
294
294
|
include_hidden: context[SHOW_ALL_SUBTOOLS_KEY], show_source_path: @show_source_path,
|
295
295
|
wrap_width: terminal.width
|
296
296
|
)
|
297
|
-
if
|
297
|
+
if less_path
|
298
298
|
require "toys/utils/exec"
|
299
299
|
Utils::Exec.new.exec([less_path, "-R"], in: [:string, str])
|
300
300
|
else
|
@@ -333,7 +333,7 @@ module Toys
|
|
333
333
|
dict = loader.list_subtools(tool_name).map(&:simple_name)
|
334
334
|
suggestions = Compat.suggestions(next_word, dict)
|
335
335
|
tool_name = (tool_name + [next_word]).join(" ")
|
336
|
-
message = "Tool not found: \"#{tool_name}\"
|
336
|
+
message = "Tool not found: \"#{tool_name}\""
|
337
337
|
unless suggestions.empty?
|
338
338
|
suggestions_str = suggestions.join("\n ")
|
339
339
|
message = "#{message}\nDid you mean... #{suggestions_str}"
|
@@ -82,6 +82,10 @@ module Toys
|
|
82
82
|
#
|
83
83
|
# include :exec, exit_on_nonzero_status: true
|
84
84
|
#
|
85
|
+
# **:e** can be used as a shortcut for **:exit_on_nonzero_status**
|
86
|
+
#
|
87
|
+
# include :exec, e: true
|
88
|
+
#
|
85
89
|
module Exec
|
86
90
|
include Mixin
|
87
91
|
|
@@ -343,25 +347,38 @@ module Toys
|
|
343
347
|
|
344
348
|
## @private
|
345
349
|
def self._setup_exec_opts(opts, context)
|
346
|
-
if opts.key?(:
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
opts = opts.merge(result_callback: result_callback)
|
352
|
-
opts.delete(:exit_on_nonzero_status)
|
353
|
-
elsif opts.key?(:result_callback)
|
354
|
-
orig_callback = opts[:result_callback]
|
355
|
-
result_callback =
|
356
|
-
if orig_callback.is_a?(::Symbol)
|
357
|
-
context.method(orig_callback)
|
358
|
-
elsif orig_callback.respond_to?(:call)
|
359
|
-
proc { |r| orig_callback.call(r, context) }
|
360
|
-
end
|
361
|
-
opts = opts.merge(result_callback: result_callback)
|
350
|
+
if opts.key?(:result_callback)
|
351
|
+
opts = _setup_result_callback_option(opts, context)
|
352
|
+
end
|
353
|
+
if opts.key?(:exit_on_nonzero_status) || opts.key?(:e)
|
354
|
+
opts = _setup_e_option(opts, context)
|
362
355
|
end
|
363
356
|
opts
|
364
357
|
end
|
358
|
+
|
359
|
+
## @private
|
360
|
+
def self._setup_e_option(opts, context)
|
361
|
+
result_callback =
|
362
|
+
if opts[:exit_on_nonzero_status] || opts[:e]
|
363
|
+
proc { |r| context.exit(r.exit_code) if r.error? }
|
364
|
+
end
|
365
|
+
opts = opts.merge(result_callback: result_callback)
|
366
|
+
opts.delete(:exit_on_nonzero_status)
|
367
|
+
opts.delete(:e)
|
368
|
+
opts
|
369
|
+
end
|
370
|
+
|
371
|
+
## @private
|
372
|
+
def self._setup_result_callback_option(opts, context)
|
373
|
+
orig_callback = opts[:result_callback]
|
374
|
+
result_callback =
|
375
|
+
if orig_callback.is_a?(::Symbol)
|
376
|
+
context.method(orig_callback)
|
377
|
+
elsif orig_callback.respond_to?(:call)
|
378
|
+
proc { |r| orig_callback.call(r, context) }
|
379
|
+
end
|
380
|
+
opts.merge(result_callback: result_callback)
|
381
|
+
end
|
365
382
|
end
|
366
383
|
end
|
367
384
|
end
|
@@ -54,7 +54,7 @@ module Toys
|
|
54
54
|
def self.gems
|
55
55
|
require "toys/utils/gems"
|
56
56
|
# rubocop:disable Naming/MemoizedInstanceVariableName
|
57
|
-
@__gems ||= Utils::Gems.new(
|
57
|
+
@__gems ||= Utils::Gems.new(**@__gems_opts)
|
58
58
|
# rubocop:enable Naming/MemoizedInstanceVariableName
|
59
59
|
end
|
60
60
|
|
data/lib/toys/tool.rb
CHANGED
@@ -87,6 +87,7 @@ module Toys
|
|
87
87
|
|
88
88
|
@interrupt_handler = nil
|
89
89
|
@usage_error_handler = nil
|
90
|
+
@delegate_target = nil
|
90
91
|
|
91
92
|
@completion = DefaultCompletion.new
|
92
93
|
end
|
@@ -259,6 +260,14 @@ module Toys
|
|
259
260
|
#
|
260
261
|
attr_reader :usage_error_handler
|
261
262
|
|
263
|
+
##
|
264
|
+
# The full name of the delegate target, if any.
|
265
|
+
#
|
266
|
+
# @return [Array<String>] if this tool delegates
|
267
|
+
# @return [nil] if this tool does not delegate
|
268
|
+
#
|
269
|
+
attr_reader :delegate_target
|
270
|
+
|
262
271
|
##
|
263
272
|
# The local name of this tool, i.e. the last element of the full name.
|
264
273
|
#
|
@@ -587,7 +596,7 @@ module Toys
|
|
587
596
|
"A completion named #{name.inspect} has already been defined in tool" \
|
588
597
|
" #{display_name.inspect}."
|
589
598
|
end
|
590
|
-
@completions[name] = Toys::Completion.create(completion, options, &block)
|
599
|
+
@completions[name] = Toys::Completion.create(completion, **options, &block)
|
591
600
|
self
|
592
601
|
end
|
593
602
|
|
@@ -916,7 +925,7 @@ module Toys
|
|
916
925
|
# @param proc [Proc] The runnable block
|
917
926
|
#
|
918
927
|
def run_handler=(proc)
|
919
|
-
check_definition_state
|
928
|
+
check_definition_state(is_method: true)
|
920
929
|
@tool_class.to_run(&proc)
|
921
930
|
end
|
922
931
|
|
@@ -926,7 +935,7 @@ module Toys
|
|
926
935
|
# @param handler [Proc,Symbol] The interrupt handler
|
927
936
|
#
|
928
937
|
def interrupt_handler=(handler)
|
929
|
-
check_definition_state
|
938
|
+
check_definition_state(is_method: true)
|
930
939
|
if !handler.is_a?(::Proc) && !handler.is_a?(::Symbol) && !handler.nil?
|
931
940
|
raise ToolDefinitionError, "Interrupt handler must be a proc or symbol"
|
932
941
|
end
|
@@ -939,7 +948,7 @@ module Toys
|
|
939
948
|
# @param handler [Proc,Symbol] The usage error handler
|
940
949
|
#
|
941
950
|
def usage_error_handler=(handler)
|
942
|
-
check_definition_state
|
951
|
+
check_definition_state(is_method: true)
|
943
952
|
if !handler.is_a?(::Proc) && !handler.is_a?(::Symbol) && !handler.nil?
|
944
953
|
raise ToolDefinitionError, "Usage error handler must be a proc or symbol"
|
945
954
|
end
|
@@ -979,15 +988,17 @@ module Toys
|
|
979
988
|
# @param spec [Object]
|
980
989
|
#
|
981
990
|
def completion=(spec)
|
982
|
-
|
991
|
+
spec = resolve_completion_name(spec)
|
992
|
+
spec =
|
983
993
|
case spec
|
984
994
|
when nil, :default
|
985
|
-
DefaultCompletion
|
995
|
+
DefaultCompletion
|
986
996
|
when ::Hash
|
987
|
-
|
997
|
+
spec[:""].nil? ? spec.merge({"": DefaultCompletion}) : spec
|
988
998
|
else
|
989
|
-
|
999
|
+
spec
|
990
1000
|
end
|
1001
|
+
@completion = Completion.create(spec, **{})
|
991
1002
|
end
|
992
1003
|
|
993
1004
|
##
|
@@ -1005,6 +1016,35 @@ module Toys
|
|
1005
1016
|
lookup_custom_context_directory || source_info&.context_directory
|
1006
1017
|
end
|
1007
1018
|
|
1019
|
+
##
|
1020
|
+
# Causes this tool to delegate to another tool.
|
1021
|
+
#
|
1022
|
+
# @param target [Array<String>] The full path to the delegate tool.
|
1023
|
+
# @return [self]
|
1024
|
+
#
|
1025
|
+
def delegate_to(target)
|
1026
|
+
if @delegate_target
|
1027
|
+
raise ToolDefinitionError,
|
1028
|
+
"Cannot delegate tool #{display_name.inspect} because" \
|
1029
|
+
" it already delegates to \"#{@delegate_target.join(' ')}\"."
|
1030
|
+
end
|
1031
|
+
if includes_arguments?
|
1032
|
+
raise ToolDefinitionError,
|
1033
|
+
"Cannot delegate tool #{display_name.inspect} because" \
|
1034
|
+
" arguments have already been defined."
|
1035
|
+
end
|
1036
|
+
if runnable?
|
1037
|
+
raise ToolDefinitionError,
|
1038
|
+
"Cannot delegate tool #{display_name.inspect} because" \
|
1039
|
+
" the run method has already been defined."
|
1040
|
+
end
|
1041
|
+
disable_argument_parsing
|
1042
|
+
self.run_handler = make_delegation_run_handler(target)
|
1043
|
+
self.completion = DefaultCompletion.new(delegation_target: target)
|
1044
|
+
@delegate_target = target
|
1045
|
+
self
|
1046
|
+
end
|
1047
|
+
|
1008
1048
|
##
|
1009
1049
|
# Lookup the custom context directory in this tool and its ancestors.
|
1010
1050
|
# @private
|
@@ -1013,20 +1053,6 @@ module Toys
|
|
1013
1053
|
custom_context_directory || @parent&.lookup_custom_context_directory
|
1014
1054
|
end
|
1015
1055
|
|
1016
|
-
## @private
|
1017
|
-
def scalar_acceptor(spec = nil, type_desc: nil, &block)
|
1018
|
-
Acceptor.create(resolve_acceptor_name(spec), type_desc: type_desc, &block)
|
1019
|
-
end
|
1020
|
-
|
1021
|
-
## @private
|
1022
|
-
def scalar_completion(spec = nil, **options, &block)
|
1023
|
-
if spec.nil? && block.nil? || spec == :default
|
1024
|
-
options
|
1025
|
-
else
|
1026
|
-
Completion.create(resolve_completion_name(spec), options, &block)
|
1027
|
-
end
|
1028
|
-
end
|
1029
|
-
|
1030
1056
|
##
|
1031
1057
|
# Mark this tool as having at least one module included.
|
1032
1058
|
# @private
|
@@ -1074,7 +1100,7 @@ module Toys
|
|
1074
1100
|
# or from the DSL only.
|
1075
1101
|
# @private
|
1076
1102
|
#
|
1077
|
-
def check_definition_state(is_arg: false)
|
1103
|
+
def check_definition_state(is_arg: false, is_method: false)
|
1078
1104
|
if @definition_finished
|
1079
1105
|
raise ToolDefinitionError,
|
1080
1106
|
"Defintion of tool #{display_name.inspect} is already finished"
|
@@ -1083,6 +1109,10 @@ module Toys
|
|
1083
1109
|
raise ToolDefinitionError,
|
1084
1110
|
"Tool #{display_name.inspect} has disabled argument parsing"
|
1085
1111
|
end
|
1112
|
+
if (is_arg || is_method) && delegate_target
|
1113
|
+
raise ToolDefinitionError,
|
1114
|
+
"Tool #{display_name.inspect} is already delegating to another tool"
|
1115
|
+
end
|
1086
1116
|
self
|
1087
1117
|
end
|
1088
1118
|
|
@@ -1099,14 +1129,18 @@ module Toys
|
|
1099
1129
|
# @param complete_args [Boolean] Whether to complete positional args
|
1100
1130
|
# @param complete_flags [Boolean] Whether to complete flag names
|
1101
1131
|
# @param complete_flag_values [Boolean] Whether to complete flag values
|
1132
|
+
# @param delegation_target [Array<String>,nil] Delegation target, or
|
1133
|
+
# `nil` if none.
|
1102
1134
|
#
|
1103
1135
|
def initialize(complete_subtools: true, include_hidden_subtools: false,
|
1104
|
-
complete_args: true, complete_flags: true, complete_flag_values: true
|
1136
|
+
complete_args: true, complete_flags: true, complete_flag_values: true,
|
1137
|
+
delegation_target: nil)
|
1105
1138
|
@complete_subtools = complete_subtools
|
1106
1139
|
@include_hidden_subtools = include_hidden_subtools
|
1107
1140
|
@complete_flags = complete_flags
|
1108
1141
|
@complete_args = complete_args
|
1109
1142
|
@complete_flag_values = complete_flag_values
|
1143
|
+
@delegation_target = delegation_target
|
1110
1144
|
end
|
1111
1145
|
|
1112
1146
|
##
|
@@ -1149,6 +1183,13 @@ module Toys
|
|
1149
1183
|
@complete_flag_values
|
1150
1184
|
end
|
1151
1185
|
|
1186
|
+
##
|
1187
|
+
# Delegation target, or nil for none.
|
1188
|
+
# @return [Array<String>] if there is a delegation target
|
1189
|
+
# @return [nil] if there is no delegation target
|
1190
|
+
#
|
1191
|
+
attr_accessor :delegation_target
|
1192
|
+
|
1152
1193
|
##
|
1153
1194
|
# Returns candidates for the current completion.
|
1154
1195
|
#
|
@@ -1162,6 +1203,13 @@ module Toys
|
|
1162
1203
|
candidates = subtool_or_arg_candidates(context)
|
1163
1204
|
candidates += plain_flag_candidates(context)
|
1164
1205
|
candidates += flag_value_candidates(context)
|
1206
|
+
if delegation_target
|
1207
|
+
delegate_tool = context.cli.loader.lookup_specific(delegation_target)
|
1208
|
+
if delegate_tool
|
1209
|
+
context = context.with(previous_words: delegation_target)
|
1210
|
+
candidates += delegate_tool.completion.call(context)
|
1211
|
+
end
|
1212
|
+
end
|
1165
1213
|
candidates
|
1166
1214
|
end
|
1167
1215
|
|
@@ -1260,6 +1308,27 @@ module Toys
|
|
1260
1308
|
proc { middleware.config(self, loader, &next_config) }
|
1261
1309
|
end
|
1262
1310
|
|
1311
|
+
def make_delegation_run_handler(target)
|
1312
|
+
lambda do
|
1313
|
+
path = [target.join(" ").inspect]
|
1314
|
+
walk_context = self
|
1315
|
+
until walk_context.nil?
|
1316
|
+
name = walk_context[::Toys::Context::Key::TOOL_NAME]
|
1317
|
+
path << name.join(" ").inspect
|
1318
|
+
if name == target
|
1319
|
+
raise "Delegation loop: #{path.join(' <- ')}"
|
1320
|
+
end
|
1321
|
+
walk_context = walk_context[::Toys::Context::Key::DELEGATED_FROM]
|
1322
|
+
end
|
1323
|
+
cli = self[::Toys::Context::Key::CLI]
|
1324
|
+
cli.loader.load_for_prefix(target)
|
1325
|
+
unless cli.loader.tool_defined?(target)
|
1326
|
+
raise "Delegate target not found: \"#{target.join(' ')}\""
|
1327
|
+
end
|
1328
|
+
exit(cli.run(target + self[::Toys::Context::Key::ARGS], delegated_from: self))
|
1329
|
+
end
|
1330
|
+
end
|
1331
|
+
|
1263
1332
|
def resolve_acceptor_name(name)
|
1264
1333
|
return name unless name.is_a?(::String)
|
1265
1334
|
accept = lookup_acceptor(name)
|
data/lib/toys/utils/help_text.rb
CHANGED
@@ -53,8 +53,14 @@ module Toys
|
|
53
53
|
# @return [Toys::Utils::HelpText]
|
54
54
|
#
|
55
55
|
def self.from_context(context)
|
56
|
+
orig_context = context
|
57
|
+
while (from = context[Context::Key::DELEGATED_FROM])
|
58
|
+
context = from
|
59
|
+
end
|
60
|
+
delegate_target = orig_context == context ? nil : orig_context[Context::Key::TOOL_NAME]
|
56
61
|
cli = context[Context::Key::CLI]
|
57
|
-
new(context[Context::Key::TOOL], cli.loader, cli.executable_name
|
62
|
+
new(context[Context::Key::TOOL], cli.loader, cli.executable_name,
|
63
|
+
delegate_target: delegate_target)
|
58
64
|
end
|
59
65
|
|
60
66
|
##
|
@@ -64,13 +70,16 @@ module Toys
|
|
64
70
|
# @param loader [Toys::Loader] A loader that can provide subcommands.
|
65
71
|
# @param executable_name [String] The name of the executable.
|
66
72
|
# e.g. `"toys"`.
|
73
|
+
# @param delegate_target [Array<String>,nil] The full name of a tool this
|
74
|
+
# tool will delegate to. Default is `nil` for no delegation.
|
67
75
|
#
|
68
76
|
# @return [Toys::Utils::HelpText]
|
69
77
|
#
|
70
|
-
def initialize(tool, loader, executable_name)
|
78
|
+
def initialize(tool, loader, executable_name, delegate_target: nil)
|
71
79
|
@tool = tool
|
72
80
|
@loader = loader
|
73
81
|
@executable_name = executable_name
|
82
|
+
@delegate_target = delegate_target
|
74
83
|
end
|
75
84
|
|
76
85
|
##
|
@@ -99,8 +108,10 @@ module Toys
|
|
99
108
|
left_column_width ||= DEFAULT_LEFT_COLUMN_WIDTH
|
100
109
|
indent ||= DEFAULT_INDENT
|
101
110
|
subtools = find_subtools(recursive, nil, include_hidden)
|
102
|
-
assembler = UsageStringAssembler.new(
|
103
|
-
|
111
|
+
assembler = UsageStringAssembler.new(
|
112
|
+
@tool, @executable_name, @delegate_target, subtools,
|
113
|
+
indent, left_column_width, wrap_width
|
114
|
+
)
|
104
115
|
assembler.result
|
105
116
|
end
|
106
117
|
|
@@ -130,8 +141,10 @@ module Toys
|
|
130
141
|
indent ||= DEFAULT_INDENT
|
131
142
|
indent2 ||= DEFAULT_INDENT
|
132
143
|
subtools = find_subtools(recursive, search, include_hidden)
|
133
|
-
assembler = HelpStringAssembler.new(
|
134
|
-
|
144
|
+
assembler = HelpStringAssembler.new(
|
145
|
+
@tool, @executable_name, @delegate_target, subtools, search, show_source_path,
|
146
|
+
indent, indent2, wrap_width, styled
|
147
|
+
)
|
135
148
|
assembler.result
|
136
149
|
end
|
137
150
|
|
@@ -174,10 +187,11 @@ module Toys
|
|
174
187
|
|
175
188
|
## @private
|
176
189
|
class UsageStringAssembler
|
177
|
-
def initialize(tool, executable_name, subtools,
|
190
|
+
def initialize(tool, executable_name, delegate_target, subtools,
|
178
191
|
indent, left_column_width, wrap_width)
|
179
192
|
@tool = tool
|
180
193
|
@executable_name = executable_name
|
194
|
+
@delegate_target = delegate_target
|
181
195
|
@subtools = subtools
|
182
196
|
@indent = indent
|
183
197
|
@left_column_width = left_column_width
|
@@ -201,9 +215,8 @@ module Toys
|
|
201
215
|
|
202
216
|
def add_synopsis_section
|
203
217
|
synopses = []
|
204
|
-
synopses << namespace_synopsis
|
205
|
-
synopses << tool_synopsis
|
206
|
-
synopses << namespace_synopsis if !@subtools.empty? && @tool.runnable?
|
218
|
+
synopses << namespace_synopsis unless @subtools.empty?
|
219
|
+
synopses << (@delegate_target ? delegate_synopsis : tool_synopsis)
|
207
220
|
first = true
|
208
221
|
synopses.each do |synopsis|
|
209
222
|
@lines << (first ? "Usage: #{synopsis}" : " #{synopsis}")
|
@@ -220,8 +233,13 @@ module Toys
|
|
220
233
|
synopsis.join(" ")
|
221
234
|
end
|
222
235
|
|
236
|
+
def delegate_synopsis
|
237
|
+
target = @delegate_target.join(" ")
|
238
|
+
"#{@executable_name} #{@tool.display_name} [ARGUMENTS FOR \"#{target}\"...]"
|
239
|
+
end
|
240
|
+
|
223
241
|
def namespace_synopsis
|
224
|
-
|
242
|
+
"#{@executable_name} #{@tool.display_name} TOOL [ARGUMENTS...]"
|
225
243
|
end
|
226
244
|
|
227
245
|
def add_flag_group_sections
|
@@ -264,13 +282,7 @@ module Toys
|
|
264
282
|
@lines << "Tools:"
|
265
283
|
@subtools.each do |subtool|
|
266
284
|
tool_name = subtool.full_name.slice(name_len..-1).join(" ")
|
267
|
-
desc
|
268
|
-
if subtool.is_a?(Alias)
|
269
|
-
["(Alias of #{subtool.display_target})"]
|
270
|
-
else
|
271
|
-
wrap_desc(subtool.desc)
|
272
|
-
end
|
273
|
-
add_right_column_desc(tool_name, desc)
|
285
|
+
add_right_column_desc(tool_name, wrap_desc(subtool.desc))
|
274
286
|
end
|
275
287
|
end
|
276
288
|
|
@@ -310,11 +322,12 @@ module Toys
|
|
310
322
|
|
311
323
|
## @private
|
312
324
|
class HelpStringAssembler
|
313
|
-
def initialize(tool, executable_name, subtools, search_term,
|
314
|
-
indent, indent2, wrap_width, styled)
|
325
|
+
def initialize(tool, executable_name, delegate_target, subtools, search_term,
|
326
|
+
show_source_path, indent, indent2, wrap_width, styled)
|
315
327
|
require "toys/utils/terminal"
|
316
328
|
@tool = tool
|
317
329
|
@executable_name = executable_name
|
330
|
+
@delegate_target = delegate_target
|
318
331
|
@subtools = subtools
|
319
332
|
@search_term = search_term
|
320
333
|
@show_source_path = show_source_path
|
@@ -363,9 +376,8 @@ module Toys
|
|
363
376
|
def add_synopsis_section
|
364
377
|
@lines << ""
|
365
378
|
@lines << bold("SYNOPSIS")
|
366
|
-
add_synopsis_clause(namespace_synopsis)
|
367
|
-
add_synopsis_clause(tool_synopsis)
|
368
|
-
add_synopsis_clause(namespace_synopsis) if !@subtools.empty? && @tool.runnable?
|
379
|
+
add_synopsis_clause(namespace_synopsis) unless @subtools.empty?
|
380
|
+
add_synopsis_clause(@delegate_target ? delegate_synopsis : tool_synopsis)
|
369
381
|
end
|
370
382
|
|
371
383
|
def add_synopsis_clause(synopsis)
|
@@ -454,6 +466,13 @@ module Toys
|
|
454
466
|
wrap_indent_indent2(WrappableString.new(synopsis))
|
455
467
|
end
|
456
468
|
|
469
|
+
def delegate_synopsis
|
470
|
+
target = @delegate_target.join(" ")
|
471
|
+
args_clause = underline("ARGUMENTS FOR \"#{target}\"")
|
472
|
+
synopsis = [full_executable_name, "[#{args_clause}...]"]
|
473
|
+
wrap_indent_indent2(WrappableString.new(synopsis))
|
474
|
+
end
|
475
|
+
|
457
476
|
def full_executable_name
|
458
477
|
bold(([@executable_name] + @tool.full_name).join(" "))
|
459
478
|
end
|
@@ -466,7 +485,13 @@ module Toys
|
|
466
485
|
end
|
467
486
|
|
468
487
|
def add_description_section
|
469
|
-
desc =
|
488
|
+
desc = @tool.long_desc
|
489
|
+
if @delegate_target
|
490
|
+
delegate_clause =
|
491
|
+
"Passes all arguments to \"#{@delegate_target.join(' ')}\" if invoked directly."
|
492
|
+
desc = desc.empty? ? [delegate_clause] : desc + ["", delegate_clause]
|
493
|
+
end
|
494
|
+
desc = wrap_indent(desc)
|
470
495
|
return if desc.empty?
|
471
496
|
@lines << ""
|
472
497
|
@lines << bold("DESCRIPTION")
|
@@ -532,13 +557,7 @@ module Toys
|
|
532
557
|
name_len = @tool.full_name.length
|
533
558
|
@subtools.each do |subtool|
|
534
559
|
tool_name = subtool.full_name.slice(name_len..-1).join(" ")
|
535
|
-
desc
|
536
|
-
if subtool.is_a?(Alias)
|
537
|
-
"(Alias of #{subtool.display_target})"
|
538
|
-
else
|
539
|
-
subtool.desc
|
540
|
-
end
|
541
|
-
add_prefix_with_desc(bold(tool_name), desc)
|
560
|
+
add_prefix_with_desc(bold(tool_name), subtool.desc)
|
542
561
|
end
|
543
562
|
end
|
544
563
|
|
@@ -642,13 +661,7 @@ module Toys
|
|
642
661
|
name_len = @tool.full_name.length
|
643
662
|
@subtools.each do |subtool|
|
644
663
|
tool_name = subtool.full_name.slice(name_len..-1).join(" ")
|
645
|
-
desc
|
646
|
-
if subtool.is_a?(Alias)
|
647
|
-
"(Alias of #{subtool.display_target})"
|
648
|
-
else
|
649
|
-
subtool.desc
|
650
|
-
end
|
651
|
-
add_prefix_with_desc(bold(tool_name), desc)
|
664
|
+
add_prefix_with_desc(bold(tool_name), subtool.desc)
|
652
665
|
end
|
653
666
|
end
|
654
667
|
|