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.
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