choosy 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGELOG.md +8 -0
  2. data/LICENSE +2 -0
  3. data/README.markdown +102 -54
  4. data/Rakefile +27 -5
  5. data/TODO.md +5 -0
  6. data/examples/bar.rb +9 -7
  7. data/examples/foo.rb +9 -7
  8. data/examples/superfoo.rb +48 -36
  9. data/lib/VERSION.yml +6 -0
  10. data/lib/choosy/argument.rb +5 -0
  11. data/lib/choosy/base_command.rb +11 -10
  12. data/lib/choosy/command.rb +17 -1
  13. data/lib/choosy/converter.rb +5 -1
  14. data/lib/choosy/dsl/argument_builder.rb +33 -21
  15. data/lib/choosy/dsl/base_builder.rb +26 -0
  16. data/lib/choosy/dsl/base_command_builder.rb +23 -30
  17. data/lib/choosy/dsl/command_builder.rb +8 -12
  18. data/lib/choosy/dsl/option_builder.rb +14 -45
  19. data/lib/choosy/dsl/super_command_builder.rb +17 -20
  20. data/lib/choosy/errors.rb +1 -0
  21. data/lib/choosy/option.rb +19 -0
  22. data/lib/choosy/printing/base_printer.rb +231 -0
  23. data/lib/choosy/printing/color.rb +8 -5
  24. data/lib/choosy/printing/erb_printer.rb +1 -1
  25. data/lib/choosy/printing/help_printer.rb +49 -163
  26. data/lib/choosy/printing/manpage.rb +235 -0
  27. data/lib/choosy/printing/manpage_printer.rb +95 -0
  28. data/lib/choosy/printing/terminal.rb +39 -8
  29. data/lib/choosy/printing.rb +1 -0
  30. data/lib/choosy/super_command.rb +13 -4
  31. data/lib/choosy/super_parser.rb +5 -1
  32. data/lib/choosy/verifier.rb +8 -0
  33. data/lib/choosy/version.rb +64 -5
  34. data/spec/choosy/argument_spec.rb +28 -0
  35. data/spec/choosy/base_command_spec.rb +7 -3
  36. data/spec/choosy/command_spec.rb +2 -2
  37. data/spec/choosy/converter_spec.rb +3 -2
  38. data/spec/choosy/dsl/argument_builder_spec.rb +19 -8
  39. data/spec/choosy/dsl/base_builder_spec.rb +43 -0
  40. data/spec/choosy/dsl/base_command_builder_spec.rb +7 -9
  41. data/spec/choosy/dsl/commmand_builder_spec.rb +9 -1
  42. data/spec/choosy/dsl/option_builder_spec.rb +1 -65
  43. data/spec/choosy/dsl/super_command_builder_spec.rb +19 -8
  44. data/spec/choosy/option_spec.rb +68 -0
  45. data/spec/choosy/printing/base_printer_spec.rb +155 -0
  46. data/spec/choosy/printing/color_spec.rb +4 -0
  47. data/spec/choosy/printing/help_printer_spec.rb +15 -109
  48. data/spec/choosy/printing/manpage_printer_spec.rb +95 -0
  49. data/spec/choosy/printing/manpage_spec.rb +206 -0
  50. data/spec/choosy/super_command_spec.rb +7 -0
  51. data/spec/choosy/super_parser_spec.rb +9 -0
  52. data/spec/choosy/verifier_spec.rb +31 -1
  53. data/spec/choosy/version.yml +5 -0
  54. data/spec/choosy/version_spec.rb +87 -0
  55. data/spec/integration/command-C_spec.rb +23 -0
  56. data/spec/integration/supercommand-C_spec.rb +45 -0
  57. metadata +81 -78
  58. 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?(color)
52
- COLORS.has_key?(color.to_sym)
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.exist?(options[:template])
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
- include Terminal
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
- @indent = options[:indent] || ' '
12
- @offset = options[:offset] || ' '
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
- if options[:color] == false
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
- print_usage(command)
27
-
28
- cmd_indent, option_indent, prefixes = retrieve_formatting_info(command)
29
-
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
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 print_usage(command)
45
- print_header(@usage)
27
+ def format_prologue(command)
28
+ format_header(@usage)
46
29
  @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
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 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
38
+ def format_epilogue(command)
39
+ nl
40
+ @buffer
89
41
  end
90
42
 
91
- def print_element(element)
92
- if element.header?
93
- @buffer << "\n"
94
- print_header(element.value, element.styles)
95
- @buffer << "\n"
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 print_option(option, formatted_prefix, opt_indent)
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 print_command(command, formatted_prefix, cmd_indent)
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 usage_option(option, value=nil)
113
- value ||= ""
114
- value << "["
115
- if option.short_flag
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
- value << ' '
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 retrieve_formatting_info(command)
171
- cmdlen = 0
172
- optionlen = 0
173
- prefixes = []
77
+ def option_begin
78
+ @option_begin ||= color.multiple(nil, option_styles)
79
+ end
174
80
 
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
81
+ def option_end
82
+ color.reset
83
+ end
193
84
 
194
- option_indent = ' ' * (optionlen + indent.length + offset.length)
195
- cmd_indent = ' ' * (cmdlen + indent.length + offset.length)
196
- [cmd_indent, option_indent, prefixes]
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
- @buffer << "\n"
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
- @buffer << "\n"
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
- @buffer << "\n"
148
+ nl
263
149
  line.length
264
150
  end
265
151
  end