highline 1.7.10 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +84 -0
  4. data/.simplecov +5 -0
  5. data/.travis.yml +35 -9
  6. data/Changelog.md +214 -15
  7. data/Gemfile +16 -5
  8. data/README.md +202 -0
  9. data/Rakefile +8 -20
  10. data/appveyor.yml +37 -0
  11. data/examples/ansi_colors.rb +6 -11
  12. data/examples/asking_for_arrays.rb +6 -2
  13. data/examples/basic_usage.rb +31 -21
  14. data/examples/color_scheme.rb +11 -10
  15. data/examples/get_character.rb +8 -4
  16. data/examples/limit.rb +4 -0
  17. data/examples/menus.rb +16 -10
  18. data/examples/overwrite.rb +9 -5
  19. data/examples/page_and_wrap.rb +5 -4
  20. data/examples/password.rb +4 -0
  21. data/examples/repeat_entry.rb +10 -5
  22. data/examples/trapping_eof.rb +2 -1
  23. data/examples/using_readline.rb +2 -1
  24. data/highline.gemspec +25 -27
  25. data/lib/highline/builtin_styles.rb +129 -0
  26. data/lib/highline/color_scheme.rb +49 -32
  27. data/lib/highline/compatibility.rb +11 -4
  28. data/lib/highline/custom_errors.rb +57 -0
  29. data/lib/highline/import.rb +19 -12
  30. data/lib/highline/io_console_compatible.rb +37 -0
  31. data/lib/highline/list.rb +177 -0
  32. data/lib/highline/list_renderer.rb +261 -0
  33. data/lib/highline/menu/item.rb +32 -0
  34. data/lib/highline/menu.rb +306 -111
  35. data/lib/highline/paginator.rb +52 -0
  36. data/lib/highline/question/answer_converter.rb +103 -0
  37. data/lib/highline/question.rb +281 -131
  38. data/lib/highline/question_asker.rb +150 -0
  39. data/lib/highline/simulate.rb +24 -13
  40. data/lib/highline/statement.rb +88 -0
  41. data/lib/highline/string.rb +36 -0
  42. data/lib/highline/string_extensions.rb +83 -64
  43. data/lib/highline/style.rb +196 -63
  44. data/lib/highline/template_renderer.rb +62 -0
  45. data/lib/highline/terminal/io_console.rb +36 -0
  46. data/lib/highline/terminal/ncurses.rb +38 -0
  47. data/lib/highline/terminal/unix_stty.rb +51 -0
  48. data/lib/highline/terminal.rb +190 -0
  49. data/lib/highline/version.rb +3 -1
  50. data/lib/highline/wrapper.rb +53 -0
  51. data/lib/highline.rb +390 -788
  52. metadata +56 -35
  53. data/INSTALL +0 -59
  54. data/README.rdoc +0 -74
  55. data/lib/highline/system_extensions.rb +0 -254
  56. data/setup.rb +0 -1360
  57. data/test/string_methods.rb +0 -32
  58. data/test/tc_color_scheme.rb +0 -96
  59. data/test/tc_highline.rb +0 -1402
  60. data/test/tc_import.rb +0 -52
  61. data/test/tc_menu.rb +0 -439
  62. data/test/tc_simulator.rb +0 -33
  63. data/test/tc_string_extension.rb +0 -33
  64. data/test/tc_string_highline.rb +0 -38
  65. data/test/tc_style.rb +0 -578
data/lib/highline/menu.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # coding: utf-8
2
+
3
+ #--
1
4
  # menu.rb
2
5
  #
3
6
  # Created by Gregory Thomas Brown on 2005-05-10.
@@ -6,30 +9,55 @@
6
9
  # This is Free Software. See LICENSE and COPYING for details.
7
10
 
8
11
  require "highline/question"
12
+ require "highline/menu/item"
9
13
 
10
14
  class HighLine
11
15
  #
