tty-prompt 0.18.0 → 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +92 -0
- data/README.md +549 -248
- data/lib/tty-prompt.rb +1 -2
- data/lib/tty/prompt.rb +187 -143
- data/lib/tty/prompt/answers_collector.rb +5 -5
- data/lib/tty/prompt/{enum_paginator.rb → block_paginator.rb} +20 -19
- data/lib/tty/prompt/choice.rb +5 -7
- data/lib/tty/prompt/choices.rb +29 -11
- data/lib/tty/prompt/confirm_question.rb +38 -16
- data/lib/tty/prompt/const.rb +17 -0
- data/lib/tty/prompt/converter_dsl.rb +6 -7
- data/lib/tty/prompt/converter_registry.rb +31 -26
- data/lib/tty/prompt/converters.rb +139 -32
- data/lib/tty/prompt/enum_list.rb +57 -27
- data/lib/tty/prompt/errors.rb +31 -0
- data/lib/tty/prompt/evaluator.rb +1 -1
- data/lib/tty/prompt/expander.rb +39 -13
- data/lib/tty/prompt/keypress.rb +31 -36
- data/lib/tty/prompt/list.rb +175 -65
- data/lib/tty/prompt/mask_question.rb +4 -5
- data/lib/tty/prompt/multi_list.rb +124 -33
- data/lib/tty/prompt/multiline.rb +7 -6
- data/lib/tty/prompt/paginator.rb +38 -26
- data/lib/tty/prompt/question.rb +83 -34
- data/lib/tty/prompt/question/checks.rb +18 -0
- data/lib/tty/prompt/question/validation.rb +3 -3
- data/lib/tty/prompt/selected_choices.rb +76 -0
- data/lib/tty/prompt/slider.rb +83 -16
- data/lib/tty/prompt/statement.rb +3 -3
- data/lib/tty/prompt/suggestion.rb +6 -6
- data/lib/tty/prompt/symbols.rb +58 -34
- data/lib/tty/prompt/test.rb +36 -0
- data/lib/tty/prompt/timer.rb +75 -0
- data/lib/tty/prompt/utils.rb +1 -3
- data/lib/tty/prompt/version.rb +1 -1
- metadata +29 -227
- data/Rakefile +0 -8
- data/examples/ask.rb +0 -7
- data/examples/ask_valid.rb +0 -12
- data/examples/collect.rb +0 -21
- data/examples/echo.rb +0 -11
- data/examples/enum_select.rb +0 -7
- data/examples/enum_select_disabled.rb +0 -16
- data/examples/enum_select_paged.rb +0 -9
- data/examples/enum_select_wrapped.rb +0 -15
- data/examples/expand.rb +0 -29
- data/examples/in.rb +0 -9
- data/examples/inputs.rb +0 -10
- data/examples/key_events.rb +0 -15
- data/examples/keypress.rb +0 -9
- data/examples/mask.rb +0 -13
- data/examples/multi_select.rb +0 -8
- data/examples/multi_select_disabled.rb +0 -17
- data/examples/multi_select_paged.rb +0 -9
- data/examples/multi_select_wrapped.rb +0 -15
- data/examples/multiline.rb +0 -9
- data/examples/pause.rb +0 -9
- data/examples/select.rb +0 -20
- data/examples/select_disabled.rb +0 -18
- data/examples/select_enum.rb +0 -8
- data/examples/select_filtered.rb +0 -11
- data/examples/select_paginated.rb +0 -11
- data/examples/select_wrapped.rb +0 -15
- data/examples/slider.rb +0 -6
- data/examples/validation.rb +0 -9
- data/examples/yes_no.rb +0 -7
- data/lib/tty/prompt/messages.rb +0 -49
- data/lib/tty/prompt/timeout.rb +0 -78
- data/lib/tty/test_prompt.rb +0 -20
- data/spec/spec_helper.rb +0 -45
- data/spec/unit/ask_spec.rb +0 -132
- data/spec/unit/choice/eql_spec.rb +0 -22
- data/spec/unit/choice/from_spec.rb +0 -96
- data/spec/unit/choices/add_spec.rb +0 -12
- data/spec/unit/choices/each_spec.rb +0 -13
- data/spec/unit/choices/find_by_spec.rb +0 -10
- data/spec/unit/choices/new_spec.rb +0 -10
- data/spec/unit/choices/pluck_spec.rb +0 -9
- data/spec/unit/collect_spec.rb +0 -96
- data/spec/unit/converters/convert_bool_spec.rb +0 -58
- data/spec/unit/converters/convert_char_spec.rb +0 -11
- data/spec/unit/converters/convert_custom_spec.rb +0 -14
- data/spec/unit/converters/convert_date_spec.rb +0 -34
- data/spec/unit/converters/convert_file_spec.rb +0 -18
- data/spec/unit/converters/convert_number_spec.rb +0 -39
- data/spec/unit/converters/convert_path_spec.rb +0 -15
- data/spec/unit/converters/convert_range_spec.rb +0 -22
- data/spec/unit/converters/convert_regex_spec.rb +0 -12
- data/spec/unit/converters/convert_string_spec.rb +0 -21
- data/spec/unit/converters/on_error_spec.rb +0 -9
- data/spec/unit/distance/distance_spec.rb +0 -73
- data/spec/unit/enum_paginator_spec.rb +0 -75
- data/spec/unit/enum_select_spec.rb +0 -446
- data/spec/unit/error_spec.rb +0 -20
- data/spec/unit/evaluator_spec.rb +0 -67
- data/spec/unit/expand_spec.rb +0 -198
- data/spec/unit/keypress_spec.rb +0 -72
- data/spec/unit/mask_spec.rb +0 -132
- data/spec/unit/multi_select_spec.rb +0 -495
- data/spec/unit/multiline_spec.rb +0 -77
- data/spec/unit/new_spec.rb +0 -20
- data/spec/unit/ok_spec.rb +0 -10
- data/spec/unit/paginator_spec.rb +0 -73
- data/spec/unit/question/checks_spec.rb +0 -97
- data/spec/unit/question/default_spec.rb +0 -31
- data/spec/unit/question/echo_spec.rb +0 -38
- data/spec/unit/question/in_spec.rb +0 -115
- data/spec/unit/question/initialize_spec.rb +0 -12
- data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
- data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
- data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
- data/spec/unit/question/modify_spec.rb +0 -41
- data/spec/unit/question/required_spec.rb +0 -92
- data/spec/unit/question/validate_spec.rb +0 -115
- data/spec/unit/question/validation/call_spec.rb +0 -31
- data/spec/unit/question/validation/coerce_spec.rb +0 -30
- data/spec/unit/result_spec.rb +0 -40
- data/spec/unit/say_spec.rb +0 -67
- data/spec/unit/select_spec.rb +0 -643
- data/spec/unit/slider_spec.rb +0 -100
- data/spec/unit/statement/initialize_spec.rb +0 -15
- data/spec/unit/subscribe_spec.rb +0 -22
- data/spec/unit/suggest_spec.rb +0 -28
- data/spec/unit/warn_spec.rb +0 -21
- data/spec/unit/yes_no_spec.rb +0 -251
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-prompt.gemspec +0 -33
data/lib/tty/prompt/expander.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "choices"
|
4
4
|
|
5
5
|
module TTY
|
6
6
|
class Prompt
|
@@ -10,8 +10,8 @@ module TTY
|
|
10
10
|
# @api private
|
11
11
|
class Expander
|
12
12
|
HELP_CHOICE = {
|
13
|
-
key:
|
14
|
-
name:
|
13
|
+
key: "h",
|
14
|
+
name: "print help",
|
15
15
|
value: :help
|
16
16
|
}.freeze
|
17
17
|
|
@@ -22,8 +22,10 @@ module TTY
|
|
22
22
|
@prompt = prompt
|
23
23
|
@prefix = options.fetch(:prefix) { @prompt.prefix }
|
24
24
|
@default = options.fetch(:default) { 1 }
|
25
|
+
@auto_hint = options.fetch(:auto_hint) { false }
|
25
26
|
@active_color = options.fetch(:active_color) { @prompt.active_color }
|
26
27
|
@help_color = options.fetch(:help_color) { @prompt.help_color }
|
28
|
+
@quiet = options.fetch(:quiet) { @prompt.quiet }
|
27
29
|
@choices = Choices.new
|
28
30
|
@selected = nil
|
29
31
|
@done = false
|
@@ -55,15 +57,16 @@ module TTY
|
|
55
57
|
|
56
58
|
selected = select_choice(@input)
|
57
59
|
|
58
|
-
if selected && selected.key.to_s ==
|
60
|
+
if selected && selected.key.to_s == "h"
|
59
61
|
expand
|
60
62
|
@selected = nil
|
61
|
-
@input =
|
63
|
+
@input = ""
|
62
64
|
elsif selected
|
63
65
|
@done = true
|
64
66
|
@selected = selected
|
67
|
+
@hint = nil
|
65
68
|
else
|
66
|
-
@input =
|
69
|
+
@input = ""
|
67
70
|
end
|
68
71
|
end
|
69
72
|
alias keyreturn keyenter
|
@@ -77,6 +80,7 @@ module TTY
|
|
77
80
|
elsif event.value =~ /^[^\e\n\r]/
|
78
81
|
@input += event.value
|
79
82
|
end
|
83
|
+
|
80
84
|
@selected = select_choice(@input)
|
81
85
|
if @selected && !@default_key && collapsed?
|
82
86
|
@hint = @selected.name
|
@@ -100,6 +104,13 @@ module TTY
|
|
100
104
|
@default = value
|
101
105
|
end
|
102
106
|
|
107
|
+
# Set quiet mode.
|
108
|
+
#
|
109
|
+
# @api public
|
110
|
+
def quiet(value)
|
111
|
+
@quiet = value
|
112
|
+
end
|
113
|
+
|
103
114
|
# Add a single choice
|
104
115
|
#
|
105
116
|
# @api public
|
@@ -151,19 +162,19 @@ module TTY
|
|
151
162
|
elsif @input.to_s.empty? && default_key
|
152
163
|
keys[@default - 1] = @prompt.decorate(default_key, @active_color)
|
153
164
|
end
|
154
|
-
keys.join(
|
165
|
+
keys.join(",")
|
155
166
|
end
|
156
167
|
|
157
168
|
# @api private
|
158
169
|
def render
|
159
|
-
@input =
|
170
|
+
@input = ""
|
160
171
|
until @done
|
161
172
|
question = render_question
|
162
173
|
@prompt.print(question)
|
163
174
|
read_input
|
164
175
|
@prompt.print(refresh(question.lines.count))
|
165
176
|
end
|
166
|
-
@prompt.print(render_question)
|
177
|
+
@prompt.print(render_question) unless @quiet
|
167
178
|
answer
|
168
179
|
end
|
169
180
|
|
@@ -196,7 +207,7 @@ module TTY
|
|
196
207
|
#
|
197
208
|
# @api private
|
198
209
|
def render_hint
|
199
|
-
"\n" + @prompt.decorate(
|
210
|
+
"\n" + @prompt.decorate(">> ", @active_color) +
|
200
211
|
@hint +
|
201
212
|
@prompt.cursor.prev_line +
|
202
213
|
@prompt.cursor.forward(@prompt.strip(render_header).size)
|
@@ -208,6 +219,7 @@ module TTY
|
|
208
219
|
#
|
209
220
|
# @api private
|
210
221
|
def render_question
|
222
|
+
load_auto_hint if @auto_hint
|
211
223
|
header = render_header
|
212
224
|
header << render_hint if @hint
|
213
225
|
header << "\n" if @done
|
@@ -219,6 +231,20 @@ module TTY
|
|
219
231
|
header
|
220
232
|
end
|
221
233
|
|
234
|
+
def load_auto_hint
|
235
|
+
if @hint.nil? && collapsed?
|
236
|
+
if @selected
|
237
|
+
@hint = @selected.name
|
238
|
+
else
|
239
|
+
if @input.empty?
|
240
|
+
@hint = @choices[@default - 1].name
|
241
|
+
else
|
242
|
+
@hint = "invalid option"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
222
248
|
def render_footer
|
223
249
|
" Choice [#{@choices[@default - 1].key}]: #{@input}"
|
224
250
|
end
|
@@ -235,7 +261,7 @@ module TTY
|
|
235
261
|
#
|
236
262
|
# @api private
|
237
263
|
def refresh(lines)
|
238
|
-
if @hint && (!@selected || @done)
|
264
|
+
if (@hint && (!@selected || @done)) || (@auto_hint && collapsed?)
|
239
265
|
@hint = nil
|
240
266
|
@prompt.clear_lines(lines, :down) +
|
241
267
|
@prompt.cursor.prev_line
|
@@ -256,7 +282,7 @@ module TTY
|
|
256
282
|
if @selected && @selected.key == choice.key
|
257
283
|
chosen = @prompt.decorate(chosen, @active_color)
|
258
284
|
end
|
259
|
-
output <<
|
285
|
+
output << " " + chosen + "\n"
|
260
286
|
end
|
261
287
|
output.join
|
262
288
|
end
|
@@ -276,7 +302,7 @@ module TTY
|
|
276
302
|
if choice.key.length != 1
|
277
303
|
errors << "Choice key `#{choice.key}` is more than one character long."
|
278
304
|
end
|
279
|
-
if choice.key.to_s ==
|
305
|
+
if choice.key.to_s == "h"
|
280
306
|
errors << "Choice key `#{choice.key}` is reserved for help menu."
|
281
307
|
end
|
282
308
|
if keys.include?(choice.key)
|
data/lib/tty/prompt/keypress.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative 'timeout'
|
3
|
+
require_relative "question"
|
4
|
+
require_relative "timer"
|
6
5
|
|
7
6
|
module TTY
|
8
7
|
class Prompt
|
@@ -13,7 +12,7 @@ module TTY
|
|
13
12
|
# @param [Hash] options
|
14
13
|
#
|
15
14
|
# @api public
|
16
|
-
def initialize(prompt, options
|
15
|
+
def initialize(prompt, **options)
|
17
16
|
super
|
18
17
|
@echo = options.fetch(:echo) { false }
|
19
18
|
@keys = options.fetch(:keys) { UndefinedSetting }
|
@@ -21,18 +20,10 @@ module TTY
|
|
21
20
|
@interval = options.fetch(:interval) {
|
22
21
|
(@timeout != UndefinedSetting && @timeout < 1) ? @timeout : 1
|
23
22
|
}
|
23
|
+
@decimals = (@interval.to_s.split(".")[1] || []).size
|
24
24
|
@countdown = @timeout
|
25
|
-
|
26
|
-
|
27
|
-
question = render_question
|
28
|
-
line_size = question.size
|
29
|
-
total_lines = @prompt.count_screen_lines(line_size)
|
30
|
-
@prompt.print(refresh(question.lines.count, total_lines))
|
31
|
-
countdown(time)
|
32
|
-
@prompt.print(render_question)
|
33
|
-
end
|
34
|
-
}
|
35
|
-
@scheduler = Timeout.new(interval_handler: @interval_handler)
|
25
|
+
time = timeout? ? Float(@timeout) : nil
|
26
|
+
@timer = Timer.new(time, Float(@interval))
|
36
27
|
|
37
28
|
@prompt.subscribe(self)
|
38
29
|
end
|
@@ -55,10 +46,8 @@ module TTY
|
|
55
46
|
def keypress(event)
|
56
47
|
if any_key?
|
57
48
|
@done = true
|
58
|
-
@scheduler.cancel
|
59
49
|
elsif @keys.is_a?(Array) && @keys.include?(event.key.name)
|
60
50
|
@done = true
|
61
|
-
@scheduler.cancel
|
62
51
|
else
|
63
52
|
@done = false
|
64
53
|
end
|
@@ -66,36 +55,42 @@ module TTY
|
|
66
55
|
|
67
56
|
def render_question
|
68
57
|
header = super
|
69
|
-
|
58
|
+
if timeout?
|
59
|
+
header.gsub!(/:countdown/, format("%.#{@decimals}f", countdown))
|
60
|
+
end
|
70
61
|
header
|
71
62
|
end
|
72
63
|
|
64
|
+
def interval_handler(time)
|
65
|
+
return if @done
|
66
|
+
|
67
|
+
question = render_question
|
68
|
+
line_size = question.size
|
69
|
+
total_lines = @prompt.count_screen_lines(line_size)
|
70
|
+
@prompt.print(refresh(question.lines.count, total_lines))
|
71
|
+
countdown(time)
|
72
|
+
@prompt.print(render_question)
|
73
|
+
end
|
74
|
+
|
73
75
|
def process_input(question)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
76
|
+
@prompt.print(render_question)
|
77
|
+
|
78
|
+
@timer.on_tick do |time|
|
79
|
+
interval_handler(time)
|
80
|
+
end
|
81
|
+
|
82
|
+
@timer.while_remaining do |remaining|
|
83
|
+
break if @done
|
84
|
+
@input = @prompt.read_keypress(nonblock: true)
|
79
85
|
end
|
86
|
+
countdown(0) unless @done
|
87
|
+
|
80
88
|
@evaluator.(@input)
|
81
89
|
end
|
82
90
|
|
83
91
|
def refresh(lines, lines_to_clear)
|
84
92
|
@prompt.clear_lines(lines)
|
85
93
|
end
|
86
|
-
|
87
|
-
# Wait for keypress or timeout
|
88
|
-
#
|
89
|
-
# @api private
|
90
|
-
def time(&job)
|
91
|
-
if timeout?
|
92
|
-
time = Float(@timeout)
|
93
|
-
interval = Float(@interval)
|
94
|
-
@scheduler.timeout(time, interval, &job)
|
95
|
-
else
|
96
|
-
job.()
|
97
|
-
end
|
98
|
-
end
|
99
94
|
end # Keypress
|
100
95
|
end # Prompt
|
101
96
|
end # TTY
|
data/lib/tty/prompt/list.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "English"
|
4
4
|
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
5
|
+
require_relative "choices"
|
6
|
+
require_relative "paginator"
|
7
|
+
require_relative "block_paginator"
|
8
8
|
|
9
9
|
module TTY
|
10
10
|
class Prompt
|
@@ -13,14 +13,8 @@ module TTY
|
|
13
13
|
#
|
14
14
|
# @api private
|
15
15
|
class List
|
16
|
-
include Symbols
|
17
|
-
|
18
|
-
HELP = '(Use arrow%s keys, press Enter to select%s)'.freeze
|
19
|
-
|
20
|
-
PAGE_HELP = '(Move up or down to reveal more choices)'.freeze
|
21
|
-
|
22
16
|
# Allowed keys for filter, along with backspace and canc.
|
23
|
-
FILTER_KEYS_MATCHER = /\A([[:alnum:]]|[[:punct:]])\Z
|
17
|
+
FILTER_KEYS_MATCHER = /\A([[:alnum:]]|[[:punct:]])\Z/.freeze
|
24
18
|
|
25
19
|
# Create instance of TTY::Prompt::List menu.
|
26
20
|
#
|
@@ -36,34 +30,42 @@ module TTY
|
|
36
30
|
# the delimiter for the item index
|
37
31
|
#
|
38
32
|
# @api public
|
39
|
-
def initialize(prompt, options
|
33
|
+
def initialize(prompt, **options)
|
40
34
|
check_options_consistency(options)
|
41
35
|
|
42
36
|
@prompt = prompt
|
43
37
|
@prefix = options.fetch(:prefix) { @prompt.prefix }
|
44
38
|
@enum = options.fetch(:enum) { nil }
|
45
|
-
@default = Array[
|
46
|
-
@active = @default.first
|
39
|
+
@default = Array(options[:default])
|
47
40
|
@choices = Choices.new
|
48
41
|
@active_color = options.fetch(:active_color) { @prompt.active_color }
|
49
42
|
@help_color = options.fetch(:help_color) { @prompt.help_color }
|
50
|
-
@marker = options.fetch(:marker) { symbols[:pointer] }
|
51
43
|
@cycle = options.fetch(:cycle) { false }
|
52
44
|
@filterable = options.fetch(:filter) { false }
|
45
|
+
@symbols = @prompt.symbols.merge(options.fetch(:symbols, {}))
|
46
|
+
@quiet = options.fetch(:quiet) { @prompt.quiet }
|
53
47
|
@filter = []
|
48
|
+
@filter_cache = {}
|
54
49
|
@help = options[:help]
|
50
|
+
@show_help = options.fetch(:show_help) { :start }
|
55
51
|
@first_render = true
|
56
52
|
@done = false
|
57
53
|
@per_page = options[:per_page]
|
58
|
-
@page_help = options[:page_help] || PAGE_HELP
|
59
54
|
@paginator = Paginator.new
|
55
|
+
@block_paginator = BlockPaginator.new
|
56
|
+
@by_page = false
|
57
|
+
@paging_changed = false
|
60
58
|
end
|
61
59
|
|
62
|
-
#
|
60
|
+
# Change symbols used by this prompt
|
61
|
+
#
|
62
|
+
# @param [Hash] new_symbols
|
63
|
+
# the new symbols to use
|
63
64
|
#
|
64
65
|
# @api public
|
65
|
-
def
|
66
|
-
@
|
66
|
+
def symbols(new_symbols = (not_set = true))
|
67
|
+
return @symbols if not_set
|
68
|
+
@symbols.merge!(new_symbols)
|
67
69
|
end
|
68
70
|
|
69
71
|
# Set default option selected
|
@@ -73,6 +75,32 @@ module TTY
|
|
73
75
|
@default = default_values
|
74
76
|
end
|
75
77
|
|
78
|
+
# Select paginator based on the current navigation key
|
79
|
+
#
|
80
|
+
# @return [Paginator]
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
def paginator
|
84
|
+
@by_page ? @block_paginator : @paginator
|
85
|
+
end
|
86
|
+
|
87
|
+
# Synchronize paginators start positions
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
def sync_paginators
|
91
|
+
if @by_page
|
92
|
+
if @paginator.start_index
|
93
|
+
@block_paginator.reset!
|
94
|
+
@block_paginator.start_index = @paginator.start_index
|
95
|
+
end
|
96
|
+
else
|
97
|
+
if @block_paginator.start_index
|
98
|
+
@paginator.reset!
|
99
|
+
@paginator.start_index = @block_paginator.start_index
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
76
104
|
# Set number of items per page
|
77
105
|
#
|
78
106
|
# @api public
|
@@ -93,13 +121,6 @@ module TTY
|
|
93
121
|
choices.size > page_size
|
94
122
|
end
|
95
123
|
|
96
|
-
# @param [String] text
|
97
|
-
# the help text to display per page
|
98
|
-
# @api pbulic
|
99
|
-
def page_help(text)
|
100
|
-
@page_help = text
|
101
|
-
end
|
102
|
-
|
103
124
|
# Provide help information
|
104
125
|
#
|
105
126
|
# @param [String] value
|
@@ -114,20 +135,46 @@ module TTY
|
|
114
135
|
@help = (@help.nil? && !not_set) ? value : default_help
|
115
136
|
end
|
116
137
|
|
117
|
-
#
|
138
|
+
# Change when help is displayed
|
118
139
|
#
|
119
140
|
# @api public
|
120
|
-
def
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
141
|
+
def show_help(value = (not_set = true))
|
142
|
+
return @show_ehlp if not_set
|
143
|
+
|
144
|
+
@show_help = value
|
145
|
+
end
|
146
|
+
|
147
|
+
# Information about arrow keys
|
148
|
+
#
|
149
|
+
# @return [String]
|
150
|
+
#
|
151
|
+
# @api private
|
152
|
+
def arrows_help
|
153
|
+
up_down = @symbols[:arrow_up] + "/" + @symbols[:arrow_down]
|
154
|
+
left_right = @symbols[:arrow_left] + "/" + @symbols[:arrow_right]
|
155
|
+
|
156
|
+
arrows = [up_down]
|
157
|
+
arrows << "/" if paginated?
|
158
|
+
arrows << left_right if paginated?
|
159
|
+
arrows.join
|
160
|
+
end
|
129
161
|
|
130
|
-
|
162
|
+
# Default help text
|
163
|
+
#
|
164
|
+
# Note that enumeration and filter are mutually exclusive
|
165
|
+
#
|
166
|
+
# @a public
|
167
|
+
def default_help
|
168
|
+
str = []
|
169
|
+
str << "(Press "
|
170
|
+
str << "#{arrows_help} arrow"
|
171
|
+
str << " or 1-#{choices.size} number" if enumerate?
|
172
|
+
str << " to move"
|
173
|
+
str << (filterable? ? "," : " and")
|
174
|
+
str << " Enter to select"
|
175
|
+
str << " and letters to filter" if filterable?
|
176
|
+
str << ")"
|
177
|
+
str.join
|
131
178
|
end
|
132
179
|
|
133
180
|
# Set selecting active index using number pad
|
@@ -137,10 +184,18 @@ module TTY
|
|
137
184
|
@enum = value
|
138
185
|
end
|
139
186
|
|
187
|
+
# Set whether selected answers are echoed
|
188
|
+
#
|
189
|
+
# @api public
|
190
|
+
def quiet(value)
|
191
|
+
@quiet = value
|
192
|
+
end
|
193
|
+
|
140
194
|
# Add a single choice
|
141
195
|
#
|
142
196
|
# @api public
|
143
197
|
def choice(*value, &block)
|
198
|
+
@filter_cache = {}
|
144
199
|
if block
|
145
200
|
@choices << (value << block)
|
146
201
|
else
|
@@ -160,12 +215,13 @@ module TTY
|
|
160
215
|
if !filterable? || @filter.empty?
|
161
216
|
@choices
|
162
217
|
else
|
163
|
-
@
|
164
|
-
|
165
|
-
|
218
|
+
filter_value = @filter.join.downcase
|
219
|
+
@filter_cache[filter_value] ||= @choices.enabled.select do |choice|
|
220
|
+
choice.name.to_s.downcase.include?(filter_value)
|
166
221
|
end
|
167
222
|
end
|
168
223
|
else
|
224
|
+
@filter_cache = {}
|
169
225
|
values.each { |val| @choices << val }
|
170
226
|
end
|
171
227
|
end
|
@@ -195,6 +251,7 @@ module TTY
|
|
195
251
|
|
196
252
|
def keynum(event)
|
197
253
|
return unless enumerate?
|
254
|
+
|
198
255
|
value = event.value.to_i
|
199
256
|
return unless (1..choices.count).cover?(value)
|
200
257
|
return if choices[value - 1].disabled?
|
@@ -218,11 +275,14 @@ module TTY
|
|
218
275
|
if prev_active
|
219
276
|
@active = prev_active
|
220
277
|
elsif @cycle
|
221
|
-
searchable =
|
278
|
+
searchable = choices.length.downto(1).to_a
|
222
279
|
prev_active = search_choice_in(searchable)
|
223
280
|
|
224
281
|
@active = prev_active if prev_active
|
225
282
|
end
|
283
|
+
|
284
|
+
@paging_changed = @by_page
|
285
|
+
@by_page = false
|
226
286
|
end
|
227
287
|
|
228
288
|
def keydown(*)
|
@@ -237,9 +297,50 @@ module TTY
|
|
237
297
|
|
238
298
|
@active = next_active if next_active
|
239
299
|
end
|
300
|
+
@paging_changed = @by_page
|
301
|
+
@by_page = false
|
240
302
|
end
|
241
303
|
alias keytab keydown
|
242
304
|
|
305
|
+
# Moves all choices page by page keeping the current selected item
|
306
|
+
# at the same level on each page.
|
307
|
+
#
|
308
|
+
# When the choice on a page is outside of next page range then
|
309
|
+
# adjust it to the last item, otherwise leave unchanged.
|
310
|
+
def keyright(*)
|
311
|
+
if (@active + page_size) <= @choices.size
|
312
|
+
searchable = ((@active + page_size)..choices.length)
|
313
|
+
@active = search_choice_in(searchable)
|
314
|
+
elsif @active <= @choices.size # last page shorter
|
315
|
+
current = @active % page_size
|
316
|
+
remaining = @choices.size % page_size
|
317
|
+
if current.zero? || (remaining > 0 && current > remaining)
|
318
|
+
searchable = @choices.size.downto(0).to_a
|
319
|
+
@active = search_choice_in(searchable)
|
320
|
+
elsif @cycle
|
321
|
+
searchable = ((current.zero? ? page_size : current)..choices.length)
|
322
|
+
@active = search_choice_in(searchable)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
@paging_changed = !@by_page
|
327
|
+
@by_page = true
|
328
|
+
end
|
329
|
+
alias keypage_down keyright
|
330
|
+
|
331
|
+
def keyleft(*)
|
332
|
+
if (@active - page_size) > 0
|
333
|
+
searchable = ((@active - page_size)..choices.length)
|
334
|
+
@active = search_choice_in(searchable)
|
335
|
+
elsif @cycle
|
336
|
+
searchable = @choices.size.downto(1).to_a
|
337
|
+
@active = search_choice_in(searchable)
|
338
|
+
end
|
339
|
+
@paging_changed = !@by_page
|
340
|
+
@by_page = true
|
341
|
+
end
|
342
|
+
alias keypage_up keyleft
|
343
|
+
|
243
344
|
def keypress(event)
|
244
345
|
return unless filterable?
|
245
346
|
|
@@ -277,7 +378,12 @@ module TTY
|
|
277
378
|
# @api private
|
278
379
|
def setup_defaults
|
279
380
|
validate_defaults
|
280
|
-
|
381
|
+
|
382
|
+
if !@default.empty?
|
383
|
+
@active = @default.first
|
384
|
+
else
|
385
|
+
@active = @choices.index { |choice| !choice.disabled? } + 1
|
386
|
+
end
|
281
387
|
end
|
282
388
|
|
283
389
|
# Validate default indexes to be within range
|
@@ -322,7 +428,7 @@ module TTY
|
|
322
428
|
|
323
429
|
@prompt.print(refresh(question_lines_count(question_lines)))
|
324
430
|
end
|
325
|
-
@prompt.print(render_question)
|
431
|
+
@prompt.print(render_question) unless @quiet
|
326
432
|
answer
|
327
433
|
ensure
|
328
434
|
@prompt.print(@prompt.show)
|
@@ -367,7 +473,6 @@ module TTY
|
|
367
473
|
@first_render = false
|
368
474
|
unless @done
|
369
475
|
header << render_menu
|
370
|
-
header << render_footer
|
371
476
|
end
|
372
477
|
header.join
|
373
478
|
end
|
@@ -390,6 +495,20 @@ module TTY
|
|
390
495
|
"(Filter: #{@filter.join.inspect})"
|
391
496
|
end
|
392
497
|
|
498
|
+
# Check if help is shown only on start
|
499
|
+
#
|
500
|
+
# @api private
|
501
|
+
def help_start?
|
502
|
+
@show_help =~ /start/i
|
503
|
+
end
|
504
|
+
|
505
|
+
# Check if help is always displayed
|
506
|
+
#
|
507
|
+
# @api private
|
508
|
+
def help_always?
|
509
|
+
@show_help =~ /always/i
|
510
|
+
end
|
511
|
+
|
393
512
|
# Render initial help and selected choice
|
394
513
|
#
|
395
514
|
# @return [String]
|
@@ -398,8 +517,9 @@ module TTY
|
|
398
517
|
def render_header
|
399
518
|
if @done
|
400
519
|
selected_item = choices[@active - 1].name
|
401
|
-
@prompt.decorate(selected_item, @active_color)
|
402
|
-
elsif @first_render
|
520
|
+
@prompt.decorate(selected_item.to_s, @active_color)
|
521
|
+
elsif (@first_render && (help_start? || help_always?)) ||
|
522
|
+
(help_always? && !@filter.any?)
|
403
523
|
@prompt.decorate(help, @help_color)
|
404
524
|
elsif filterable? && @filter.any?
|
405
525
|
@prompt.decorate(filter_help, @help_color)
|
@@ -414,35 +534,25 @@ module TTY
|
|
414
534
|
def render_menu
|
415
535
|
output = []
|
416
536
|
|
417
|
-
|
418
|
-
|
537
|
+
sync_paginators if @paging_changed
|
538
|
+
paginator.paginate(choices, @active, @per_page) do |choice, index|
|
539
|
+
num = enumerate? ? (index + 1).to_s + @enum + " " : ""
|
419
540
|
message = if index + 1 == @active && !choice.disabled?
|
420
|
-
selected = @marker
|
541
|
+
selected = "#{@symbols[:marker]} #{num}#{choice.name}"
|
421
542
|
@prompt.decorate(selected.to_s, @active_color)
|
422
543
|
elsif choice.disabled?
|
423
|
-
@prompt.decorate(symbols[:cross], :red) +
|
424
|
-
|
544
|
+
@prompt.decorate(@symbols[:cross], :red) +
|
545
|
+
" #{num}#{choice.name} #{choice.disabled}"
|
425
546
|
else
|
426
|
-
|
547
|
+
" #{num}#{choice.name}"
|
427
548
|
end
|
428
|
-
|
429
|
-
newline = (index ==
|
549
|
+
end_index = paginated? ? paginator.end_index : choices.size - 1
|
550
|
+
newline = (index == end_index) ? "" : "\n"
|
430
551
|
output << (message + newline)
|
431
552
|
end
|
432
553
|
|
433
554
|
output.join
|
434
555
|
end
|
435
|
-
|
436
|
-
# Render page info footer
|
437
|
-
#
|
438
|
-
# @return [String]
|
439
|
-
#
|
440
|
-
# @api private
|
441
|
-
def render_footer
|
442
|
-
return '' unless paginated?
|
443
|
-
colored_footer = @prompt.decorate(@page_help, @help_color)
|
444
|
-
"\n" + colored_footer
|
445
|
-
end
|
446
556
|
end # List
|
447
557
|
end # Prompt
|
448
558
|
end # TTY
|