highline 1.7.10 → 2.0.3
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.
- checksums.yaml +5 -5
- data/.gitignore +5 -0
- data/.rubocop.yml +84 -0
- data/.simplecov +5 -0
- data/.travis.yml +35 -9
- data/Changelog.md +214 -15
- data/Gemfile +16 -5
- data/README.md +202 -0
- data/Rakefile +8 -20
- data/appveyor.yml +37 -0
- data/examples/ansi_colors.rb +6 -11
- data/examples/asking_for_arrays.rb +6 -2
- data/examples/basic_usage.rb +31 -21
- data/examples/color_scheme.rb +11 -10
- data/examples/get_character.rb +8 -4
- data/examples/limit.rb +4 -0
- data/examples/menus.rb +16 -10
- data/examples/overwrite.rb +9 -5
- data/examples/page_and_wrap.rb +5 -4
- data/examples/password.rb +4 -0
- data/examples/repeat_entry.rb +10 -5
- data/examples/trapping_eof.rb +2 -1
- data/examples/using_readline.rb +2 -1
- data/highline.gemspec +25 -27
- data/lib/highline/builtin_styles.rb +129 -0
- data/lib/highline/color_scheme.rb +49 -32
- data/lib/highline/compatibility.rb +11 -4
- data/lib/highline/custom_errors.rb +57 -0
- data/lib/highline/import.rb +19 -12
- data/lib/highline/io_console_compatible.rb +37 -0
- data/lib/highline/list.rb +177 -0
- data/lib/highline/list_renderer.rb +261 -0
- data/lib/highline/menu/item.rb +32 -0
- data/lib/highline/menu.rb +306 -111
- data/lib/highline/paginator.rb +52 -0
- data/lib/highline/question/answer_converter.rb +103 -0
- data/lib/highline/question.rb +281 -131
- data/lib/highline/question_asker.rb +150 -0
- data/lib/highline/simulate.rb +24 -13
- data/lib/highline/statement.rb +88 -0
- data/lib/highline/string.rb +36 -0
- data/lib/highline/string_extensions.rb +83 -64
- data/lib/highline/style.rb +196 -63
- data/lib/highline/template_renderer.rb +62 -0
- data/lib/highline/terminal/io_console.rb +36 -0
- data/lib/highline/terminal/ncurses.rb +38 -0
- data/lib/highline/terminal/unix_stty.rb +51 -0
- data/lib/highline/terminal.rb +190 -0
- data/lib/highline/version.rb +3 -1
- data/lib/highline/wrapper.rb +53 -0
- data/lib/highline.rb +390 -788
- metadata +56 -35
- data/INSTALL +0 -59
- data/README.rdoc +0 -74
- data/lib/highline/system_extensions.rb +0 -254
- data/setup.rb +0 -1360
- data/test/string_methods.rb +0 -32
- data/test/tc_color_scheme.rb +0 -96
- data/test/tc_highline.rb +0 -1402
- data/test/tc_import.rb +0 -52
- data/test/tc_menu.rb +0 -439
- data/test/tc_simulator.rb +0 -33
- data/test/tc_string_extension.rb +0 -33
- data/test/tc_string_highline.rb +0 -38
- 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
|
13
|
-
#
|
14
|
-
#
|
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
|
20
|
-
# choices
|
21
|
-
# Questions, so all that functionality is available
|
22
|
-
# well.
|
23
|
-
#
|
24
|
-
|
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", [
|
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
|
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
|
-
|
137
|
-
|
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
|
-
|
140
|
-
|
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
|
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(
|
237
|
+
def choices(*names, &action)
|
150
238
|
names.each { |n| choice(n, &action) }
|
151
239
|
end
|
152
240
|
|
153
|
-
# Identical to choice
|
154
|
-
|
155
|
-
|
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
|
-
|
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=(
|
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
|
-
|
180
|
-
|
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 =
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
-
|
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
|
239
|
-
# <tt
|
240
|
-
# otherwise evaluated in the
|
241
|
-
# context
|
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=(
|
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
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
-
|
372
|
+
map_items_by_index + map_items_by_name
|
272
373
|
end
|
273
|
-
|
374
|
+
end
|
274
375
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
382
|
+
(1..all_items.size).map(&:to_s)
|
282
383
|
end
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
292
|
-
#
|
293
|
-
|
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
|
-
|
408
|
+
items = all_items
|
296
409
|
|
297
410
|
# Find the selected action.
|
298
|
-
|
299
|
-
|
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
|
-
|
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
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
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
|
-
|
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
|
-
|
334
|
-
when :letter
|
335
|
-
|
336
|
-
|
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
|
-
|
525
|
+
[text, ""]
|
339
526
|
else
|
340
|
-
|
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
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
538
|
+
%(<%= header ? "#{header}:\n" : '' %>) +
|
539
|
+
parse_list +
|
540
|
+
show_default_if_any +
|
541
|
+
"<%= prompt %>"
|
355
542
|
when :one_line
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
543
|
+
%(<%= header ? "#{header}: " : '' %>) +
|
544
|
+
"<%= prompt %>" \
|
545
|
+
"(" + parse_list + ")" +
|
546
|
+
show_default_if_any +
|
547
|
+
"<%= prompt[/\s*$/] %>"
|
361
548
|
when :menu_only
|
362
|
-
|
363
|
-
|
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
|
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
|
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
|