12
- # Menu objects encapsulate all the details of a call to HighLine.choose().
13
- # Using the accessors and Menu.choice() and Menu.choices(), the block passed
14
- # to HighLine.choose() can detail all aspects of menu display and control.
16
+ # Menu objects encapsulate all the details of a call to
17
+ # {HighLine#choose HighLine#choose}.
18
+ # Using the accessors and {Menu#choice} and {Menu#choices}, the block passed
19
+ # to {HighLine#choose} can detail all aspects of menu display and control.
15
20
  #
16
21
  class Menu < Question
22
+ # Pass +false+ to _color_ to turn off HighLine::Menu's
23
+ # index coloring.
24
+ # Pass a color and the Menu's indices will be colored.
25
+ class << self
26
+ attr_writer :index_color
27
+ end
28
+
29
+ # Initialize it
30
+ self.index_color = false
31
+
32
+ # Returns color used for coloring Menu's indices
33
+ class << self
34
+ attr_reader :index_color
35
+ end
36
+
17
37
  #
18
38
  # Create an instance of HighLine::Menu. All customization is done
19
- # through the passed block, which should call accessors and choice() and
20
- # choices() as needed to define the Menu. Note that Menus are also
21
- # Questions, so all that functionality is available to the block as
22
- # well.
23
- #
24
- def initialize( )
39
+ # through the passed block, which should call accessors, {#choice} and
40
+ # {#choices} as needed to define the Menu. Note that Menus are also
41
+ # {HighLine::Question Questions}, so all that functionality is available
42
+ # to the block as well.
43
+ #
44
+ # @example Implicit menu creation through HighLine#choose
45
+ # cli = HighLine.new
46
+ # answer = cli.choose do |menu|
47
+ # menu.prompt = "Please choose your favorite programming language? "
48
+ # menu.choice(:ruby) { say("Good choice!") }
49
+ # menu.choices(:python, :perl) { say("Not from around here, are you?") }
50
+ # end
51
+
52
+ def initialize
25
53
  #
26
54
  # Initialize Question objects with ignored values, we'll
27
55
  # adjust ours as needed.
28
56
  #
29
- super("Ignored", [ ], &nil) # avoiding passing the block along
57
+ super("Ignored", [], &nil) # avoiding passing the block along
30
58
 
31
- @items = [ ]
32
- @hidden_items = [ ]
59
+ @items = []
60
+ @hidden_items = []
33
61
  @help = Hash.new("There's no help for that topic.")
34
62
 
35
63
  @index = :number
@@ -43,14 +71,18 @@ class HighLine
43
71
  @shell = false
44
72
  @nil_on_handled = false
45
73
 
74
+ # Used for coloring Menu indices.
75
+ # Set it to default. But you may override it.
76
+ @index_color = self.class.index_color
77
+
46
78
  # Override Questions responses, we'll set our own.
47
- @responses = { }
79
+ @responses = {}
48
80
  # Context for action code.
49
81
  @highline = nil
50
82
 
51
83
  yield self if block_given?
52
84
 
53
- init_help if @shell and not @help.empty?
85
+ init_help if @shell && !@help.empty?
54
86
  end
55
87
 
56
88
  #
@@ -119,6 +151,11 @@ class HighLine
119
151
  # Defaults to +false+.
120
152
  #
121
153
  attr_accessor :nil_on_handled
154
+ #
155
+ # The color of the index when displaying the menu. See Style class for
156
+ # available colors.
157
+ #
158
+ attr_accessor :index_color
122
159
 
123
160
  #
124
161
  # Adds _name_ to the list of available menu items. Menu items will be
@@ -133,28 +170,85 @@ class HighLine
133
170
  # the current HighLine context before the action code is called and can
134
171
  # thus be used for adding output and the like.
135
172
  #
