tty-prompt 0.18.0 → 0.22.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.
- 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
|