choosy 0.2.5 → 0.3.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/CHANGELOG.md +8 -0
- data/LICENSE +2 -0
- data/README.markdown +102 -54
- data/Rakefile +27 -5
- data/TODO.md +5 -0
- data/examples/bar.rb +9 -7
- data/examples/foo.rb +9 -7
- data/examples/superfoo.rb +48 -36
- data/lib/VERSION.yml +6 -0
- data/lib/choosy/argument.rb +5 -0
- data/lib/choosy/base_command.rb +11 -10
- data/lib/choosy/command.rb +17 -1
- data/lib/choosy/converter.rb +5 -1
- data/lib/choosy/dsl/argument_builder.rb +33 -21
- data/lib/choosy/dsl/base_builder.rb +26 -0
- data/lib/choosy/dsl/base_command_builder.rb +23 -30
- data/lib/choosy/dsl/command_builder.rb +8 -12
- data/lib/choosy/dsl/option_builder.rb +14 -45
- data/lib/choosy/dsl/super_command_builder.rb +17 -20
- data/lib/choosy/errors.rb +1 -0
- data/lib/choosy/option.rb +19 -0
- data/lib/choosy/printing/base_printer.rb +231 -0
- data/lib/choosy/printing/color.rb +8 -5
- data/lib/choosy/printing/erb_printer.rb +1 -1
- data/lib/choosy/printing/help_printer.rb +49 -163
- data/lib/choosy/printing/manpage.rb +235 -0
- data/lib/choosy/printing/manpage_printer.rb +95 -0
- data/lib/choosy/printing/terminal.rb +39 -8
- data/lib/choosy/printing.rb +1 -0
- data/lib/choosy/super_command.rb +13 -4
- data/lib/choosy/super_parser.rb +5 -1
- data/lib/choosy/verifier.rb +8 -0
- data/lib/choosy/version.rb +64 -5
- data/spec/choosy/argument_spec.rb +28 -0
- data/spec/choosy/base_command_spec.rb +7 -3
- data/spec/choosy/command_spec.rb +2 -2
- data/spec/choosy/converter_spec.rb +3 -2
- data/spec/choosy/dsl/argument_builder_spec.rb +19 -8
- data/spec/choosy/dsl/base_builder_spec.rb +43 -0
- data/spec/choosy/dsl/base_command_builder_spec.rb +7 -9
- data/spec/choosy/dsl/commmand_builder_spec.rb +9 -1
- data/spec/choosy/dsl/option_builder_spec.rb +1 -65
- data/spec/choosy/dsl/super_command_builder_spec.rb +19 -8
- data/spec/choosy/option_spec.rb +68 -0
- data/spec/choosy/printing/base_printer_spec.rb +155 -0
- data/spec/choosy/printing/color_spec.rb +4 -0
- data/spec/choosy/printing/help_printer_spec.rb +15 -109
- data/spec/choosy/printing/manpage_printer_spec.rb +95 -0
- data/spec/choosy/printing/manpage_spec.rb +206 -0
- data/spec/choosy/super_command_spec.rb +7 -0
- data/spec/choosy/super_parser_spec.rb +9 -0
- data/spec/choosy/verifier_spec.rb +31 -1
- data/spec/choosy/version.yml +5 -0
- data/spec/choosy/version_spec.rb +87 -0
- data/spec/integration/command-C_spec.rb +23 -0
- data/spec/integration/supercommand-C_spec.rb +45 -0
- metadata +81 -78
- data/lib/VERSION +0 -1
@@ -0,0 +1,231 @@
|
|
1
|
+
require 'choosy/errors'
|
2
|
+
require 'choosy/printing/terminal'
|
3
|
+
|
4
|
+
module Choosy::Printing
|
5
|
+
class BasePrinter
|
6
|
+
include Terminal
|
7
|
+
|
8
|
+
attr_accessor :indent, :offset, :formatting_options, :header_styles, :option_styles
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
@formatting_options = options
|
12
|
+
|
13
|
+
@header_styles = options[:header_styles] || [:bold, :blue]
|
14
|
+
@option_styles = options[:option_styles] || [:bold]
|
15
|
+
@indent = options[:indent] || ' '
|
16
|
+
@offset = options[:offset] || ' '
|
17
|
+
|
18
|
+
if options[:color] == false
|
19
|
+
self.color.disable!
|
20
|
+
end
|
21
|
+
if options[:max_width] && self.columns > options[:max_width]
|
22
|
+
self.columns = options[:max_width]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def print!(command)
|
27
|
+
page format!(command)
|
28
|
+
end
|
29
|
+
|
30
|
+
def format!(command)
|
31
|
+
format_prologue(command)
|
32
|
+
|
33
|
+
cmd_indent, option_indent, prefixes = retrieve_formatting_info(command)
|
34
|
+
command.listing.each_with_index do |item, i|
|
35
|
+
case item
|
36
|
+
when Choosy::Option
|
37
|
+
format_option(item, prefixes[i], option_indent)
|
38
|
+
when Choosy::Command
|
39
|
+
format_command(item, prefixes[i], cmd_indent)
|
40
|
+
when Choosy::Printing::FormattingElement
|
41
|
+
format_element(item)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
format_epilogue(command)
|
46
|
+
end
|
47
|
+
|
48
|
+
def line_count
|
49
|
+
# Override
|
50
|
+
end
|
51
|
+
|
52
|
+
def format_prologue(command)
|
53
|
+
# Override
|
54
|
+
end
|
55
|
+
|
56
|
+
def format_option(option, formatted_prefix, indent)
|
57
|
+
# Override
|
58
|
+
end
|
59
|
+
|
60
|
+
def format_command(command, formatted_prefix, indent)
|
61
|
+
# Override
|
62
|
+
end
|
63
|
+
|
64
|
+
def format_element(item)
|
65
|
+
# Override
|
66
|
+
end
|
67
|
+
|
68
|
+
def format_epilogue(command)
|
69
|
+
# Override
|
70
|
+
end
|
71
|
+
|
72
|
+
def usage_option(option, value="")
|
73
|
+
value << "["
|
74
|
+
if option.short_flag
|
75
|
+
value << option.short_flag
|
76
|
+
if option.long_flag
|
77
|
+
value << "|"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
if option.long_flag
|
81
|
+
value << option.long_flag
|
82
|
+
end
|
83
|
+
if option.negated?
|
84
|
+
value << '|'
|
85
|
+
value << option.negated
|
86
|
+
end
|
87
|
+
if option.metaname
|
88
|
+
if option.arity.max > 1
|
89
|
+
value << ' '
|
90
|
+
value << option.metaname
|
91
|
+
else
|
92
|
+
value << '='
|
93
|
+
value << option.metaname
|
94
|
+
end
|
95
|
+
end
|
96
|
+
value << ']'
|
97
|
+
end
|
98
|
+
|
99
|
+
def regular_option(option, value="")
|
100
|
+
if option.short_flag
|
101
|
+
value << option_begin
|
102
|
+
value << option.short_flag
|
103
|
+
value << option_end
|
104
|
+
if option.long_flag
|
105
|
+
value << ', '
|
106
|
+
end
|
107
|
+
else
|
108
|
+
value << ' '
|
109
|
+
end
|
110
|
+
|
111
|
+
if option.long_flag
|
112
|
+
value << option_begin
|
113
|
+
if option.negated?
|
114
|
+
value << '--['
|
115
|
+
value << option.negation
|
116
|
+
value << '-]'
|
117
|
+
value << option.long_flag.gsub(/^--/, '')
|
118
|
+
else
|
119
|
+
value << option.long_flag
|
120
|
+
end
|
121
|
+
value << option_end
|
122
|
+
end
|
123
|
+
|
124
|
+
if option.metaname
|
125
|
+
value << ' '
|
126
|
+
value << option.metaname
|
127
|
+
end
|
128
|
+
value
|
129
|
+
end
|
130
|
+
|
131
|
+
def command_name(command)
|
132
|
+
if command.is_a?(Choosy::Command) && command.subcommand?
|
133
|
+
"#{command.parent.name} #{command.name}"
|
134
|
+
else
|
135
|
+
"#{command.name}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# doesn't indent the first line
|
140
|
+
def usage_wrapped(command, indent='', columns=80)
|
141
|
+
lines = []
|
142
|
+
line = command_name(command)
|
143
|
+
starting_width = width = line.length + indent.length
|
144
|
+
lines << line
|
145
|
+
|
146
|
+
wrap = lambda do |part|
|
147
|
+
if width + part.length > columns
|
148
|
+
line = ' ' * starting_width
|
149
|
+
lines << line
|
150
|
+
line << ' ' << part
|
151
|
+
width = starting_width + part.length
|
152
|
+
else
|
153
|
+
line << ' ' << part
|
154
|
+
width += part.length
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
command.listing.each do |option|
|
159
|
+
if option.is_a?(Choosy::Option)
|
160
|
+
formatted = usage_option(option)
|
161
|
+
wrap.call(formatted)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
case command
|
166
|
+
when Choosy::Command
|
167
|
+
if command.arguments
|
168
|
+
wrap.call(command.arguments.metaname)
|
169
|
+
end
|
170
|
+
when Choosy::SuperCommand
|
171
|
+
wrap.call(command.metaname)
|
172
|
+
end
|
173
|
+
|
174
|
+
lines
|
175
|
+
end
|
176
|
+
|
177
|
+
protected
|
178
|
+
def fix_termcap
|
179
|
+
if pager.include?('less')
|
180
|
+
headers = color.multiple(nil, @header_styles)
|
181
|
+
options = color.multiple(nil, @option_styles)
|
182
|
+
|
183
|
+
ENV['LESS_TERMCAP_mb'] ||= "\e[31m" # Begin blinking
|
184
|
+
ENV['LESS_TERMCAP_md'] ||= headers # Begin bold
|
185
|
+
ENV['LESS_TERMCAP_me'] ||= "\e[0m" # End mode
|
186
|
+
ENV['LESS_TERMCAP_se'] ||= "\e[0m" # End STDOUT-mode
|
187
|
+
ENV['LESS_TERMCAP_so'] ||= headers # Begin STDOUT-mode
|
188
|
+
ENV['LESS_TERMCAP_ue'] ||= "\e[0m" # End Underline
|
189
|
+
ENV['LESS_TERMCAP_us'] ||= options # Begin Underline
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def option_begin
|
194
|
+
''
|
195
|
+
end
|
196
|
+
|
197
|
+
def option_end
|
198
|
+
''
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
def retrieve_formatting_info(command)
|
203
|
+
cmdlen = 0
|
204
|
+
optionlen = 0
|
205
|
+
prefixes = []
|
206
|
+
|
207
|
+
command.listing.each do |item|
|
208
|
+
case item
|
209
|
+
when Choosy::Option
|
210
|
+
opt = regular_option(item)
|
211
|
+
if opt.length > optionlen
|
212
|
+
optionlen = opt.length
|
213
|
+
end
|
214
|
+
prefixes << opt
|
215
|
+
when Choosy::Command
|
216
|
+
name = item.name.to_s
|
217
|
+
if name.length > cmdlen
|
218
|
+
cmdlen = name.length
|
219
|
+
end
|
220
|
+
prefixes << name
|
221
|
+
else
|
222
|
+
prefixes << nil
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
option_indent = ' ' * (optionlen + indent.length + offset.length)
|
227
|
+
cmd_indent = ' ' * (cmdlen + indent.length + offset.length)
|
228
|
+
[cmd_indent, option_indent, prefixes]
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -48,8 +48,8 @@ module Choosy::Printing
|
|
48
48
|
@disabled = true
|
49
49
|
end
|
50
50
|
|
51
|
-
def color?(
|
52
|
-
COLORS.has_key?(
|
51
|
+
def color?(col)
|
52
|
+
COLORS.has_key?(col.to_sym)
|
53
53
|
end
|
54
54
|
|
55
55
|
def effect?(effect)
|
@@ -59,11 +59,12 @@ module Choosy::Printing
|
|
59
59
|
def multiple(str, styles)
|
60
60
|
return str if styles.nil? || styles.empty? || disabled?
|
61
61
|
|
62
|
+
originally_nil = str.nil?
|
62
63
|
styles.each do |style|
|
63
64
|
if color?(style)
|
64
|
-
str = bedazzle(COLORS[style] + FOREGROUND, str)
|
65
|
+
str = bedazzle(COLORS[style] + FOREGROUND, str, originally_nil)
|
65
66
|
elsif effect?(style)
|
66
|
-
str = bedazzle(EFFECTS[style], str)
|
67
|
+
str = bedazzle(EFFECTS[style], str, originally_nil)
|
67
68
|
end
|
68
69
|
end
|
69
70
|
str
|
@@ -105,12 +106,14 @@ module Choosy::Printing
|
|
105
106
|
end
|
106
107
|
end
|
107
108
|
|
108
|
-
def bedazzle(number, str)
|
109
|
+
def bedazzle(number, str, keep_open=nil)
|
109
110
|
prefix = "\e[#{number}m"
|
110
111
|
if str.nil?
|
111
112
|
prefix
|
112
113
|
elsif str =~ /\e\[0m$/
|
113
114
|
"#{prefix}#{str}"
|
115
|
+
elsif keep_open
|
116
|
+
"#{prefix}#{str}"
|
114
117
|
else
|
115
118
|
"#{prefix}#{str}\e[0m"
|
116
119
|
end
|
@@ -10,7 +10,7 @@ module Choosy::Printing
|
|
10
10
|
super(options)
|
11
11
|
if options[:template].nil?
|
12
12
|
raise Choosy::ConfigurationError.new("no template file given to ERBPrinter")
|
13
|
-
elsif !File.
|
13
|
+
elsif !File.file?(options[:template])
|
14
14
|
raise Choosy::ConfigurationError.new("the template file doesn't exist: #{options[:template]}")
|
15
15
|
end
|
16
16
|
@template = options[:template]
|
@@ -1,199 +1,90 @@
|
|
1
1
|
require 'choosy/errors'
|
2
2
|
require 'choosy/printing/terminal'
|
3
|
+
require 'choosy/printing/base_printer'
|
3
4
|
|
4
5
|
module Choosy::Printing
|
5
|
-
class HelpPrinter
|
6
|
-
|
7
|
-
|
8
|
-
attr_reader :header_styles, :indent, :offset, :buffer, :usage
|
6
|
+
class HelpPrinter < BasePrinter
|
7
|
+
attr_reader :buffer, :usage, :line_count
|
9
8
|
|
10
9
|
def initialize(options)
|
11
|
-
|
12
|
-
|
13
|
-
@header_styles = options[:header_styles] || [:bold, :blue]
|
10
|
+
super(options)
|
11
|
+
|
14
12
|
@buffer = options[:buffer] || ""
|
15
13
|
@usage = options[:usage] || 'Usage:'
|
16
14
|
|
17
|
-
|
18
|
-
color.disable!
|
19
|
-
end
|
20
|
-
if options[:max_width]
|
21
|
-
self.columns = options[:max_width]
|
22
|
-
end
|
15
|
+
@line_count = 0
|
23
16
|
end
|
24
17
|
|
25
18
|
def print!(command)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
19
|
+
format!(command)
|
20
|
+
if @line_count > lines
|
21
|
+
page(@buffer)
|
22
|
+
else
|
23
|
+
puts @buffer
|
39
24
|
end
|
40
|
-
|
41
|
-
@buffer
|
42
25
|
end
|
43
26
|
|
44
|
-
def
|
45
|
-
|
27
|
+
def format_prologue(command)
|
28
|
+
format_header(@usage)
|
46
29
|
@buffer << ' '
|
47
|
-
@
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
73
|
-
end
|
74
|
-
when Choosy::SuperCommand
|
75
|
-
@buffer << ' '
|
76
|
-
@buffer << command.metaname
|
30
|
+
indent = ' ' * (@usage.length + 1)
|
31
|
+
usage_wrapped(command, indent, columns).each do |line|
|
32
|
+
@buffer << line
|
33
|
+
nl
|
77
34
|
end
|
78
|
-
|
79
|
-
@buffer << "\n"
|
35
|
+
nl
|
80
36
|
end
|
81
37
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
@buffer << color.multiple(str, styles)
|
86
|
-
else
|
87
|
-
@buffer << color.multiple(str, header_styles)
|
88
|
-
end
|
38
|
+
def format_epilogue(command)
|
39
|
+
nl
|
40
|
+
@buffer
|
89
41
|
end
|
90
42
|
|
91
|
-
def
|
92
|
-
if element.
|
93
|
-
|
94
|
-
|
95
|
-
@buffer
|
43
|
+
def format_element(element)
|
44
|
+
if element.value.nil?
|
45
|
+
nl
|
46
|
+
elsif element.header?
|
47
|
+
nl if @buffer[-2,1] != "\n"
|
48
|
+
format_header(element.value, element.styles)
|
49
|
+
nl
|
50
|
+
nl
|
96
51
|
else
|
97
|
-
@buffer << "\n"
|
98
52
|
write_lines(element.value, indent, true)
|
53
|
+
nl
|
99
54
|
end
|
100
55
|
end
|
101
56
|
|
102
|
-
def
|
57
|
+
def format_option(option, formatted_prefix, opt_indent)
|
103
58
|
write_prefix(formatted_prefix, opt_indent)
|
104
59
|
write_lines(option.description, opt_indent, false)
|
105
60
|
end
|
106
61
|
|
107
|
-
def
|
62
|
+
def format_command(command, formatted_prefix, cmd_indent)
|
108
63
|
write_prefix(formatted_prefix, cmd_indent)
|
109
64
|
write_lines(command.summary, cmd_indent, false)
|
110
65
|
end
|
111
66
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
value << option.short_flag
|
117
|
-
if option.long_flag
|
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
|
135
|
-
end
|
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
|
67
|
+
def format_header(str, styles=nil)
|
68
|
+
return if str.nil?
|
69
|
+
if styles && !styles.empty?
|
70
|
+
@buffer << color.multiple(str, styles)
|
147
71
|
else
|
148
|
-
|
149
|
-
end
|
150
|
-
|
151
|
-
if option.long_flag
|
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
|
160
|
-
end
|
161
|
-
|
162
|
-
if option.metaname
|
163
|
-
value << ' '
|
164
|
-
value << option.metaname
|
72
|
+
@buffer << color.multiple(str, header_styles)
|
165
73
|
end
|
166
|
-
value
|
167
74
|
end
|
168
75
|
|
169
76
|
protected
|
170
|
-
def
|
171
|
-
|
172
|
-
|
173
|
-
prefixes = []
|
77
|
+
def option_begin
|
78
|
+
@option_begin ||= color.multiple(nil, option_styles)
|
79
|
+
end
|
174
80
|
|
175
|
-
|
176
|
-
|
177
|
-
|
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
|
81
|
+
def option_end
|
82
|
+
color.reset
|
83
|
+
end
|
193
84
|
|
194
|
-
|
195
|
-
|
196
|
-
|
85
|
+
def nl
|
86
|
+
@line_count += 1
|
87
|
+
@buffer << "\n"
|
197
88
|
end
|
198
89
|
|
199
90
|
def write_prefix(prefix, after_indent)
|
@@ -204,14 +95,9 @@ module Choosy::Printing
|
|
204
95
|
end
|
205
96
|
|
206
97
|
def write_lines(str, prefix, indent_first)
|
207
|
-
if str.nil?
|
208
|
-
@buffer << "\n"
|
209
|
-
return
|
210
|
-
end
|
211
|
-
|
212
98
|
str.split("\n").each do |line|
|
213
99
|
if line.length == 0
|
214
|
-
|
100
|
+
nl
|
215
101
|
else
|
216
102
|
index = 0
|
217
103
|
|
@@ -248,7 +134,7 @@ module Choosy::Printing
|
|
248
134
|
char.downto(char - MAX_BACKTRACK) do |i| # Only go back a fixed line segment
|
249
135
|
if line[i, 1] == ' '
|
250
136
|
@buffer << line[index, i - index]
|
251
|
-
|
137
|
+
nl
|
252
138
|
return i + 1
|
253
139
|
end
|
254
140
|
end
|
@@ -259,7 +145,7 @@ module Choosy::Printing
|
|
259
145
|
|
260
146
|
def write_rest(line, index)
|
261
147
|
@buffer << line[index, line.length]
|
262
|
-
|
148
|
+
nl
|
263
149
|
line.length
|
264
150
|
end
|
265
151
|
end
|