136
- def choice( name, help = nil, &action )
137
- @items << [name, action]
173
+ # @param name [#to_s] menu item title/header/name to be displayed.
174
+ # @param action [Proc] callback action to be run when the item is selected.
175
+ # @param help [String] help/hint string to be displayed.
176
+ # @return [void]
177
+ # @example (see HighLine::Menu#initialize)
178
+ # @example Use of help string on menu items
179
+ # cli = HighLine.new
180
+ # cli.choose do |menu|
181
+ # menu.shell = true
182
+ #
183
+ # menu.choice(:load, text: 'Load a file',
184
+ # help: "Load a file using your favourite editor.")
185
+ # menu.choice(:save, help: "Save data in file.")
186
+ # menu.choice(:quit, help: "Exit program.")
187
+ #
188
+ # menu.help("rules", "The rules of this system are as follows...")
189
+ # end
190
+
191
+ def choice(name, help = nil, text = nil, &action)
192
+ item = Menu::Item.new(name, text: text, help: help, action: action)
193
+ @items << item
194
+ @help.merge!(item.item_help)
195
+ update_responses # rebuild responses based on our settings
196
+ end
197
+
198
+ #
199
+ # This method helps reduce the namespaces in the original call,
200
+ # which would look like this: HighLine::Menu::Item.new(...)
201
+ # With #build_item, it looks like this: menu.build_item(...)
202
+ # @param *args splat args, the same args you would pass to an
203
+ # initialization of HighLine::Menu::Item
204
+ # @return [HighLine::Menu::Item] the menu item
205
+
206
+ def build_item(*args)
207
+ Menu::Item.new(*args)
208
+ end
209
+
210
+ #
211
+ # Adds an item directly to the menu. If you want more configuration
212
+ # or options, use this method
213
+ #
214
+ # @param item [Menu::Item] item containing choice fields and more
215
+ # @return [void]
138
216
 
139
- @help[name.to_s.downcase] = help unless help.nil?
140
- update_responses # rebuild responses based on our settings
217
+ def add_item(item)
218
+ @items << item
219
+ @help.merge!(item.item_help)
220
+ update_responses
141
221
  end
142
222
 
143
223
  #
144
- # A shortcut for multiple calls to the sister method choice(). <b>Be
224
+ # A shortcut for multiple calls to the sister method {#choice}. <b>Be
145
225
  # warned:</b> An _action_ set here will apply to *all* provided
146
226
  # _names_. This is considered to be a feature, so you can easily
147
227
  # hand-off interface processing to a different chunk of code.
228
+ # @param names [Array<#to_s>] menu item titles/headers/names to be
229
+ # displayed.
230
+ # @param action (see #choice)
231
+ # @return [void]
232
+ # @example (see HighLine::Menu#initialize)
233
+ #
234
+ # choice has more options available to you, like longer text or help (and
235
+ # of course, individual actions)
148
236
  #
149
- def choices( *names, &action )
237
+ def choices(*names, &action)
150
238
  names.each { |n| choice(n, &action) }
151
239
  end
152
240
 
153
- # Identical to choice(), but the item will not be listed for the user.
154
- def hidden( name, help = nil, &action )
155
- @hidden_items << [name, action]
241
+ # Identical to {#choice}, but the item will not be listed for the user.
242
+ # @see #choice
243
+ # @param name (see #choice)
244
+ # @param help (see #choice)
245
+ # @param action (see #choice)
246
+ # @return (see #choice)
156
247
 
157
- @help[name.to_s.downcase] = help unless help.nil?
248
+ def hidden(name, help = nil, &action)
249
+ item = Menu::Item.new(name, text: name, help: help, action: action)
250
+ @hidden_items << item
251
+ @help.merge!(item.item_help)
158
252
  end
159
253
 
160
254
  #
@@ -172,31 +266,36 @@ class HighLine
172
266
  # _index_suffix_ to a single space and _select_by_ to <tt>:name</tt>.
173
267
  # Because of this, you should make a habit of setting the _index_ first.
174
268
  #
175
- def index=( style )
269
+ def index=(style)
176
270
  @index = style
177
271
 
272
+ return unless @index == :none || @index.is_a?(::String)
273
+
178
274
  # Default settings.
