shellopts 2.0.0.pre.13 → 2.0.0.pre.14
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/TODO +15 -135
- data/lib/ext/algorithm.rb +14 -0
- data/lib/ext/ruby_env.rb +8 -0
- data/lib/shellopts.rb +90 -228
- data/lib/shellopts/args.rb +1 -1
- data/lib/shellopts/ast/command.rb +101 -30
- data/lib/shellopts/ast/dump.rb +28 -0
- data/lib/shellopts/ast/option.rb +8 -14
- data/lib/shellopts/ast/parser.rb +106 -0
- data/lib/shellopts/constants.rb +88 -0
- data/lib/shellopts/exceptions.rb +21 -0
- data/lib/shellopts/formatter.rb +125 -0
- data/lib/shellopts/grammar/analyzer.rb +76 -0
- data/lib/shellopts/grammar/command.rb +67 -60
- data/lib/shellopts/grammar/dump.rb +56 -0
- data/lib/shellopts/grammar/lexer.rb +56 -0
- data/lib/shellopts/grammar/option.rb +49 -60
- data/lib/shellopts/grammar/parser.rb +78 -0
- data/lib/shellopts/version.rb +2 -2
- data/shellopts.gemspec +1 -1
- metadata +13 -15
- data/lib/ext/array.rb +0 -9
- data/lib/main.rb +0 -1
- data/lib/shellopts/ast/node.rb +0 -37
- data/lib/shellopts/ast/program.rb +0 -14
- data/lib/shellopts/compiler.rb +0 -128
- data/lib/shellopts/generator.rb +0 -15
- data/lib/shellopts/grammar/node.rb +0 -33
- data/lib/shellopts/grammar/program.rb +0 -65
- data/lib/shellopts/idr.rb +0 -236
- data/lib/shellopts/main.rb +0 -10
- data/lib/shellopts/option_struct.rb +0 -148
- data/lib/shellopts/parser.rb +0 -106
- data/lib/shellopts/shellopts.rb +0 -123
data/lib/shellopts/args.rb
CHANGED
@@ -1,41 +1,112 @@
|
|
1
1
|
module ShellOpts
|
2
2
|
module Ast
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
attr_accessor :subcommand
|
11
|
-
|
12
|
-
def initialize(grammar, name)
|
13
|
-
super(grammar, name)
|
14
|
-
@options = []
|
3
|
+
# Note that Command is derived from BasicObject to minimize the number of
|
4
|
+
# reserved names
|
5
|
+
class Command < BasicObject
|
6
|
+
def initialize(grammar)
|
7
|
+
@grammar = grammar
|
8
|
+
@options_list = []
|
9
|
+
@options_hash = {}
|
15
10
|
@subcommand = nil
|
11
|
+
@subcommands_hash = {} # have at most one element
|
12
|
+
|
13
|
+
@grammar.opts.each { |opt|
|
14
|
+
if opt.argument?
|
15
|
+
self.instance_eval("def #{opt.ident}() @options_hash[:#{opt.ident}] end")
|
16
|
+
end
|
17
|
+
self.instance_eval("def #{opt.ident}?() @options_hash.key?(:#{opt.ident}) end")
|
18
|
+
}
|
19
|
+
|
20
|
+
@grammar.cmds.each { |cmd|
|
21
|
+
self.instance_eval("def #{cmd.ident}!() @subcommands_hash[:#{cmd.ident}] end")
|
22
|
+
}
|
16
23
|
end
|
17
24
|
|
18
|
-
#
|
19
|
-
def
|
20
|
-
|
25
|
+
# Return true if the option was used. Defined in #initialize for each option
|
26
|
+
# def <option>?() end
|
27
|
+
|
28
|
+
# Return the value of the option. Note that repeated options have their
|
29
|
+
# values aggregated into an array. Defined in #initialize for each option
|
30
|
+
# def <option>() end
|
31
|
+
|
32
|
+
# List of Ast::Option objects in the same order as on the command line
|
33
|
+
def options() @options_list end
|
34
|
+
|
35
|
+
# Hash from option identifier to option value. Note that repeated options
|
36
|
+
# have their values aggregated into an array
|
37
|
+
def [](ident) @options_hash[ident].argument end
|
38
|
+
|
39
|
+
# Return the sub-command Command object or nil if not present. Defined in
|
40
|
+
# #initialize for each sub-command
|
41
|
+
# def <command>!() end
|
42
|
+
|
43
|
+
# The sub-command identifier or nil if not present
|
44
|
+
def subcommand() @subcommand && Command.grammar(@subcommand).ident end
|
45
|
+
|
46
|
+
# The sub-command Command object or nil if not present
|
47
|
+
def subcommand!() @subcommand end
|
48
|
+
|
49
|
+
# Class-level accessor methods
|
50
|
+
def self.program?(command) command.__send__(:__is_program__) end
|
51
|
+
def self.grammar(command) command.__send__(:__get_grammar__) end
|
52
|
+
|
53
|
+
# Class-level mutating methods
|
54
|
+
def self.add_option(command, option) command.__send__(:__add_option__, option) end
|
55
|
+
def self.add_command(command, subcommand) command.__send__(:__add_command__, subcommand) end
|
56
|
+
|
57
|
+
private
|
58
|
+
# True if this is a Program object
|
59
|
+
def __is_program__() false end
|
60
|
+
|
61
|
+
# Get grammar
|
62
|
+
def __get_grammar__()
|
63
|
+
@grammar
|
21
64
|
end
|
22
65
|
|
23
|
-
#
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
66
|
+
# Add an option. Only used from the parser
|
67
|
+
def __add_option__(option)
|
68
|
+
@options_list << option
|
69
|
+
if option.grammar.repeatable?
|
70
|
+
(@options_hash[option.grammar.ident] ||= []) << option.argument
|
71
|
+
else
|
72
|
+
@options_hash[option.grammar.ident] = option.argument
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Set sub-command. Only used from the parser
|
77
|
+
def __add_command__(command)
|
78
|
+
ident = Command.grammar(command).ident
|
79
|
+
@subcommand = command
|
80
|
+
@subcommands_hash[ident] = command
|
37
81
|
end
|
38
|
-
|
82
|
+
end
|
83
|
+
|
84
|
+
class Program < Command
|
85
|
+
def __is_program__() true end
|
39
86
|
end
|
40
87
|
end
|
41
88
|
end
|
89
|
+
|
90
|
+
# # TODO: Create class-level methods for access
|
91
|
+
# private
|
92
|
+
# # Return class of object. #class is not defined for BasicObjects so this
|
93
|
+
# # method provides an alternative way of getting the class
|
94
|
+
# def self.class_of(object)
|
95
|
+
# # https://stackoverflow.com/a/18621313/2130986
|
96
|
+
# ::Kernel.instance_method(:class).bind(object).call
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# # Class method implementation of ObjectStruct#instance_variable_set that is
|
100
|
+
# # not defined in a BasicObject
|
101
|
+
# def self.set_variable(this, var, value)
|
102
|
+
# # https://stackoverflow.com/a/18621313/2130986
|
103
|
+
# ::Kernel.instance_method(:instance_variable_set).bind(this).call(var, value)
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# # Class method implementation of ObjectStruct#instance_variable_get that is
|
107
|
+
# # not defined in a BasicObject
|
108
|
+
# def self.get_variable(this, var)
|
109
|
+
# # https://stackoverflow.com/a/18621313/2130986
|
110
|
+
# ::Kernel.instance_method(:instance_variable_get).bind(this).call(var)
|
111
|
+
# end
|
112
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ShellOpts
|
2
|
+
module Ast
|
3
|
+
class Command < BasicObject
|
4
|
+
def dump
|
5
|
+
klass = __is_program__ ? "Program" : "Command"
|
6
|
+
::Kernel.puts "#{@grammar.ident.inspect} (#{klass})"
|
7
|
+
::Kernel.indent {
|
8
|
+
if !options.empty?
|
9
|
+
options.map(&:dump)
|
10
|
+
end
|
11
|
+
if subcommand
|
12
|
+
subcommand!.dump
|
13
|
+
end
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Option
|
19
|
+
def dump
|
20
|
+
puts "#{grammar.ident.inspect} (Option)"
|
21
|
+
indent {
|
22
|
+
puts "name: #{name.inspect}"
|
23
|
+
puts "argument: #{argument.inspect}"
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/shellopts/ast/option.rb
CHANGED
@@ -1,21 +1,15 @@
|
|
1
1
|
module ShellOpts
|
2
2
|
module Ast
|
3
|
-
class Option
|
4
|
-
|
5
|
-
attr_reader :
|
3
|
+
class Option
|
4
|
+
attr_reader :grammar
|
5
|
+
attr_reader :name # The actual name used on the command line
|
6
|
+
attr_reader :argument
|
6
7
|
|
7
|
-
def initialize(grammar, name,
|
8
|
-
|
9
|
-
@
|
8
|
+
def initialize(grammar, name, argument)
|
9
|
+
@grammar = grammar
|
10
|
+
@name = name
|
11
|
+
@argument = argument
|
10
12
|
end
|
11
|
-
|
12
|
-
def values() value end
|
13
|
-
|
14
|
-
# :nocov:
|
15
|
-
def dump
|
16
|
-
super { puts "values: #{values.inspect}" }
|
17
|
-
end
|
18
|
-
# :nocov:
|
19
13
|
end
|
20
14
|
end
|
21
15
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module ShellOpts
|
2
|
+
module Ast
|
3
|
+
# Parse a subcommand
|
4
|
+
class Parser
|
5
|
+
def initialize(grammar, argv)
|
6
|
+
@grammar, @argv = grammar, argv.dup
|
7
|
+
@seen_options = {} # Used to keep track of repeated options
|
8
|
+
@current = nil # Current command
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
@current = program = Program.new(@grammar)
|
13
|
+
parse_command(program)
|
14
|
+
cmd = Command.grammar(@current)
|
15
|
+
!cmd.virtual? or error("'%s' command requires a sub-command")
|
16
|
+
[program, Args.new(cmd, @argv)]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse(grammar, argv)
|
20
|
+
self.new(grammar, argv).call
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def error(message)
|
25
|
+
grammar = Command.grammar(@current)
|
26
|
+
raise Error.new(grammar), message % grammar.name
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_command(command)
|
30
|
+
@seen_options = {} # Every new command resets the seen options
|
31
|
+
while arg = @argv.first
|
32
|
+
if arg == "--"
|
33
|
+
@argv.shift
|
34
|
+
break
|
35
|
+
elsif arg.start_with?("-")
|
36
|
+
parse_option(command)
|
37
|
+
elsif cmd = Command.grammar(command).commands[arg]
|
38
|
+
@argv.shift
|
39
|
+
@current = subcommand = Ast::Command.new(cmd)
|
40
|
+
Command.add_command(command, subcommand)
|
41
|
+
parse_command(subcommand)
|
42
|
+
break
|
43
|
+
else
|
44
|
+
break
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_option(command)
|
50
|
+
# Split into name and argument
|
51
|
+
case @argv.first
|
52
|
+
when /^--(.+?)(?:=(.*))?$/
|
53
|
+
name, arg, short = $1, $2, false
|
54
|
+
opt_name = "--#{name}"
|
55
|
+
when /^-(.)(.+)?$/
|
56
|
+
name, arg, short = $1, $2, true
|
57
|
+
opt_name = "-#{name}"
|
58
|
+
end
|
59
|
+
@argv.shift
|
60
|
+
|
61
|
+
option = Command.grammar(command).options[name] or error "Unknown option '#{opt_name}'"
|
62
|
+
!@seen_options.key?(option.ident) || option.repeatable? or error "Duplicate option '#{opt_name}'"
|
63
|
+
@seen_options[option.ident] = true
|
64
|
+
|
65
|
+
# Parse (optional) argument
|
66
|
+
if option.argument?
|
67
|
+
if arg.nil? && !option.optional?
|
68
|
+
if !@argv.empty?
|
69
|
+
arg = @argv.shift
|
70
|
+
else
|
71
|
+
error "Missing argument for option '#{opt_name}'"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
arg &&= parse_option_arg(option, name, arg)
|
75
|
+
elsif arg && short
|
76
|
+
@argv.unshift("-#{arg}")
|
77
|
+
arg = nil
|
78
|
+
elsif !arg.nil?
|
79
|
+
error "No argument allowed for option '#{opt_name}'"
|
80
|
+
end
|
81
|
+
|
82
|
+
Command.add_option(command, Option.new(option, name, arg))
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_option_arg(option, name, arg)
|
86
|
+
if option.string?
|
87
|
+
arg
|
88
|
+
elsif arg == ""
|
89
|
+
nil
|
90
|
+
elsif option.integer?
|
91
|
+
arg =~ /^-?\d+$/ or error "Illegal integer in '#{name}' argument: '#{arg}'"
|
92
|
+
arg.to_i
|
93
|
+
else # option.float?
|
94
|
+
# https://stackoverflow.com/a/21891705/2130986
|
95
|
+
arg =~ /^[+-]?(?:0|[1-9]\d*)(?:\.(?:\d*[1-9]|0))?$/ or
|
96
|
+
error "Illegal float in '#{name}' argument: '#{arg}'"
|
97
|
+
arg.to_f
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
module ShellOpts
|
3
|
+
# FIXME: An option group is -abcd, an option list is a,b,c,d
|
4
|
+
module Constants
|
5
|
+
# Short and long option names
|
6
|
+
SHORT_OPTION_NAME_SUB_RE = /[a-zA-Z0-9]/
|
7
|
+
LONG_OPTION_NAME_SUB_RE = /[a-z](?:[\w-]*\w)/
|
8
|
+
OPTION_NAME_SUB_RE = /#{SHORT_OPTION_NAME_SUB_RE}|#{LONG_OPTION_NAME_SUB_RE}/
|
9
|
+
|
10
|
+
# Initial option in a group
|
11
|
+
INITIAL_SHORT_OPTION_SUB_RE = /[-+]#{SHORT_OPTION_NAME_SUB_RE}/
|
12
|
+
INITIAL_LONG_OPTION_SUB_RE = /(?:--|\+\+)#{LONG_OPTION_NAME_SUB_RE}/
|
13
|
+
INITIAL_OPTION_SUB_RE = /#{INITIAL_SHORT_OPTION_SUB_RE}|#{INITIAL_LONG_OPTION_SUB_RE}/
|
14
|
+
|
15
|
+
# A list of short and long options
|
16
|
+
OPTION_GROUP_SUB_RE = /#{INITIAL_OPTION_SUB_RE}(?:,#{OPTION_NAME_SUB_RE})*/
|
17
|
+
|
18
|
+
# Option argument
|
19
|
+
OPTION_ARG_SUB_RE = /[A-Z](?:[A-Z0-9_-]*[A-Z0-9])?/
|
20
|
+
|
21
|
+
# Matches option flags and argument. It defines the following captures
|
22
|
+
#
|
23
|
+
# $1 - Argument flag ('=')
|
24
|
+
# $2 - Type flag ('#' or '$')
|
25
|
+
# $3 - Argument name
|
26
|
+
# $4 - Optional flag ('?')
|
27
|
+
#
|
28
|
+
OPTION_FLAGS_SUB_RE = /(=)(#|\$)?(#{OPTION_ARG_SUB_RE})?(\?)?/
|
29
|
+
|
30
|
+
# Matches a declaration of an option. The RE defines the following captures:
|
31
|
+
#
|
32
|
+
# $1 - Option group
|
33
|
+
# $2 - Argument flag ('=')
|
34
|
+
# $3 - Type flag ('#' or '$')
|
35
|
+
# $4 - Argument name
|
36
|
+
# $5 - Optional flag ('?')
|
37
|
+
#
|
38
|
+
OPTION_SUB_RE = /(#{OPTION_GROUP_SUB_RE})#{OPTION_FLAGS_SUB_RE}?/
|
39
|
+
|
40
|
+
# Command and command paths
|
41
|
+
COMMAND_IDENT_SUB_RE = /[a-z](?:[a-z0-9_-]*[a-z0-9])?/
|
42
|
+
COMMAND_SUB_RE = /#{COMMAND_IDENT_SUB_RE}!/
|
43
|
+
COMMAND_PATH_SUB_RE = /#{COMMAND_IDENT_SUB_RE}(?:\.#{COMMAND_IDENT_SUB_RE})*!/
|
44
|
+
|
45
|
+
# Command argument
|
46
|
+
ARGUMENT_SUB_RE = /[A-Z][A-Z0-9_-]*[A-Z0-9](?:\.\.\.)?/
|
47
|
+
ARGUMENT_EXPR_SUB_RE = /\[?#{ARGUMENT_SUB_RE}(?:#{ARGUMENT_SUB_RE}|[\[\]\|\s])*/
|
48
|
+
|
49
|
+
# Matches a line starting with a command or an option
|
50
|
+
SCAN_RE = /^(?:#{COMMAND_PATH_SUB_RE}|#{OPTION_SUB_RE})(?:\s+.*)?$/
|
51
|
+
|
52
|
+
|
53
|
+
# Create anchored REs for all SUB_REs
|
54
|
+
self.constants.each { |c|
|
55
|
+
next if c.to_s !~ /_SUB_RE$/
|
56
|
+
sub_re = self.const_get(c)
|
57
|
+
next if !sub_re.is_a?(Regexp)
|
58
|
+
re = /^#{sub_re}$/
|
59
|
+
name = c.to_s.sub(/_SUB_RE$/, "_RE")
|
60
|
+
self.const_set(name, re)
|
61
|
+
}
|
62
|
+
|
63
|
+
# Method names reserved by the BasicObject class
|
64
|
+
BASIC_OBJECT_RESERVED_WORDS = %w(
|
65
|
+
! != == __id__ __send__ equal? instance_eval instance_exec method_missing
|
66
|
+
singleton_method_added singleton_method_removed singleton_method_undefined)
|
67
|
+
|
68
|
+
# Method names reserved by the Ast::Command class
|
69
|
+
AST_COMMAND_RESERVED_WORDS = %w(
|
70
|
+
initialize options subcommand __is_program__ __get_grammar__
|
71
|
+
__add_option__ __add_command__)
|
72
|
+
|
73
|
+
# Reserved option names
|
74
|
+
OPTION_RESERVED_WORDS =
|
75
|
+
(BASIC_OBJECT_RESERVED_WORDS + AST_COMMAND_RESERVED_WORDS).grep(OPTION_NAME_RE)
|
76
|
+
|
77
|
+
# Reserved command names
|
78
|
+
COMMAND_RESERVED_WORDS = %w(subcommand)
|
79
|
+
end
|
80
|
+
|
81
|
+
include Constants
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
module ShellOpts
|
3
|
+
class CompileError < StandardError; end
|
4
|
+
|
5
|
+
class ShellOptsError < RuntimeError; end
|
6
|
+
|
7
|
+
class Error < ShellOptsError
|
8
|
+
attr_reader :subject
|
9
|
+
|
10
|
+
def initialize(subject = nil)
|
11
|
+
super()
|
12
|
+
@subject = subject
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Fail < ShellOptsError; end
|
17
|
+
end
|
18
|
+
|
19
|
+
class NotYet < NotImplementedError; end
|
20
|
+
class NotThis < ScriptError; end
|
21
|
+
class NotHere < ScriptError; end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
|
2
|
+
require 'ext/algorithm'
|
3
|
+
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
module ShellOpts
|
7
|
+
class Formatter
|
8
|
+
# Return string describing usage of command
|
9
|
+
def self.usage_string(command, levels: 1, margin: "")
|
10
|
+
elements(command, levels: levels, help: false).map { |line|
|
11
|
+
"#{margin}#{line}"
|
12
|
+
}.join("\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return string with help for the given command
|
16
|
+
def self.help_string(command, levels: 10, margin: "", tab: " ")
|
17
|
+
io = StringIO.new
|
18
|
+
elements(command, levels: levels, help: true).each { |head, texts, options|
|
19
|
+
io.puts "#{margin}#{head}"
|
20
|
+
texts.each { |text| io.puts "#{margin}#{tab}#{text}" }
|
21
|
+
options.each { |opt_head, opt_texts|
|
22
|
+
io.puts
|
23
|
+
io.puts "#{margin}#{tab}#{opt_head}"
|
24
|
+
opt_texts.each { |text| io.puts "#{margin}#{tab*2}#{text}" }
|
25
|
+
}
|
26
|
+
io.puts
|
27
|
+
}
|
28
|
+
io.string[0..-2]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def self.elements(command, levels: 1, help: false)
|
33
|
+
result = []
|
34
|
+
recursive_elements(result, command, levels: levels, help: help)
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.recursive_elements(acc, command, levels: 1, help: false)
|
39
|
+
cmds = (command.virtual? ? command.cmds : [command])
|
40
|
+
cmds.each { |cmd|
|
41
|
+
if levels == 1 || cmd.cmds.empty?
|
42
|
+
usage = (
|
43
|
+
path_elements(cmd) +
|
44
|
+
option_elements(cmd) +
|
45
|
+
subcommand_element(cmd) +
|
46
|
+
argument_elements(cmd)
|
47
|
+
).compact.join(" ")
|
48
|
+
if help
|
49
|
+
opts = []
|
50
|
+
cmd.opts.each { |opt|
|
51
|
+
next if opt.text.empty?
|
52
|
+
opts << [option_help(opt), opt.text]
|
53
|
+
}
|
54
|
+
acc << [usage, cmd.text, opts]
|
55
|
+
else
|
56
|
+
acc << usage
|
57
|
+
end
|
58
|
+
else
|
59
|
+
cmd.cmds.each { |subcmd|
|
60
|
+
recursive_elements(acc, subcmd, levels: levels - 1, help: help)
|
61
|
+
}
|
62
|
+
end
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return command line usage string
|
67
|
+
def self.command(cmd)
|
68
|
+
(path_elements(cmd) + option_elements(cmd) + argument_elements(cmd)).compact.join(" ")
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.path_elements(cmd)
|
72
|
+
Algorithm.follow(cmd, :parent).map { |parent| parent.name }.reverse
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.option_elements(cmd)
|
76
|
+
elements = []
|
77
|
+
collapsable_opts, other_opts = cmd.opts.partition { |opt| opt.shortname && !opt.argument? }
|
78
|
+
|
79
|
+
if !collapsable_opts.empty?
|
80
|
+
elements << "-" + collapsable_opts.map(&:shortname).join
|
81
|
+
end
|
82
|
+
|
83
|
+
elements + other_opts.map { |opt|
|
84
|
+
if opt.shortname
|
85
|
+
"-#{opt.shortname} #{opt.argument_name}" # We know opt has an argument
|
86
|
+
elsif opt.argument?
|
87
|
+
"--#{opt.longname}=#{opt.argument_name}"
|
88
|
+
else
|
89
|
+
"--#{opt.longname}"
|
90
|
+
end
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.option_help(opt)
|
95
|
+
result = opt.names.map { |name|
|
96
|
+
if name.size == 1
|
97
|
+
"-#{name}"
|
98
|
+
else
|
99
|
+
"--#{name}"
|
100
|
+
end
|
101
|
+
}.join(", ")
|
102
|
+
if opt.argument?
|
103
|
+
if opt.longname
|
104
|
+
result += "=#{opt.argument_name}"
|
105
|
+
else
|
106
|
+
result += " #{opt.argument_name}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
result
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.subcommand_element(cmd)
|
113
|
+
!cmd.cmds.empty? ? [cmd.cmds.map(&:name).join("|")] : []
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.argument_elements(cmd)
|
117
|
+
cmd.args
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.help_element(cmd)
|
121
|
+
text.map { |l| l.sub(/^\s*# /, "").rstrip }.join(" ")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|