choosy 0.1.0 → 0.2.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.
- data/README.markdown +229 -221
- data/Rakefile +21 -3
- data/examples/bar.rb +44 -0
- data/examples/foo.rb +198 -0
- data/examples/superfoo.rb +125 -0
- data/lib/VERSION +1 -1
- data/lib/choosy/argument.rb +51 -0
- data/lib/choosy/base_command.rb +22 -7
- data/lib/choosy/command.rb +12 -4
- data/lib/choosy/dsl/argument_builder.rb +88 -0
- data/lib/choosy/dsl/base_command_builder.rb +71 -56
- data/lib/choosy/dsl/command_builder.rb +14 -2
- data/lib/choosy/dsl/option_builder.rb +43 -83
- data/lib/choosy/dsl/super_command_builder.rb +37 -9
- data/lib/choosy/option.rb +13 -11
- data/lib/choosy/parse_result.rb +8 -27
- data/lib/choosy/parser.rb +20 -16
- data/lib/choosy/printing/color.rb +39 -21
- data/lib/choosy/printing/erb_printer.rb +12 -3
- data/lib/choosy/printing/formatting_element.rb +17 -0
- data/lib/choosy/printing/help_printer.rb +204 -117
- data/lib/choosy/printing/terminal.rb +53 -0
- data/lib/choosy/super_command.rb +6 -6
- data/lib/choosy/super_parser.rb +26 -15
- data/lib/choosy/verifier.rb +61 -6
- data/spec/choosy/base_command_spec.rb +27 -2
- data/spec/choosy/command_spec.rb +31 -9
- data/spec/choosy/dsl/argument_builder_spec.rb +180 -0
- data/spec/choosy/dsl/base_command_builder_spec.rb +87 -44
- data/spec/choosy/dsl/commmand_builder_spec.rb +15 -4
- data/spec/choosy/dsl/option_builder_spec.rb +101 -191
- data/spec/choosy/dsl/super_command_builder_spec.rb +34 -9
- data/spec/choosy/parser_spec.rb +30 -8
- data/spec/choosy/printing/color_spec.rb +19 -5
- data/spec/choosy/printing/help_printer_spec.rb +152 -73
- data/spec/choosy/printing/terminal_spec.rb +27 -0
- data/spec/choosy/super_command_spec.rb +17 -17
- data/spec/choosy/super_parser_spec.rb +20 -10
- data/spec/choosy/verifier_spec.rb +137 -47
- data/spec/integration/command-A_spec.rb +6 -6
- data/spec/integration/command-B_spec.rb +45 -0
- data/spec/integration/supercommand-A_spec.rb +33 -27
- data/spec/integration/supercommand-B_spec.rb +32 -0
- data/spec/spec_helpers.rb +8 -5
- metadata +95 -54
@@ -4,30 +4,58 @@ require 'choosy/command'
|
|
4
4
|
|
5
5
|
module Choosy::DSL
|
6
6
|
class SuperCommandBuilder < BaseCommandBuilder
|
7
|
-
|
7
|
+
HELP = :help
|
8
|
+
SUPER = :__SUPER_COMMAND__
|
9
|
+
|
10
|
+
def command(cmd, &block)
|
8
11
|
subcommand = if cmd.is_a?(Choosy::Command)
|
9
12
|
cmd
|
10
13
|
else
|
11
14
|
Choosy::Command.new(cmd)
|
12
15
|
end
|
13
|
-
|
16
|
+
|
17
|
+
if block_given?
|
18
|
+
subcommand.builder.instance_eval(&block)
|
19
|
+
end
|
14
20
|
finalize_subcommand(subcommand)
|
15
21
|
end
|
16
22
|
|
23
|
+
def parsimonious
|
24
|
+
@command.parsimonious = true
|
25
|
+
end
|
26
|
+
|
27
|
+
def metaname(meta)
|
28
|
+
@command.metaname = meta
|
29
|
+
end
|
30
|
+
|
17
31
|
def help(msg=nil)
|
18
32
|
msg ||= "Show the info for a command, or this message"
|
19
|
-
|
33
|
+
help_command = Choosy::Command.new HELP do |help|
|
20
34
|
help.summary msg
|
21
35
|
|
22
|
-
help.arguments do
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
36
|
+
help.arguments do
|
37
|
+
count 0..1
|
38
|
+
validate do |args, options|
|
39
|
+
if args.nil?
|
40
|
+
raise Choosy::HelpCalled.new(SUPER)
|
41
|
+
elsif args.is_a?(Array)
|
42
|
+
if args.length == 0
|
43
|
+
raise Choosy::HelpCalled.new(SUPER)
|
44
|
+
else
|
45
|
+
raise Choosy::HelpCalled.new(args[0].to_sym)
|
46
|
+
end
|
47
|
+
else
|
48
|
+
raise Choosy::HelpCalled.new(args.to_sym)
|
49
|
+
end
|
27
50
|
end
|
28
51
|
end
|
29
52
|
end
|
30
|
-
finalize_subcommand(
|
53
|
+
finalize_subcommand(help_command)
|
54
|
+
end
|
55
|
+
|
56
|
+
def finalize!
|
57
|
+
super
|
58
|
+
@command.metaname ||= 'COMMAND'
|
31
59
|
end
|
32
60
|
|
33
61
|
private
|
data/lib/choosy/option.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
|
+
require 'choosy/argument'
|
2
|
+
|
1
3
|
module Choosy
|
2
|
-
class Option
|
4
|
+
class Option < Argument
|
3
5
|
attr_accessor :name, :description
|
4
|
-
attr_accessor :short_flag, :long_flag
|
5
|
-
attr_accessor :
|
6
|
-
attr_accessor :validation_step
|
7
|
-
attr_accessor :arity
|
6
|
+
attr_accessor :short_flag, :long_flag
|
7
|
+
attr_accessor :default_value
|
8
8
|
attr_accessor :dependent_options
|
9
|
+
attr_accessor :negation
|
9
10
|
|
10
11
|
def initialize(name)
|
12
|
+
super()
|
11
13
|
@name = name
|
12
|
-
@required = false
|
13
14
|
end
|
14
|
-
|
15
|
-
def
|
16
|
-
|
15
|
+
|
16
|
+
def negated?
|
17
|
+
!negation.nil?
|
17
18
|
end
|
18
|
-
|
19
|
-
|
19
|
+
|
20
|
+
def negated
|
21
|
+
@negated ||= long_flag.gsub(/^--/, "--#{negation}-")
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
data/lib/choosy/parse_result.rb
CHANGED
@@ -4,11 +4,11 @@ module Choosy
|
|
4
4
|
class BaseParseResult
|
5
5
|
attr_reader :command, :options, :unparsed
|
6
6
|
|
7
|
-
def initialize(command)
|
7
|
+
def initialize(command, subresult)
|
8
8
|
@command = command
|
9
9
|
@options = {}
|
10
10
|
@unparsed = []
|
11
|
-
@
|
11
|
+
@subresult = subresult
|
12
12
|
end
|
13
13
|
|
14
14
|
def [](opt)
|
@@ -19,46 +19,27 @@ module Choosy
|
|
19
19
|
@options[opt] = val
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
@
|
24
|
-
end
|
25
|
-
|
26
|
-
def verify!
|
27
|
-
basic_verification
|
28
|
-
end
|
29
|
-
|
30
|
-
protected
|
31
|
-
def basic_verification(&block)
|
32
|
-
verifier = Verifier.new
|
33
|
-
verifier.verify_options!(self)
|
34
|
-
yield verifier if block_given?
|
35
|
-
@verified = true
|
36
|
-
self
|
22
|
+
def subresult?
|
23
|
+
@subresult
|
37
24
|
end
|
38
25
|
end
|
39
26
|
|
40
27
|
class ParseResult < BaseParseResult
|
41
28
|
attr_reader :args
|
42
29
|
|
43
|
-
def initialize(command)
|
44
|
-
super(command)
|
30
|
+
def initialize(command, subresult)
|
31
|
+
super(command, subresult)
|
45
32
|
@args = []
|
46
33
|
end
|
47
|
-
|
48
|
-
def verify!
|
49
|
-
return self if verified?
|
50
|
-
basic_verification do |verifier|
|
51
|
-
verifier.verify_arguments!(self)
|
52
|
-
end
|
53
|
-
end
|
54
34
|
end
|
55
35
|
|
56
36
|
class SuperParseResult < BaseParseResult
|
57
37
|
attr_reader :subresults
|
58
38
|
|
59
39
|
def initialize(command)
|
60
|
-
super(command)
|
40
|
+
super(command, false)
|
61
41
|
@subresults = []
|
62
42
|
end
|
43
|
+
|
63
44
|
end
|
64
45
|
end
|
data/lib/choosy/parser.rb
CHANGED
@@ -20,7 +20,7 @@ module Choosy
|
|
20
20
|
|
21
21
|
def parse!(argv, result=nil)
|
22
22
|
index = 0
|
23
|
-
result ||= ParseResult.new(@command)
|
23
|
+
result ||= ParseResult.new(@command, false)
|
24
24
|
|
25
25
|
while index < argv.length
|
26
26
|
case argv[index]
|
@@ -52,6 +52,9 @@ module Choosy
|
|
52
52
|
def verify_option(option)
|
53
53
|
verify_flag(option, option.short_flag)
|
54
54
|
verify_flag(option, option.long_flag)
|
55
|
+
if option.negated?
|
56
|
+
verify_flag(option, option.negated)
|
57
|
+
end
|
55
58
|
end
|
56
59
|
|
57
60
|
def verify_flag(option, flag)
|
@@ -76,19 +79,23 @@ module Choosy
|
|
76
79
|
end
|
77
80
|
end
|
78
81
|
|
79
|
-
if option.
|
80
|
-
parse_boolean_option(result, option, index, arg, current)
|
81
|
-
elsif option.
|
82
|
+
if option.boolean?
|
83
|
+
parse_boolean_option(result, option, index, arg, current, flag)
|
84
|
+
elsif option.single?
|
82
85
|
parse_single_option(result, option, index, argv, flag, arg)
|
83
86
|
else # Vararg
|
84
87
|
parse_multiple_option(result, option, index, argv, flag, arg)
|
85
88
|
end
|
86
89
|
end
|
87
90
|
|
88
|
-
def parse_boolean_option(result, option, index, arg, current)
|
89
|
-
|
91
|
+
def parse_boolean_option(result, option, index, arg, current, flag)
|
92
|
+
raise Choosy::ParseError.new("Argument given to boolean flag: '#{current}'") if arg
|
93
|
+
if option.negated? && flag == option.negated
|
94
|
+
result.options[option.name] = option.default_value
|
95
|
+
else
|
90
96
|
result.options[option.name] = !option.default_value
|
91
|
-
|
97
|
+
end
|
98
|
+
index + 1
|
92
99
|
end
|
93
100
|
|
94
101
|
def parse_single_option(result, option, index, argv, flag, arg)
|
@@ -160,7 +167,7 @@ module Choosy
|
|
160
167
|
return [nil, index] if index >= argv.length
|
161
168
|
|
162
169
|
current = argv[index]
|
163
|
-
return [nil, index] if current
|
170
|
+
return [nil, index] if current =~ /^-/
|
164
171
|
if @terminals.include? current
|
165
172
|
result.unparsed.push(*argv[index, argv.length])
|
166
173
|
return [nil, argv.length]
|
@@ -170,15 +177,12 @@ module Choosy
|
|
170
177
|
|
171
178
|
def parse_rest(argv, index, result)
|
172
179
|
index += 1
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
result.args << argv[index]
|
178
|
-
end
|
179
|
-
index += 1
|
180
|
+
if lazy?
|
181
|
+
result.unparsed.push(*argv[index, argv.length])
|
182
|
+
else
|
183
|
+
result.args.push(*argv[index, argv.length])
|
180
184
|
end
|
181
|
-
|
185
|
+
argv.length
|
182
186
|
end
|
183
187
|
end
|
184
188
|
end
|
@@ -18,10 +18,13 @@ module Choosy::Printing
|
|
18
18
|
EFFECTS = {
|
19
19
|
:reset => 0,
|
20
20
|
:bright => 1,
|
21
|
+
:bold => 1,
|
21
22
|
:underline => 4,
|
22
23
|
:blink => 5,
|
23
24
|
:exchange => 7,
|
24
|
-
:hide => 8
|
25
|
+
:hide => 8,
|
26
|
+
:primary => 10,
|
27
|
+
:normal => 22
|
25
28
|
}
|
26
29
|
|
27
30
|
FOREGROUND = 30
|
@@ -53,48 +56,63 @@ module Choosy::Printing
|
|
53
56
|
EFFECTS.has_key?(effect.to_sym)
|
54
57
|
end
|
55
58
|
|
59
|
+
def multiple(str, styles)
|
60
|
+
return str if styles.nil? || styles.empty? || disabled?
|
61
|
+
|
62
|
+
styles.each do |style|
|
63
|
+
if color?(style)
|
64
|
+
str = bedazzle(COLORS[style] + FOREGROUND, str)
|
65
|
+
elsif effect?(style)
|
66
|
+
str = bedazzle(EFFECTS[style], str)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
str
|
70
|
+
end
|
71
|
+
|
56
72
|
def respond_to?(method)
|
57
73
|
color?(method) || effect?(method)
|
58
74
|
end
|
59
75
|
|
60
76
|
# Dynamically handle colors and effects
|
61
|
-
def method_missing(method, *args, &block)
|
62
|
-
|
63
|
-
|
77
|
+
def method_missing(method, *args, &block)
|
78
|
+
if disabled?
|
79
|
+
return args[0] || ""
|
80
|
+
end
|
64
81
|
|
65
82
|
if color?(method)
|
66
|
-
|
83
|
+
raise ArgumentError.new("too many arguments to Color##{method} (max 2)") if args.length > 2
|
84
|
+
offset = find_state(method, args[1])
|
85
|
+
bedazzle(COLORS[method] + offset, args[0])
|
67
86
|
elsif effect?(method)
|
68
|
-
|
87
|
+
raise ArgumentError.new("too many arguments to Color##{method} (max 1)") if args.length > 1
|
88
|
+
bedazzle(EFFECTS[method], args[0])
|
69
89
|
else
|
70
90
|
raise NoMethodError.new("undefined method '#{method}' for Color")
|
71
91
|
end
|
72
92
|
end
|
73
93
|
|
74
94
|
private
|
75
|
-
def
|
76
|
-
case
|
77
|
-
when
|
78
|
-
|
79
|
-
when
|
80
|
-
|
81
|
-
when
|
82
|
-
|
83
|
-
when :foreground then [args[0], FOREGROUND]
|
84
|
-
when :background then [args[0], BACKGROUND]
|
85
|
-
else raise ArgumentError.new("unrecognized state for Color##{method}, :foreground or :background only")
|
86
|
-
end
|
95
|
+
def find_state(method, state)
|
96
|
+
case state
|
97
|
+
when nil
|
98
|
+
FOREGROUND
|
99
|
+
when :foreground
|
100
|
+
FOREGROUND
|
101
|
+
when :background
|
102
|
+
BACKGROUND
|
87
103
|
else
|
88
|
-
raise ArgumentError.new("
|
104
|
+
raise ArgumentError.new("unrecognized state for Color##{method}, :foreground or :background only")
|
89
105
|
end
|
90
106
|
end
|
91
107
|
|
92
108
|
def bedazzle(number, str)
|
93
|
-
prefix = "e#{number}
|
109
|
+
prefix = "\e[#{number}m"
|
94
110
|
if str.nil?
|
95
111
|
prefix
|
112
|
+
elsif str =~ /\e\[0m$/
|
113
|
+
"#{prefix}#{str}"
|
96
114
|
else
|
97
|
-
"#{prefix}#{str}
|
115
|
+
"#{prefix}#{str}\e[0m"
|
98
116
|
end
|
99
117
|
end
|
100
118
|
end
|
@@ -4,8 +4,17 @@ require 'erb'
|
|
4
4
|
|
5
5
|
module Choosy::Printing
|
6
6
|
class ERBPrinter < HelpPrinter
|
7
|
-
attr_reader :command
|
8
|
-
|
7
|
+
attr_reader :command, :template
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
super(options)
|
11
|
+
if options[:template].nil?
|
12
|
+
raise Choosy::ConfigurationError.new("no template file given to ERBPrinter")
|
13
|
+
elsif !File.exist?(options[:template])
|
14
|
+
raise Choosy::ConfigurationError.new("the template file doesn't exist: #{options[:template]}")
|
15
|
+
end
|
16
|
+
@template = options[:template]
|
17
|
+
end
|
9
18
|
|
10
19
|
def print!(command)
|
11
20
|
@command = command
|
@@ -13,7 +22,7 @@ module Choosy::Printing
|
|
13
22
|
File.open(template, 'r') {|f| contents = f.read }
|
14
23
|
erb = ERB.new contents
|
15
24
|
|
16
|
-
erb.
|
25
|
+
erb.result(self)
|
17
26
|
end
|
18
27
|
|
19
28
|
def erb_binding
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'choosy/errors'
|
2
|
+
|
3
|
+
module Choosy::Printing
|
4
|
+
class FormattingElement
|
5
|
+
attr_reader :value, :styles, :kind
|
6
|
+
|
7
|
+
def initialize(kind, value, styles)
|
8
|
+
@value = value
|
9
|
+
@kind = kind
|
10
|
+
@styles = styles
|
11
|
+
end
|
12
|
+
|
13
|
+
def header?
|
14
|
+
@kind == :header
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,174 +1,261 @@
|
|
1
1
|
require 'choosy/errors'
|
2
|
-
require 'choosy/printing/
|
2
|
+
require 'choosy/printing/terminal'
|
3
3
|
|
4
4
|
module Choosy::Printing
|
5
5
|
class HelpPrinter
|
6
|
-
|
7
|
-
DEFAULT_COLUMN_COUNT = 80
|
6
|
+
include Terminal
|
8
7
|
|
9
|
-
attr_reader :
|
8
|
+
attr_reader :header_styles, :indent, :offset, :buffer, :usage
|
10
9
|
|
11
|
-
def initialize
|
12
|
-
@
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@
|
17
|
-
end
|
10
|
+
def initialize(options)
|
11
|
+
@indent = options[:indent] || ' '
|
12
|
+
@offset = options[:offset] || ' '
|
13
|
+
@header_styles = options[:header_styles] || [:bold, :blue]
|
14
|
+
@buffer = options[:buffer] || ""
|
15
|
+
@usage = options[:usage] || 'Usage:'
|
18
16
|
|
19
|
-
|
20
|
-
|
17
|
+
if options[:color] == false
|
18
|
+
color.disable!
|
19
|
+
end
|
20
|
+
if options[:max_width]
|
21
|
+
self.columns = options[:max_width]
|
22
|
+
end
|
21
23
|
end
|
22
24
|
|
23
|
-
def
|
24
|
-
|
25
|
-
end
|
25
|
+
def print!(command)
|
26
|
+
print_usage(command)
|
26
27
|
|
27
|
-
|
28
|
-
@columns = value
|
29
|
-
end
|
28
|
+
cmd_indent, option_indent, prefixes = retrieve_formatting_info(command)
|
30
29
|
|
31
|
-
|
32
|
-
|
30
|
+
command.listing.each_with_index do |item, i|
|
31
|
+
case item
|
32
|
+
when Choosy::Option
|
33
|
+
print_option(item, prefixes[i], option_indent)
|
34
|
+
when Choosy::Command
|
35
|
+
print_command(item, prefixes[i], cmd_indent)
|
36
|
+
when Choosy::Printing::FormattingElement
|
37
|
+
print_element(item)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@buffer
|
33
42
|
end
|
34
43
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
command.
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
def print_usage(command)
|
45
|
+
print_header(@usage)
|
46
|
+
@buffer << ' '
|
47
|
+
@buffer << command.name.to_s
|
48
|
+
return if command.options.empty?
|
49
|
+
|
50
|
+
width = starting_width = 8 + command.name.to_s.length # So far
|
51
|
+
command.listing.each do |option|
|
52
|
+
if option.is_a?(Choosy::Option)
|
53
|
+
formatted = usage_option(option)
|
54
|
+
width += formatted.length
|
55
|
+
if width > columns
|
56
|
+
@buffer << "\n"
|
57
|
+
@buffer << ' ' * starting_width
|
58
|
+
@buffer << formatted
|
59
|
+
width = starting_width + formatted.length
|
60
|
+
else
|
61
|
+
@buffer << ' '
|
62
|
+
@buffer << formatted
|
63
|
+
width += 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
case command
|
69
|
+
when Choosy::Command
|
70
|
+
if command.arguments
|
71
|
+
@buffer << ' '
|
72
|
+
@buffer << command.arguments.metaname
|
46
73
|
end
|
74
|
+
when Choosy::SuperCommand
|
75
|
+
@buffer << ' '
|
76
|
+
@buffer << command.metaname
|
47
77
|
end
|
78
|
+
|
79
|
+
@buffer << "\n"
|
48
80
|
end
|
49
81
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
cmds = if command.respond_to?(:commands)
|
58
|
-
" [COMMANDS]"
|
59
|
-
else
|
60
|
-
""
|
61
|
-
end
|
62
|
-
options = if command.option_builders.length == 0
|
63
|
-
""
|
64
|
-
else
|
65
|
-
" [OPTIONS]"
|
66
|
-
end
|
67
|
-
$stdout << "USAGE: #{command.name}#{cmds}#{options}#{args}\n"
|
82
|
+
def print_header(str, styles=nil)
|
83
|
+
return if str.nil?
|
84
|
+
if styles && !styles.empty?
|
85
|
+
@buffer << color.multiple(str, styles)
|
86
|
+
else
|
87
|
+
@buffer << color.multiple(str, header_styles)
|
88
|
+
end
|
68
89
|
end
|
69
90
|
|
70
|
-
def
|
71
|
-
|
91
|
+
def print_element(element)
|
92
|
+
if element.header?
|
93
|
+
@buffer << "\n"
|
94
|
+
print_header(element.value, element.styles)
|
95
|
+
@buffer << "\n"
|
96
|
+
else
|
97
|
+
@buffer << "\n"
|
98
|
+
write_lines(element.value, indent, true)
|
99
|
+
end
|
72
100
|
end
|
73
101
|
|
74
|
-
def
|
75
|
-
|
76
|
-
write_lines(
|
102
|
+
def print_option(option, formatted_prefix, opt_indent)
|
103
|
+
write_prefix(formatted_prefix, opt_indent)
|
104
|
+
write_lines(option.description, opt_indent, false)
|
77
105
|
end
|
78
106
|
|
79
|
-
def
|
80
|
-
|
107
|
+
def print_command(command, formatted_prefix, cmd_indent)
|
108
|
+
write_prefix(formatted_prefix, cmd_indent)
|
109
|
+
write_lines(command.summary, cmd_indent, false)
|
81
110
|
end
|
82
111
|
|
83
|
-
def
|
84
|
-
|
112
|
+
def usage_option(option, value=nil)
|
113
|
+
value ||= ""
|
114
|
+
value << "["
|
85
115
|
if option.short_flag
|
86
|
-
|
116
|
+
value << option.short_flag
|
87
117
|
if option.long_flag
|
88
|
-
|
118
|
+
value << "|"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
if option.long_flag
|
122
|
+
value << option.long_flag
|
123
|
+
end
|
124
|
+
if option.negated?
|
125
|
+
value << '|'
|
126
|
+
value << option.negated
|
127
|
+
end
|
128
|
+
if option.metaname
|
129
|
+
if option.arity.max > 1
|
130
|
+
value << ' '
|
131
|
+
value << option.metaname
|
132
|
+
else
|
133
|
+
value << '='
|
134
|
+
value << option.metaname
|
89
135
|
end
|
90
136
|
end
|
137
|
+
value << ']'
|
138
|
+
end
|
139
|
+
|
140
|
+
def regular_option(option, value=nil)
|
141
|
+
value ||= ""
|
142
|
+
if option.short_flag
|
143
|
+
value << option.short_flag
|
144
|
+
if option.long_flag
|
145
|
+
value << ', '
|
146
|
+
end
|
147
|
+
else
|
148
|
+
value << ' '
|
149
|
+
end
|
91
150
|
|
92
151
|
if option.long_flag
|
93
|
-
|
152
|
+
if option.negated?
|
153
|
+
value << '--['
|
154
|
+
value << option.negation
|
155
|
+
value << '-]'
|
156
|
+
value << option.long_flag.gsub(/^--/, '')
|
157
|
+
else
|
158
|
+
value << option.long_flag
|
159
|
+
end
|
94
160
|
end
|
95
161
|
|
96
|
-
if option.
|
97
|
-
|
98
|
-
|
162
|
+
if option.metaname
|
163
|
+
value << ' '
|
164
|
+
value << option.metaname
|
99
165
|
end
|
166
|
+
value
|
167
|
+
end
|
168
|
+
|
169
|
+
protected
|
170
|
+
def retrieve_formatting_info(command)
|
171
|
+
cmdlen = 0
|
172
|
+
optionlen = 0
|
173
|
+
prefixes = []
|
100
174
|
|
101
|
-
|
102
|
-
|
175
|
+
command.listing.each do |item|
|
176
|
+
case item
|
177
|
+
when Choosy::Option
|
178
|
+
opt = regular_option(item)
|
179
|
+
if opt.length > optionlen
|
180
|
+
optionlen = opt.length
|
181
|
+
end
|
182
|
+
prefixes << opt
|
183
|
+
when Choosy::Command
|
184
|
+
name = item.name.to_s
|
185
|
+
if name.length > cmdlen
|
186
|
+
cmdlen = name.length
|
187
|
+
end
|
188
|
+
prefixes << name
|
189
|
+
else
|
190
|
+
prefixes << nil
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
option_indent = ' ' * (optionlen + indent.length + offset.length)
|
195
|
+
cmd_indent = ' ' * (cmdlen + indent.length + offset.length)
|
196
|
+
[cmd_indent, option_indent, prefixes]
|
103
197
|
end
|
104
198
|
|
105
|
-
def
|
106
|
-
|
199
|
+
def write_prefix(prefix, after_indent)
|
200
|
+
len = after_indent.length - prefix.length - indent.length
|
201
|
+
@buffer << indent
|
202
|
+
@buffer << prefix
|
203
|
+
@buffer << ' ' * len
|
107
204
|
end
|
108
205
|
|
109
|
-
|
110
|
-
def write_lines(str, prefix)
|
206
|
+
def write_lines(str, prefix, indent_first)
|
111
207
|
str.split("\n").each do |line|
|
112
208
|
if line.length == 0
|
113
|
-
|
209
|
+
@buffer << "\n"
|
114
210
|
else
|
115
|
-
|
211
|
+
index = 0
|
212
|
+
|
213
|
+
while index < line.length
|
214
|
+
index = write_line(line, prefix, index, indent_first)
|
215
|
+
indent_first = true
|
216
|
+
end
|
116
217
|
end
|
117
218
|
end
|
118
219
|
end
|
119
220
|
|
120
|
-
#
|
221
|
+
# Line:
|
222
|
+
# index char
|
223
|
+
# ----|---------------|--------------|--------------------|
|
224
|
+
# length? length?
|
225
|
+
#
|
226
|
+
# Printing:
|
227
|
+
# offset columns
|
228
|
+
# --|-------------------------------------------------|
|
229
|
+
#
|
121
230
|
MAX_BACKTRACK = 25
|
122
|
-
def
|
123
|
-
|
124
|
-
|
125
|
-
segment_size = line.length - index
|
126
|
-
if segment_size >= columns
|
127
|
-
i = columns + index - prefix.length
|
128
|
-
while i > columns - MAX_BACKTRACK
|
129
|
-
if line[i] == ' '
|
130
|
-
indent_line(line[index, i - index], prefix)
|
131
|
-
index = i + 1
|
132
|
-
break
|
133
|
-
else
|
134
|
-
i -= 1
|
135
|
-
end
|
136
|
-
end
|
137
|
-
else
|
138
|
-
indent_line(line[index, line.length], prefix)
|
139
|
-
index += segment_size
|
140
|
-
end
|
231
|
+
def write_line(line, prefix, index, indent_first)
|
232
|
+
if indent_first
|
233
|
+
@buffer << prefix
|
141
234
|
end
|
142
|
-
end
|
143
235
|
|
144
|
-
|
145
|
-
|
146
|
-
$stdout << line
|
147
|
-
$stdout << "\n"
|
148
|
-
end
|
236
|
+
max_line_length = columns - prefix.length # How much can we print?
|
237
|
+
char = index + max_line_length # Where are we within the line, looking at the max length
|
149
238
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
if
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
elsif STDIN.tty? && command_exists?('stty')
|
160
|
-
`stty size`.scan(/\d+/).map { |s| s.to_i }[stty_index]
|
161
|
-
else
|
162
|
-
nil
|
239
|
+
if char > line.length # There's not a lot of line left to print
|
240
|
+
return write_rest(line, index)
|
241
|
+
end
|
242
|
+
|
243
|
+
char.downto(char - MAX_BACKTRACK) do |i| # Only go back a fixed line segment
|
244
|
+
if line[i, 1] == ' '
|
245
|
+
@buffer << line[index, i - index]
|
246
|
+
@buffer << "\n"
|
247
|
+
return i + 1
|
163
248
|
end
|
164
|
-
rescue
|
165
|
-
nil
|
166
249
|
end
|
250
|
+
|
251
|
+
# We didn't succeed in writing the line. so just bail and write the rest.
|
252
|
+
write_rest(line, index)
|
167
253
|
end
|
168
254
|
|
169
|
-
|
170
|
-
|
171
|
-
|
255
|
+
def write_rest(line, index)
|
256
|
+
@buffer << line[index, line.length]
|
257
|
+
@buffer << "\n"
|
258
|
+
line.length
|
172
259
|
end
|
173
260
|
end
|
174
261
|
end
|