179
- if @index == :none or @index.is_a?(::String)
180
- @index_suffix = " "
181
- @select_by = :name
182
- end
275
+ @index_suffix = " "
276
+ @select_by = :name
183
277
  end
184
278
 
185
279
  #
186
280
  # Initializes the help system by adding a <tt>:help</tt> choice, some
187
281
  # action code, and the default help listing.
188
282
  #
189
- def init_help( )
283
+ def init_help
190
284
  return if @items.include?(:help)
191
285
 
192
286
  topics = @help.keys.sort
193
- help_help = @help.include?("help") ? @help["help"] :
194
- "This command will display helpful messages about " +
195
- "functionality, like this one. To see the help for " +
196
- "a specific topic enter:\n\thelp [TOPIC]\nTry asking " +
197
- "for help on any of the following:\n\n" +
198
- "<%= list(#{topics.inspect}, :columns_across) %>"
199
- choice(:help, help_help) do |command, topic|
287
+ help_help =
288
+ if @help.include?("help")
289
+ @help["help"]
290
+ else
291
+ "This command will display helpful messages about " \
292
+ "functionality, like this one. To see the help for " \
293
+ "a specific topic enter:\n\thelp [TOPIC]\nTry asking " \
294
+ "for help on any of the following:\n\n" \
295
+ "<%= list(#{topics.inspect}, :columns_across) %>"
296
+ end
297
+
298
+ choice(:help, help_help) do |_command, topic|
200
299
  topic.strip!
201
300
  topic.downcase!
202
301
  if topic.empty?
@@ -209,9 +308,13 @@ class HighLine
209
308
 
210
309
  #
211
310
  # Used to set help for arbitrary topics. Use the topic <tt>"help"</tt>
212
- # to override the default message.
311
+ # to override the default message. Mainly for internal use.
213
312
  #
214
- def help( topic, help )
313
+ # @param topic [String] the menu item header/title/name to be associated
314
+ # with a help message.
315
+ # @param help [String] the help message to be associated with the menu
316
+ # item/title/name.
317
+ def help(topic, help)
215
318
  @help[topic] = help
216
319
  end
217
320
 
@@ -235,24 +338,23 @@ class HighLine
235
338
  # <tt>:menu_only</tt>:: Just the menu items, followed up by a likely
236
339
  # short _prompt_.
237
340
  # <i>any ERb String</i>:: Will be taken as the literal _layout_. This
238
- # String can access <tt>@header</tt>,
239
- # <tt>@menu</tt> and <tt>@prompt</tt>, but is
240
- # otherwise evaluated in the typical HighLine
241
- # context, to provide access to utilities like
242
- # HighLine.list() primarily.
341
+ # String can access <tt>header</tt>,
342
+ # <tt>menu</tt> and <tt>prompt</tt>, but is
343
+ # otherwise evaluated in the TemplateRenderer
344
+ # context so each method is properly delegated.
243
345
  #
244
346
  # If set to either <tt>:one_line</tt>, or <tt>:menu_only</tt>, _index_
245
347
  # will default to <tt>:none</tt> and _flow_ will default to
246
348
  # <tt>:inline</tt>.
247
349
  #
248
- def layout=( new_layout )
350
+ def layout=(new_layout)
249
351
  @layout = new_layout
250
352
 
251
353
  # Default settings.
252
354
  case @layout
253
355
  when :one_line, :menu_only
254
356
  self.index = :none
255
- @flow = :inline
357
+ @flow = :inline
256
358
  end
257
359
  end
258
360
 
@@ -260,66 +362,142 @@ class HighLine
260
362
  # This method returns all possible options for auto-completion, based
261
363
  # on the settings of _index_ and _select_by_.
262
364
  #
