tty-option 0.0.0 → 0.1.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 +1 -1
- data/README.md +1653 -1
- data/lib/tty/option.rb +63 -4
- data/lib/tty/option/aggregate_errors.rb +95 -0
- data/lib/tty/option/conversions.rb +126 -0
- data/lib/tty/option/converter.rb +63 -0
- data/lib/tty/option/deep_dup.rb +48 -0
- data/lib/tty/option/dsl.rb +105 -0
- data/lib/tty/option/dsl/arity.rb +49 -0
- data/lib/tty/option/dsl/conversion.rb +17 -0
- data/lib/tty/option/error_aggregator.rb +35 -0
- data/lib/tty/option/errors.rb +144 -0
- data/lib/tty/option/formatter.rb +389 -0
- data/lib/tty/option/inflection.rb +50 -0
- data/lib/tty/option/param_conversion.rb +34 -0
- data/lib/tty/option/param_permitted.rb +30 -0
- data/lib/tty/option/param_validation.rb +48 -0
- data/lib/tty/option/parameter.rb +310 -0
- data/lib/tty/option/parameter/argument.rb +18 -0
- data/lib/tty/option/parameter/environment.rb +20 -0
- data/lib/tty/option/parameter/keyword.rb +15 -0
- data/lib/tty/option/parameter/option.rb +99 -0
- data/lib/tty/option/parameters.rb +157 -0
- data/lib/tty/option/params.rb +122 -0
- data/lib/tty/option/parser.rb +57 -3
- data/lib/tty/option/parser/arguments.rb +166 -0
- data/lib/tty/option/parser/arity_check.rb +34 -0
- data/lib/tty/option/parser/environments.rb +169 -0
- data/lib/tty/option/parser/keywords.rb +158 -0
- data/lib/tty/option/parser/options.rb +273 -0
- data/lib/tty/option/parser/param_types.rb +51 -0
- data/lib/tty/option/parser/required_check.rb +36 -0
- data/lib/tty/option/pipeline.rb +38 -0
- data/lib/tty/option/result.rb +46 -0
- data/lib/tty/option/section.rb +26 -0
- data/lib/tty/option/sections.rb +56 -0
- data/lib/tty/option/usage.rb +166 -0
- data/lib/tty/option/usage_wrapper.rb +58 -0
- data/lib/tty/option/version.rb +3 -3
- metadata +37 -3
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
module Option
|
5
|
+
module DSL
|
6
|
+
module Arity
|
7
|
+
# @api public
|
8
|
+
def one
|
9
|
+
1
|
10
|
+
end
|
11
|
+
|
12
|
+
# @api public
|
13
|
+
def two
|
14
|
+
2
|
15
|
+
end
|
16
|
+
|
17
|
+
# Zero or more arity
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def zero_or_more
|
21
|
+
-1
|
22
|
+
end
|
23
|
+
alias any zero_or_more
|
24
|
+
alias any_args zero_or_more
|
25
|
+
|
26
|
+
# One or more arity
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
def one_or_more
|
30
|
+
-2
|
31
|
+
end
|
32
|
+
|
33
|
+
# Two or more arity
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def two_or_more
|
37
|
+
-3
|
38
|
+
end
|
39
|
+
|
40
|
+
# At last number values for arity
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
def at_least(number)
|
44
|
+
-number.to_i - 1
|
45
|
+
end
|
46
|
+
end # Arity
|
47
|
+
end # DSL
|
48
|
+
end # Option
|
49
|
+
end # TTY
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "inflection"
|
4
|
+
|
5
|
+
module TTY
|
6
|
+
module Option
|
7
|
+
class ErrorAggregator
|
8
|
+
include Inflection
|
9
|
+
|
10
|
+
# Collected errors
|
11
|
+
attr_reader :errors
|
12
|
+
|
13
|
+
def initialize(errors = [], raise_on_parse_error: false)
|
14
|
+
@errors = errors
|
15
|
+
@raise_on_parse_error = raise_on_parse_error
|
16
|
+
end
|
17
|
+
|
18
|
+
# Record or raise an error
|
19
|
+
#
|
20
|
+
# @param [TTY::Option::Error] error
|
21
|
+
# @param [String] message
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def call(error, message = nil)
|
25
|
+
if error.is_a?(Class)
|
26
|
+
error = message.nil? ? error.new : error.new(message)
|
27
|
+
end
|
28
|
+
|
29
|
+
raise(error) if @raise_on_parse_error
|
30
|
+
|
31
|
+
@errors << error
|
32
|
+
end
|
33
|
+
end # ErrorAggregator
|
34
|
+
end # Option
|
35
|
+
end # TTY
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
module Option
|
5
|
+
Error = Class.new(StandardError)
|
6
|
+
|
7
|
+
# Raised when a parameter invariant is invalid
|
8
|
+
ConfigurationError = Class.new(Error)
|
9
|
+
|
10
|
+
# Raised when attempting to register already registered parameter
|
11
|
+
ParameterConflict = Class.new(Error)
|
12
|
+
|
13
|
+
# Raised when overriding already defined conversion
|
14
|
+
ConversionAlreadyDefined = Class.new(Error)
|
15
|
+
|
16
|
+
# Raised when conversion cannot be performed
|
17
|
+
ConversionError = Class.new(Error)
|
18
|
+
|
19
|
+
# Raised when conversion type isn't registered
|
20
|
+
UnsupportedConversion = Class.new(Error)
|
21
|
+
|
22
|
+
# Raised during command line input parsing
|
23
|
+
class ParseError < Error
|
24
|
+
attr_accessor :param
|
25
|
+
end
|
26
|
+
|
27
|
+
# Raised when found unrecognized parameter
|
28
|
+
InvalidParameter = Class.new(ParseError)
|
29
|
+
|
30
|
+
# Raised when an option matches more than one parameter option
|
31
|
+
AmbiguousOption = Class.new(ParseError)
|
32
|
+
|
33
|
+
# Raised when parameter argument doesn't match expected value
|
34
|
+
class InvalidArgument < ParseError
|
35
|
+
MESSAGE = "value of `%<value>s` fails validation for '%<name>s' %<type>s"
|
36
|
+
|
37
|
+
def initialize(param_or_message, value = nil)
|
38
|
+
if param_or_message.is_a?(Parameter)
|
39
|
+
@param = param_or_message
|
40
|
+
|
41
|
+
message = format(MESSAGE,
|
42
|
+
value: value,
|
43
|
+
name: param.name,
|
44
|
+
type: param.to_sym)
|
45
|
+
else
|
46
|
+
message = param_or_message
|
47
|
+
end
|
48
|
+
|
49
|
+
super(message)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Raised when number of parameter arguments doesn't match
|
54
|
+
class InvalidArity < ParseError
|
55
|
+
MESSAGE = "%<type>s '%<name>s' should appear %<expect>s but appeared %<actual>s"
|
56
|
+
|
57
|
+
def initialize(param_or_message, arity = nil)
|
58
|
+
if param_or_message.is_a?(Parameter)
|
59
|
+
@param = param_or_message
|
60
|
+
prefix = param.arity < 0 ? "at least " : ""
|
61
|
+
expected_arity = param.arity < 0 ? param.arity.abs - 1 : param.arity
|
62
|
+
|
63
|
+
message = format(MESSAGE,
|
64
|
+
type: param.to_sym,
|
65
|
+
name: param.name,
|
66
|
+
expect: prefix + pluralize("time", expected_arity),
|
67
|
+
actual: pluralize("time", arity))
|
68
|
+
else
|
69
|
+
message = param_or_message
|
70
|
+
end
|
71
|
+
|
72
|
+
super(message)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Pluralize a noun
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
def pluralize(noun, count = 1)
|
79
|
+
"#{count} #{noun}#{'s' unless count == 1}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Raised when conversion provided with unexpected argument
|
84
|
+
class InvalidConversionArgument < ParseError
|
85
|
+
MESSAGE = "cannot convert value of `%<value>s` into '%<cast>s' type " \
|
86
|
+
"for '%<name>s' %<type>s"
|
87
|
+
|
88
|
+
def initialize(param, value)
|
89
|
+
@param = param
|
90
|
+
message = format(MESSAGE, value: value, cast: param.convert,
|
91
|
+
name: param.name, type: param.to_sym)
|
92
|
+
super(message)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Raised when option requires an argument
|
97
|
+
class MissingArgument < ParseError
|
98
|
+
MESSAGE = "%<type>s %<name>s requires an argument"
|
99
|
+
|
100
|
+
def initialize(param)
|
101
|
+
@param = param
|
102
|
+
message = format(MESSAGE, type: param.to_sym, name: param.name)
|
103
|
+
super(message)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Raised when a parameter is required but not present
|
108
|
+
class MissingParameter < ParseError
|
109
|
+
MESSAGE = "%<type>s '%<name>s' must be provided"
|
110
|
+
|
111
|
+
def initialize(param_or_message)
|
112
|
+
if param_or_message.is_a?(Parameter)
|
113
|
+
@param = param_or_message
|
114
|
+
message = format(MESSAGE, name: param.name, type: param.to_sym)
|
115
|
+
else
|
116
|
+
message = param_or_message
|
117
|
+
end
|
118
|
+
|
119
|
+
super(message)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Raised when argument value isn't permitted
|
124
|
+
class UnpermittedArgument < ParseError
|
125
|
+
MESSAGE = "unpermitted value `%<value>s` for '%<name>s' %<type>s: " \
|
126
|
+
"choose from %<choices>s"
|
127
|
+
|
128
|
+
def initialize(param_or_message, value = nil)
|
129
|
+
if param_or_message.is_a?(Parameter)
|
130
|
+
@param = param_or_message
|
131
|
+
message = format(MESSAGE,
|
132
|
+
value: value,
|
133
|
+
name: param.name,
|
134
|
+
type: param.to_sym,
|
135
|
+
choices: param.permit.join(", "))
|
136
|
+
else
|
137
|
+
message = param_or_message
|
138
|
+
end
|
139
|
+
|
140
|
+
super(message)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end # Option
|
144
|
+
end # TTY
|
@@ -0,0 +1,389 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "sections"
|
4
|
+
require_relative "usage_wrapper"
|
5
|
+
|
6
|
+
module TTY
|
7
|
+
module Option
|
8
|
+
class Formatter
|
9
|
+
include UsageWrapper
|
10
|
+
|
11
|
+
SHORT_OPT_LENGTH = 4
|
12
|
+
DEFAULT_WIDTH = 80
|
13
|
+
NEWLINE = "\n"
|
14
|
+
ELLIPSIS = "..."
|
15
|
+
SPACE = " "
|
16
|
+
|
17
|
+
DEFAULT_PARAM_DISPLAY = ->(str) { str.to_s.upcase }
|
18
|
+
DEFAULT_ORDER = ->(params) { params.sort }
|
19
|
+
NOOP_PROC = ->(param) { param }
|
20
|
+
DEFAULT_NAME_SELECTOR = ->(param) { param.name }
|
21
|
+
|
22
|
+
# @api public
|
23
|
+
def self.help(parameters, usage, **config, &block)
|
24
|
+
new(parameters, usage, **config).help(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :width
|
28
|
+
|
29
|
+
# Create a help formatter
|
30
|
+
#
|
31
|
+
# @param [Parameters]
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def initialize(parameters, usage, param_display: DEFAULT_PARAM_DISPLAY,
|
35
|
+
width: DEFAULT_WIDTH, order: DEFAULT_ORDER, indent: 0)
|
36
|
+
@parameters = parameters
|
37
|
+
@usage = usage
|
38
|
+
@param_display = param_display
|
39
|
+
@order = order
|
40
|
+
@width = width
|
41
|
+
@indent = indent
|
42
|
+
@space_indent = SPACE * indent
|
43
|
+
@param_indent = indent + 2
|
44
|
+
@section_names = {
|
45
|
+
usage: "Usage:",
|
46
|
+
arguments: "Arguments:",
|
47
|
+
keywords: "Keywords:",
|
48
|
+
options: "Options:",
|
49
|
+
env: "Environment:",
|
50
|
+
examples: "Examples:"
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# A formatted help usage information
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def help(&block)
|
60
|
+
sections = Sections.new
|
61
|
+
|
62
|
+
sections.add(:header, help_header) if @usage.header?
|
63
|
+
sections.add(:banner, help_banner)
|
64
|
+
sections.add(:description, help_description) if @usage.desc?
|
65
|
+
|
66
|
+
if @parameters.arguments.any?(&:display?)
|
67
|
+
sections.add(:arguments, help_arguments)
|
68
|
+
end
|
69
|
+
|
70
|
+
if @parameters.keywords.any?(&:display?)
|
71
|
+
sections.add(:keywords, help_keywords)
|
72
|
+
end
|
73
|
+
|
74
|
+
if @parameters.options?
|
75
|
+
sections.add(:options, help_options)
|
76
|
+
end
|
77
|
+
|
78
|
+
if @parameters.environments.any?(&:display?)
|
79
|
+
sections.add(:environments, help_environments)
|
80
|
+
end
|
81
|
+
|
82
|
+
sections.add(:examples, help_examples) if @usage.example?
|
83
|
+
sections.add(:footer, help_footer) if @usage.footer?
|
84
|
+
|
85
|
+
if block_given?
|
86
|
+
yield(sections)
|
87
|
+
end
|
88
|
+
|
89
|
+
formatted = sections.reject(&:empty?).join(NEWLINE)
|
90
|
+
formatted.end_with?(NEWLINE) ? formatted : formatted + NEWLINE
|
91
|
+
end
|
92
|
+
|
93
|
+
def help_header
|
94
|
+
"#{format_multiline(@usage.header, @indent)}#{NEWLINE}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def help_banner
|
98
|
+
(@usage.banner? ? @usage.banner : format_usage)
|
99
|
+
end
|
100
|
+
|
101
|
+
def help_description
|
102
|
+
"#{NEWLINE}#{format_description}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def help_arguments
|
106
|
+
"#{NEWLINE}#{@space_indent}#{@section_names[:arguments]}#{NEWLINE}" +
|
107
|
+
format_section(@parameters.arguments, ->(param) do
|
108
|
+
@param_display.(param.name)
|
109
|
+
end)
|
110
|
+
end
|
111
|
+
|
112
|
+
def help_keywords
|
113
|
+
"#{NEWLINE}#{@space_indent}#{@section_names[:keywords]}#{NEWLINE}" +
|
114
|
+
format_section(@parameters.keywords, ->(param) do
|
115
|
+
kwarg_param_display(param).split("=").map(&@param_display).join("=")
|
116
|
+
end)
|
117
|
+
end
|
118
|
+
|
119
|
+
def help_options
|
120
|
+
"#{NEWLINE}#{@space_indent}#{@section_names[:options]}#{NEWLINE}" +
|
121
|
+
format_options
|
122
|
+
end
|
123
|
+
|
124
|
+
def help_environments
|
125
|
+
"#{NEWLINE}#{@space_indent}#{@section_names[:env]}#{NEWLINE}" +
|
126
|
+
format_section(@order.(@parameters.environments))
|
127
|
+
end
|
128
|
+
|
129
|
+
def help_examples
|
130
|
+
"#{NEWLINE}#{@space_indent}#{@section_names[:examples]}#{NEWLINE}" +
|
131
|
+
format_examples
|
132
|
+
end
|
133
|
+
|
134
|
+
def help_footer
|
135
|
+
"#{NEWLINE}#{format_multiline(@usage.footer, @indent)}"
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
# Provide a default usage banner
|
141
|
+
#
|
142
|
+
# @api private
|
143
|
+
def format_usage
|
144
|
+
usage = @space_indent + @section_names[:usage] + SPACE
|
145
|
+
output = []
|
146
|
+
output << @usage.program
|
147
|
+
output << " #{@usage.commands.join(" ")}" if @usage.command?
|
148
|
+
output << " [#{@param_display.("options")}]" if @parameters.options?
|
149
|
+
output << " [#{@param_display.("environment")}]" if @parameters.environments?
|
150
|
+
output << " #{format_arguments_usage}" if @parameters.arguments?
|
151
|
+
output << " #{format_keywords_usage}" if @parameters.keywords?
|
152
|
+
usage + wrap(output.join, indent: usage.length, width: width)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Format arguments
|
156
|
+
#
|
157
|
+
# @api private
|
158
|
+
def format_arguments_usage
|
159
|
+
return "" unless @parameters.arguments?
|
160
|
+
|
161
|
+
@parameters.arguments.reduce([]) do |acc, arg|
|
162
|
+
next acc if arg.hidden?
|
163
|
+
|
164
|
+
acc << format_argument_usage(arg)
|
165
|
+
end.join(SPACE)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Provide an argument summary
|
169
|
+
#
|
170
|
+
# @api private
|
171
|
+
def format_argument_usage(arg)
|
172
|
+
arg_name = @param_display.(arg.name)
|
173
|
+
format_parameter_usage(arg, arg_name)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Format parameter usage
|
177
|
+
#
|
178
|
+
# @api private
|
179
|
+
def format_parameter_usage(param, param_name)
|
180
|
+
args = []
|
181
|
+
if 0 < param.arity
|
182
|
+
args << "[" if param.optional?
|
183
|
+
args << param_name
|
184
|
+
(param.arity - 1).times { args << " #{param_name}" }
|
185
|
+
args. << "]" if param.optional?
|
186
|
+
args.join
|
187
|
+
else
|
188
|
+
(param.arity.abs - 1).times { args << param_name }
|
189
|
+
args << "[#{param_name}#{ELLIPSIS}]"
|
190
|
+
args.join(SPACE)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Format keywords usage
|
195
|
+
#
|
196
|
+
# @api private
|
197
|
+
def format_keywords_usage
|
198
|
+
return "" unless @parameters.keywords?
|
199
|
+
|
200
|
+
@parameters.keywords.reduce([]) do |acc, kwarg|
|
201
|
+
next acc if kwarg.hidden?
|
202
|
+
|
203
|
+
acc << format_keyword_usage(kwarg)
|
204
|
+
end.join(SPACE)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Provide a keyword summary
|
208
|
+
#
|
209
|
+
# @api private
|
210
|
+
def format_keyword_usage(kwarg)
|
211
|
+
param_name = kwarg_param_display(kwarg, @param_display)
|
212
|
+
format_parameter_usage(kwarg, param_name)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Provide a keyword argument display format
|
216
|
+
#
|
217
|
+
# @api private
|
218
|
+
def kwarg_param_display(kwarg, param_display = NOOP_PROC)
|
219
|
+
kwarg_name = param_display.(kwarg.name)
|
220
|
+
conv_name = case kwarg.convert
|
221
|
+
when Proc, NilClass
|
222
|
+
kwarg_name
|
223
|
+
else
|
224
|
+
param_display.(kwarg.convert)
|
225
|
+
end
|
226
|
+
|
227
|
+
"#{kwarg_name}=#{conv_name}"
|
228
|
+
end
|
229
|
+
|
230
|
+
# Format a parameter section in the help display
|
231
|
+
#
|
232
|
+
# @param [String] parameters_name
|
233
|
+
# the name of parameter type
|
234
|
+
#
|
235
|
+
# @param [Proc] name_selector
|
236
|
+
# selects a name from the parameter, by defeault the name
|
237
|
+
#
|
238
|
+
# @return [String]
|
239
|
+
#
|
240
|
+
# @api private
|
241
|
+
def format_section(params, name_selector = DEFAULT_NAME_SELECTOR)
|
242
|
+
longest_param = params.map(&name_selector).compact.max_by(&:length).length
|
243
|
+
|
244
|
+
params.reduce([]) do |acc, param|
|
245
|
+
next acc if param.hidden?
|
246
|
+
|
247
|
+
acc << format_section_parameter(param, longest_param, name_selector)
|
248
|
+
end.join(NEWLINE)
|
249
|
+
end
|
250
|
+
|
251
|
+
# Format a section parameter line
|
252
|
+
#
|
253
|
+
# @return [String]
|
254
|
+
#
|
255
|
+
# @api private
|
256
|
+
def format_section_parameter(param, longest_param, name_selector)
|
257
|
+
line = []
|
258
|
+
desc = []
|
259
|
+
indent = @param_indent + longest_param + 2
|
260
|
+
param_name = name_selector.(param)
|
261
|
+
|
262
|
+
if param.desc?
|
263
|
+
line << format("%s%-#{longest_param}s", SPACE * @param_indent, param_name)
|
264
|
+
desc << " #{param.desc}"
|
265
|
+
else
|
266
|
+
line << format("%s%s", SPACE * @param_indent, param_name)
|
267
|
+
end
|
268
|
+
|
269
|
+
if param.permit?
|
270
|
+
desc << format(" (permitted: %s)", param.permit.join(", "))
|
271
|
+
end
|
272
|
+
|
273
|
+
if (default = format_default(param))
|
274
|
+
desc << default
|
275
|
+
end
|
276
|
+
|
277
|
+
line << wrap(desc.join, indent: indent, width: width)
|
278
|
+
line.join
|
279
|
+
end
|
280
|
+
|
281
|
+
# Format multiline description
|
282
|
+
#
|
283
|
+
# @api private
|
284
|
+
def format_description
|
285
|
+
format_multiline(@usage.desc, @indent)
|
286
|
+
end
|
287
|
+
|
288
|
+
# Returns all the options formatted to fit 80 columns
|
289
|
+
#
|
290
|
+
# @return [String]
|
291
|
+
#
|
292
|
+
# @api private
|
293
|
+
def format_options
|
294
|
+
return "" if @parameters.options.empty?
|
295
|
+
|
296
|
+
longest_option = @parameters.options.map(&:long)
|
297
|
+
.compact.max_by(&:length).length
|
298
|
+
any_short = @parameters.options.map(&:short).compact.any?
|
299
|
+
ordered_options = @order.(@parameters.options)
|
300
|
+
|
301
|
+
ordered_options.reduce([]) do |acc, option|
|
302
|
+
next acc if option.hidden?
|
303
|
+
acc << format_option(option, longest_option, any_short)
|
304
|
+
end.join(NEWLINE)
|
305
|
+
end
|
306
|
+
|
307
|
+
# Format an option
|
308
|
+
#
|
309
|
+
# @api private
|
310
|
+
def format_option(option, longest_length, any_short)
|
311
|
+
line = [@space_indent]
|
312
|
+
desc = []
|
313
|
+
indent = @indent
|
314
|
+
|
315
|
+
if any_short
|
316
|
+
short_option = option.short? ? option.short_name : SPACE
|
317
|
+
line << format("%#{SHORT_OPT_LENGTH}s", short_option)
|
318
|
+
indent += SHORT_OPT_LENGTH
|
319
|
+
end
|
320
|
+
|
321
|
+
# short & long option separator
|
322
|
+
line << ((option.short? && option.long?) ? ", " : " ")
|
323
|
+
indent += 2
|
324
|
+
|
325
|
+
if option.long?
|
326
|
+
if option.desc?
|
327
|
+
line << format("%-#{longest_length}s", option.long)
|
328
|
+
else
|
329
|
+
line << option.long
|
330
|
+
end
|
331
|
+
else
|
332
|
+
line << format("%-#{longest_length}s", SPACE)
|
333
|
+
end
|
334
|
+
indent += longest_length
|
335
|
+
|
336
|
+
if option.desc?
|
337
|
+
desc << " #{option.desc}"
|
338
|
+
end
|
339
|
+
indent += 2
|
340
|
+
|
341
|
+
if option.permit?
|
342
|
+
desc << format(" (permitted: %s)", option.permit.join(","))
|
343
|
+
end
|
344
|
+
|
345
|
+
if (default = format_default(option))
|
346
|
+
desc << default
|
347
|
+
end
|
348
|
+
|
349
|
+
line << wrap(desc.join, indent: indent, width: width)
|
350
|
+
|
351
|
+
line.join
|
352
|
+
end
|
353
|
+
|
354
|
+
# Format default value
|
355
|
+
#
|
356
|
+
# @api private
|
357
|
+
def format_default(param)
|
358
|
+
return if !param.default? || [true, false].include?(param.default)
|
359
|
+
|
360
|
+
if param.default.is_a?(String)
|
361
|
+
format(" (default %p)", param.default)
|
362
|
+
else
|
363
|
+
format(" (default %s)", param.default)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Format examples section
|
368
|
+
#
|
369
|
+
# @api private
|
370
|
+
def format_examples
|
371
|
+
format_multiline(@usage.example, @param_indent)
|
372
|
+
end
|
373
|
+
|
374
|
+
# Format multiline content
|
375
|
+
#
|
376
|
+
# @api private
|
377
|
+
def format_multiline(lines, indent)
|
378
|
+
last_index = lines.size - 1
|
379
|
+
lines.map.with_index do |line, i|
|
380
|
+
line.map do |part|
|
381
|
+
part.split(NEWLINE).map do |p|
|
382
|
+
wrap(p, indent: indent, width: width, indent_first: true)
|
383
|
+
end.join(NEWLINE)
|
384
|
+
end.join(NEWLINE) + (last_index != i ? NEWLINE : "")
|
385
|
+
end.join(NEWLINE)
|
386
|
+
end
|
387
|
+
end # Formatter
|
388
|
+
end # Option
|
389
|
+
end # TTY
|