263
- def options( )
264
- # add in any hidden menu commands
265
- @items.concat(@hidden_items)
266
-
267
- by_index = if @index == :letter
268
- l_index = "`"
269
- @items.map { "#{l_index.succ!}" }
365
+ def options
366
+ case @select_by
367
+ when :index
368
+ map_items_by_index
369
+ when :name
370
+ map_items_by_name
270
371
  else
271
- (1 .. @items.size).collect { |s| String(s) }
372
+ map_items_by_index + map_items_by_name
272
373
  end
273
- by_name = @items.collect { |c| c.first }
374
+ end
274
375
 
275
- case @select_by
276
- when :index then
277
- by_index
278
- when :name
279
- by_name
376
+ def map_items_by_index
377
+ if [:letter, :capital_letter].include?(@index)
378
+ # @ and ` are the previous ASCII characters to A and a respectively
379
+ prev_char = (@index == :capital_letter ? '@' : '`')
380
+ all_items.map { prev_char.succ!.dup }
280
381
  else
281
- by_index + by_name
382
+ (1..all_items.size).map(&:to_s)
282
383
  end
283
- ensure
284
- # make sure the hidden items are removed, before we return
285
- @items.slice!(@items.size - @hidden_items.size, @hidden_items.size)
384
+ end
385
+
386
+ def map_items_by_name
387
+ all_items.map(&:name)
388
+ end
389
+
390
+ def all_items
391
+ @items + @hidden_items
286
392
  end
287
393
 
288
394
  #
289
395
  # This method processes the auto-completed user selection, based on the
290
396
  # rules for this Menu object. If an action was provided for the
291
- # selection, it will be executed as described in Menu.choice().
292
- #
293
- def select( highline_context, selection, details = nil )
397
+ # selection, it will be executed as described in {#choice}.
398
+ #
399
+ # @param highline_context [HighLine] a HighLine instance to be used
400
+ # as context.
401
+ # @param selection [String, Integer] index or title of the selected
402
+ # menu item.
403
+ # @param details additional parameter to be passed when in shell mode.
404
+ # @return [nil, Object] if @nil_on_handled is set it returns +nil+,
405
+ # else it returns the action return value.
406
+ def select(highline_context, selection, details = nil)
294
407
  # add in any hidden menu commands
295
- @items.concat(@hidden_items)
408
+ items = all_items
296
409
 
297
410
  # Find the selected action.
298
- name, action = if selection =~ /^\d+$/
299
- @items[selection.to_i - 1]
411
+ selected_item = find_item_from_selection(items, selection)
412
+
413
+ # Run or return it.
414
+ @highline = highline_context
415
+ value_for_selected_item(selected_item, details)
416
+ end
417
+
418
+ def find_item_from_selection(items, selection)
419
+ if selection =~ /^\d+$/ # is a number?
420
+ get_item_by_number(items, selection)
300
421
  else
301
- l_index = "`"
302
- index = @items.map { "#{l_index.succ!}" }.index(selection)
303
- @items.find { |c| c.first == selection } or @items[index]
422
+ get_item_by_letter(items, selection)
304
423
  end
424
+ end
305
425
 
306
- # Run or return it.
307
- if not action.nil?
308
- @highline = highline_context
309
- if @shell
310
- result = action.call(name, details)
311
- else
312
- result = action.call(name)
313
- end
426
+ # Returns the menu item referenced by its index
427
+ # @param selection [Integer] menu item's index.
428
+ def get_item_by_number(items, selection)
429
+ items[selection.to_i - 1]
430
+ end
431
+
432
+ # Returns the menu item referenced by its title/header/name.
433
+ # @param selection [String] menu's title/header/name
434
+ def get_item_by_letter(items, selection)
435
+ item = items.find { |i| i.name == selection }
436
+ return item if item
437
+
438
+ # 97 is the "a" letter at ascii table
439
+ # Ex: For "a" it will return 0, and for "c" it will return 2
440
+ index = selection.downcase.ord - 97
441
+ items[index]
442
+ end
443
+
444
+ def value_for_selected_item(item, details)
445
+ if item.action
446
+ result = if @shell
447
+ item.action.call(item.name, details)
448
+ else
449
+ item.action.call(item.name)
450
+ end
314
451
  @nil_on_handled ? nil : result
315
- elsif action.nil?
316
- name
317
452
  else
318
- nil
453
+ item.name
454
+ end
455
+ end
456
+
457
+ def gather_selected(highline_context, selections, details = nil)
458
+ @highline = highline_context
459
+ # add in any hidden menu commands
460
+ items = all_items
461
+
462
+ if selections.is_a?(Array)
463
+ value_for_array_selections(items, selections, details)
464
+ elsif selections.is_a?(Hash)
465
+ value_for_hash_selections(items, selections, details)
466
+ else
467
+ raise ArgumentError, "selections must be either Array or Hash"
468
+ end
469
+ end
470
+
471
+ def value_for_array_selections(items, selections, details)
472
+ # Find the selected items and return values
473
+ selected_items = selections.map do |selection|
474
+ find_item_from_selection(items, selection)
475
+ end
476
+ index = 0
477
+ selected_items.map do |selected_item|
478
+ value = value_for_selected_item(selected_item, self.shell ? details[index] : nil)
479
+ index += 1
480
+ value
481
+ end
482
+ end
483
+
484
+ def value_for_hash_selections(items, selections, details)
485
+ # Find the selected items and return in hash form
486
+ index = 0
487
+ selections.each_with_object({}) do |(key, selection), memo|
488
+ selected_item = find_item_from_selection(items, selection)
489
+ value = value_for_selected_item(selected_item, self.shell ? details[index] : nil)
490
+ index += 1
491
+ memo[key] = value
492
+ end
493
+ end
494
+
495
+ def decorate_index(index)
496
+ if index_color
497
+ HighLine.color(index, index_color)
498
+ else
499
+ index
319
500
  end
320
- ensure
321
- # make sure the hidden items are removed, before we return
322
- @items.slice!(@items.size - @hidden_items.size, @hidden_items.size)
323
501
  end
324
502
 
325
503
  #
@@ -327,17 +505,26 @@ class HighLine
327
505
  # This method returns all menu items to be displayed, complete with
328
506
  # indexes.
329
507
  #
330
- def to_ary( )
508
+ def to_ary
509
+ @items.map.with_index { |item, ix| decorate_item(item.text.to_s, ix) }
510
+ end
511
+
512
+ def decorate_item(text, ix)
513
+ decorated, non_decorated = mark_for_decoration(text, ix)
514
+ decorate_index(decorated) + non_decorated
515
+ end
516
+
517
+ def mark_for_decoration(text, ix)
331
518
  case @index
332
519
  when :number
333
- @items.map { |c| "#{@items.index(c) + 1}#{@index_suffix}#{c.first}" }
334
- when :letter
335
- l_index = "`"
336
- @items.map { |c| "#{l_index.succ!}#{@index_suffix}#{c.first}" }
520
+ ["#{ix + 1}#{@index_suffix}", text]
521
+ when :letter, :capital_letter
522
+ first_letter = (@index == :capital_letter ? 'A' : 'a')
523
+ ["#{(first_letter.ord + ix).chr}#{@index_suffix}", text]
337
524
  when :none
338
- @items.map { |c| "#{c.first}" }
525
+ [text, ""]
339
526
  else
340
- @items.map { |c| "#{index}#{@index_suffix}#{c.first}" }
527
+ ["#{index}#{@index_suffix}", text]
341
528
  end
342
529
  end
343
530
 
@@ -345,37 +532,45 @@ class HighLine
345
532
  # Allows Menu to behave as a String, just like Question. Returns the
346
533
  # _layout_ to be rendered, which is used by HighLine.say().
347
534
  #
348
- def to_s( )
535
+ def to_s
349
536
  case @layout
350
537
  when :list
351
- '<%= if @header.nil? then '' else "#{@header}:\n" end %>' +
352
- "<%= list( @menu, #{@flow.inspect},
353
- #{@list_option.inspect} ) %>" +
354
- "<%= @prompt %>"
538
+ %(<%= header ? "#{header}:\n" : '' %>) +
539
+ parse_list +
540
+ show_default_if_any +
541
+ "<%= prompt %>"
355
542
  when :one_line
356
- '<%= if @header.nil? then '' else "#{@header}: " end %>' +
357
- "<%= @prompt %>" +
358
- "(<%= list( @menu, #{@flow.inspect},
359
- #{@list_option.inspect} ) %>)" +
360
- "<%= @prompt[/\s*$/] %>"
543
+ %(<%= header ? "#{header}: " : '' %>) +
544
+ "<%= prompt %>" \
545
+ "(" + parse_list + ")" +
546
+ show_default_if_any +
547
+ "<%= prompt[/\s*$/] %>"
361
548
  when :menu_only
362
- "<%= list( @menu, #{@flow.inspect},
363
- #{@list_option.inspect} ) %><%= @prompt %>"
549
+ parse_list +
550
+ show_default_if_any +
551
+ "<%= prompt %>"
364
552
  else
365
553
  @layout
366
554
  end
367
555
  end
368
556
 
557
+ def parse_list
558
+ "<%= list( menu, #{@flow.inspect},
559
+ #{@list_option.inspect} ) %>"
560
+ end
561
+
562
+ def show_default_if_any
563
+ default.to_s.empty? ? "" : "(#{default}) "
564
+ end
565
+
369
566
  #
370
567
  # This method will update the intelligent responses to account for
371
568
  # Menu specific differences. Calls the superclass' (Question's)
372
569
  # build_responses method, overriding its default arguments to specify
373
- # 'options' will be used to populate choice lists, and that
374
- # the newly built hash will predominate over the preexisting hash
375
- # for any keys that are the same.
570
+ # 'options' will be used to populate choice lists.
376
571
  #
377
- def update_responses( )
378
- build_responses(options, true)
572
+ def update_responses
573
+ build_responses(options)
379
574
  end
380
575
  end
381
576
  end
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+
3
+ class HighLine
4
+ # Take the task of paginating some piece of text given a HighLine context
5
+ class Paginator
6
+ # @return [HighLine] HighLine context
7
+ attr_reader :highline
8
+
9
+ # Returns a HighLine::Paginator instance where you can
10
+ # call {#page_print} on it.
11
+ # @param highline [HighLine] context
12
+ # @example
13
+ # HighLine::Paginator.new(highline).page_print(statement)
14
+ def initialize(highline)
15
+ @highline = highline
16
+ end
17
+
18
+ #
19
+ # Page print a series of at most _page_at_ lines for _output_. After each
20
+ # page is printed, HighLine will pause until the user presses enter/return
21
+ # then display the next page of data.
22
+ #
23
+ # Note that the final page of _output_ is *not* printed, but returned
24
+ # instead. This is to support any special handling for the final sequence.
25
+ #
26
+ # @param text [String] text to be paginated
27
+ # @return [String] last line if paging is aborted
28
+ def page_print(text)
29
+ return text unless highline.page_at
30
+
31
+ lines = text.lines.to_a
32
+ while lines.size > highline.page_at
33
+ highline.puts lines.slice!(0...highline.page_at).join
34
+ highline.puts
35
+ # Return last line if user wants to abort paging
36
+ return "...\n#{lines.last}" unless continue_paging?
37
+ end
38
+ lines.join
39
+ end
40
+
41
+ #
42
+ # Ask user if they wish to continue paging output. Allows them to
43
+ # type "q" to cancel the paging process.
44
+ #
45
+ def continue_paging?
46
+ command = highline.new_scope.ask(
47
+ "-- press enter/return to continue or q to stop -- "
48
+ ) { |q| q.character = true }
49
+ command !~ /\A[qQ]\Z/ # Only continue paging if Q was not hit.
50
+ end
51
+ end
52
+ end