tty-prompt 0.8.0 → 0.9.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 +12 -0
- data/README.md +66 -6
- data/appveyor.yml +23 -0
- data/examples/enum_paged.rb +9 -0
- data/examples/select_paginated.rb +9 -0
- data/lib/tty-prompt.rb +2 -0
- data/lib/tty/prompt/enum_list.rb +127 -28
- data/lib/tty/prompt/enum_paginator.rb +54 -0
- data/lib/tty/prompt/list.rb +55 -9
- data/lib/tty/prompt/multi_list.rb +3 -3
- data/lib/tty/prompt/paginator.rb +88 -0
- data/lib/tty/prompt/reader.rb +15 -35
- data/lib/tty/prompt/version.rb +1 -1
- data/spec/unit/enum_paginator_spec.rb +49 -0
- data/spec/unit/enum_select_spec.rb +161 -7
- data/spec/unit/multi_select_spec.rb +41 -0
- data/spec/unit/paginator_spec.rb +47 -0
- data/spec/unit/reader/read_keypress_spec.rb +10 -0
- data/spec/unit/reader/read_line_spec.rb +9 -2
- data/spec/unit/reader/read_multiline_spec.rb +8 -0
- data/spec/unit/select_spec.rb +43 -2
- metadata +11 -2
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Prompt
|
5
|
+
class EnumPaginator < Paginator
|
6
|
+
# Paginate list of choices based on current active choice.
|
7
|
+
# Move entire pages.
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
def paginate(list, active, per_page = nil, &block)
|
11
|
+
default_size = (list.size <= DEFAULT_PAGE_SIZE ? list.size : DEFAULT_PAGE_SIZE)
|
12
|
+
@per_page = @per_page || per_page || default_size
|
13
|
+
|
14
|
+
# Don't paginate short lists
|
15
|
+
if list.size <= @per_page
|
16
|
+
@lower_index = 0
|
17
|
+
@upper_index = list.size - 1
|
18
|
+
if block
|
19
|
+
return list.each_with_index(&block)
|
20
|
+
else
|
21
|
+
return list.each_with_index.to_enum
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
unless active.nil? # User may input index out of range
|
26
|
+
@last_index = active
|
27
|
+
end
|
28
|
+
page = (@last_index / @per_page.to_f).ceil
|
29
|
+
pages = (list.size / @per_page.to_f).ceil
|
30
|
+
if page == 0
|
31
|
+
@lower_index = 0
|
32
|
+
@upper_index = @lower_index + @per_page - 1
|
33
|
+
elsif page > 0 && page <= pages
|
34
|
+
@lower_index = (page - 1) * @per_page
|
35
|
+
@upper_index = @lower_index + @per_page - 1
|
36
|
+
else
|
37
|
+
@upper_index = list.size - 1
|
38
|
+
@lower_index = @upper_index - @per_page + 1
|
39
|
+
end
|
40
|
+
|
41
|
+
sliced_list = list[@lower_index..@upper_index]
|
42
|
+
indices = (@lower_index..@upper_index)
|
43
|
+
|
44
|
+
if block
|
45
|
+
sliced_list.each_with_index do |item, index|
|
46
|
+
block[item, @lower_index + index]
|
47
|
+
end
|
48
|
+
else
|
49
|
+
sliced_list.zip(indices).to_enum unless block_given?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end # EnumPaginator
|
53
|
+
end # Prompt
|
54
|
+
end # TTY
|
data/lib/tty/prompt/list.rb
CHANGED
@@ -7,7 +7,9 @@ module TTY
|
|
7
7
|
#
|
8
8
|
# @api private
|
9
9
|
class List
|
10
|
-
HELP = '(Use arrow%s keys, press Enter to select)'
|
10
|
+
HELP = '(Use arrow%s keys, press Enter to select)'
|
11
|
+
|
12
|
+
PAGE_HELP = '(Move up or down to reveal more choices)'
|
11
13
|
|
12
14
|
# Create instance of TTY::Prompt::List menu.
|
13
15
|
#
|
@@ -36,6 +38,9 @@ module TTY
|
|
36
38
|
@help = options[:help]
|
37
39
|
@first_render = true
|
38
40
|
@done = false
|
41
|
+
@per_page = options[:per_page]
|
42
|
+
@page_help = options[:page_help] || PAGE_HELP
|
43
|
+
@paginator = Paginator.new
|
39
44
|
|
40
45
|
@prompt.subscribe(self)
|
41
46
|
end
|
@@ -54,6 +59,29 @@ module TTY
|
|
54
59
|
@default = default_values
|
55
60
|
end
|
56
61
|
|
62
|
+
# Set number of items per page
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def per_page(value)
|
66
|
+
@per_page = value
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if list is paginated
|
70
|
+
#
|
71
|
+
# @return [Boolean]
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
def paginated?
|
75
|
+
@choices.size >= (@per_page || Paginator::DEFAULT_PAGE_SIZE)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param [String] text
|
79
|
+
# the help text to display per page
|
80
|
+
# @api pbulic
|
81
|
+
def page_help(text)
|
82
|
+
@page_help = text
|
83
|
+
end
|
84
|
+
|
57
85
|
# Set selecting active index using number pad
|
58
86
|
#
|
59
87
|
# @api public
|
@@ -125,6 +153,7 @@ module TTY
|
|
125
153
|
def keydown(*)
|
126
154
|
@active = (@active == @choices.length) ? 1 : @active + 1
|
127
155
|
end
|
156
|
+
alias keytab keydown
|
128
157
|
|
129
158
|
private
|
130
159
|
|
@@ -163,9 +192,9 @@ module TTY
|
|
163
192
|
def render
|
164
193
|
@prompt.print(@prompt.hide)
|
165
194
|
until @done
|
166
|
-
render_question
|
195
|
+
lines = render_question
|
167
196
|
@prompt.read_keypress
|
168
|
-
refresh
|
197
|
+
refresh(lines)
|
169
198
|
end
|
170
199
|
render_question
|
171
200
|
answer = render_answer
|
@@ -183,22 +212,30 @@ module TTY
|
|
183
212
|
@choices[@active - 1].value
|
184
213
|
end
|
185
214
|
|
186
|
-
#
|
215
|
+
# Clear screen lines
|
216
|
+
#
|
217
|
+
# @param [Integer] lines
|
218
|
+
# the lines to clear
|
187
219
|
#
|
188
220
|
# @api private
|
189
|
-
def refresh
|
190
|
-
lines = @question.scan("\n").length + @choices.length + 1
|
221
|
+
def refresh(lines)
|
191
222
|
@prompt.print(@prompt.clear_lines(lines))
|
192
223
|
end
|
193
224
|
|
194
225
|
# Render question with instructions and menu
|
195
226
|
#
|
227
|
+
# @return [Integer] The number of lines for menu
|
228
|
+
#
|
196
229
|
# @api private
|
197
230
|
def render_question
|
198
231
|
header = "#{@prefix}#{@question} #{render_header}"
|
199
232
|
@prompt.puts(header)
|
200
233
|
@first_render = false
|
201
|
-
|
234
|
+
rendered_menu = render_menu
|
235
|
+
rendered_menu << render_footer
|
236
|
+
@prompt.print(rendered_menu) unless @done
|
237
|
+
|
238
|
+
header.lines.count + rendered_menu.lines.count
|
202
239
|
end
|
203
240
|
|
204
241
|
# Provide help information
|
@@ -228,7 +265,7 @@ module TTY
|
|
228
265
|
# @api private
|
229
266
|
def render_menu
|
230
267
|
output = ''
|
231
|
-
@choices
|
268
|
+
@paginator.paginate(@choices, @active, @per_page) do |choice, index|
|
232
269
|
num = enumerate? ? (index + 1).to_s + @enum + Symbols::SPACE : ''
|
233
270
|
message = if index + 1 == @active
|
234
271
|
selected = @marker + Symbols::SPACE + num + choice.name
|
@@ -236,11 +273,20 @@ module TTY
|
|
236
273
|
else
|
237
274
|
Symbols::SPACE * 2 + num + choice.name
|
238
275
|
end
|
239
|
-
newline = (index == @
|
276
|
+
newline = (index == @paginator.max_index) ? '' : "\n"
|
240
277
|
output << (message + newline)
|
241
278
|
end
|
242
279
|
output
|
243
280
|
end
|
281
|
+
|
282
|
+
# Render page info footer
|
283
|
+
#
|
284
|
+
# @api private
|
285
|
+
def render_footer
|
286
|
+
return '' unless paginated?
|
287
|
+
colored_footer = @prompt.decorate(@page_help, @help_color)
|
288
|
+
"\n" << colored_footer
|
289
|
+
end
|
244
290
|
end # List
|
245
291
|
end # Prompt
|
246
292
|
end # TTY
|
@@ -21,7 +21,7 @@ module TTY
|
|
21
21
|
super
|
22
22
|
@selected = []
|
23
23
|
@help = options[:help]
|
24
|
-
@default = options
|
24
|
+
@default = Array(options[:default])
|
25
25
|
end
|
26
26
|
|
27
27
|
# Callback fired when space key is pressed
|
@@ -84,7 +84,7 @@ module TTY
|
|
84
84
|
# @api private
|
85
85
|
def render_menu
|
86
86
|
output = ''
|
87
|
-
@choices
|
87
|
+
@paginator.paginate(@choices, @active, @per_page) do |choice, index|
|
88
88
|
num = enumerate? ? (index + 1).to_s + @enum + Symbols::SPACE : ''
|
89
89
|
indicator = (index + 1 == @active) ? @marker : Symbols::SPACE
|
90
90
|
indicator += Symbols::SPACE
|
@@ -94,7 +94,7 @@ module TTY
|
|
94
94
|
else
|
95
95
|
Symbols::RADIO_UNCHECKED + Symbols::SPACE + num + choice.name
|
96
96
|
end
|
97
|
-
newline = (index == @
|
97
|
+
newline = (index == @paginator.max_index) ? '' : "\n"
|
98
98
|
output << indicator + message + newline
|
99
99
|
end
|
100
100
|
output
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Prompt
|
5
|
+
class Paginator
|
6
|
+
DEFAULT_PAGE_SIZE = 6
|
7
|
+
|
8
|
+
# Create a Paginator
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
def initialize(options = {})
|
12
|
+
@last_index = Array(options[:default]).flatten.first || 0
|
13
|
+
@per_page = options[:per_page]
|
14
|
+
@lower_index = Array(options[:default]).flatten.first
|
15
|
+
end
|
16
|
+
|
17
|
+
# Maximum index for current pagination
|
18
|
+
#
|
19
|
+
# @return [Integer]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def max_index
|
23
|
+
raise ArgumentError, 'no max index' unless @per_page
|
24
|
+
@lower_index + @per_page - 1
|
25
|
+
end
|
26
|
+
|
27
|
+
# Paginate collection given an active index
|
28
|
+
#
|
29
|
+
# @param [Array[Choice]] list
|
30
|
+
# a collection of choice items
|
31
|
+
# @param [Integer] active
|
32
|
+
# current choice active index
|
33
|
+
# @param [Integer] per_page
|
34
|
+
# number of choice items per page
|
35
|
+
#
|
36
|
+
# @return [Enumerable]
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
def paginate(list, active, per_page = nil, &block)
|
40
|
+
current_index = active - 1
|
41
|
+
default_size = (list.size <= DEFAULT_PAGE_SIZE ? list.size : DEFAULT_PAGE_SIZE)
|
42
|
+
@per_page = @per_page || per_page || default_size
|
43
|
+
@lower_index ||= current_index
|
44
|
+
@upper_index ||= max_index
|
45
|
+
|
46
|
+
# Don't paginate short lists
|
47
|
+
if list.size <= @per_page
|
48
|
+
@lower_index = 0
|
49
|
+
@upper_index = list.size - 1
|
50
|
+
if block
|
51
|
+
return list.each_with_index(&block)
|
52
|
+
else
|
53
|
+
return list.each_with_index.to_enum
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if current_index > @last_index # going up
|
58
|
+
if current_index > @upper_index && current_index < list.size - 1
|
59
|
+
@lower_index += 1
|
60
|
+
end
|
61
|
+
elsif current_index < @last_index # going down
|
62
|
+
if current_index < @lower_index && current_index > 0
|
63
|
+
@lower_index -= 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Cycle list
|
68
|
+
if current_index.zero?
|
69
|
+
@lower_index = 0
|
70
|
+
elsif current_index == list.size - 1
|
71
|
+
@lower_index = list.size - 1 - (@per_page - 1)
|
72
|
+
end
|
73
|
+
|
74
|
+
@upper_index = @lower_index + (@per_page - 1)
|
75
|
+
@last_index = current_index
|
76
|
+
|
77
|
+
sliced_list = list[@lower_index..@upper_index]
|
78
|
+
indices = (@lower_index..@upper_index)
|
79
|
+
|
80
|
+
return sliced_list.zip(indices).to_enum unless block_given?
|
81
|
+
|
82
|
+
sliced_list.each_with_index do |item, index|
|
83
|
+
block[item, @lower_index + index]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end # Paginator
|
87
|
+
end # Prompt
|
88
|
+
end # TTY
|
data/lib/tty/prompt/reader.rb
CHANGED
@@ -93,22 +93,18 @@ module TTY
|
|
93
93
|
#
|
94
94
|
# @api public
|
95
95
|
def read_char(bytes = 1)
|
96
|
-
chars = input.
|
97
|
-
|
98
|
-
|
96
|
+
chars = input.getc
|
97
|
+
return if chars.nil?
|
98
|
+
while CSI.start_with?(chars) || chars.start_with?(CSI) &&
|
99
99
|
!(64..126).include?(chars.each_codepoint.to_a.last)
|
100
|
-
|
101
|
-
chars << next_char
|
100
|
+
chars << read_char(bytes + 1)
|
102
101
|
end
|
103
102
|
chars
|
104
|
-
rescue EOFError
|
105
|
-
# Finished processing
|
106
|
-
chars
|
107
103
|
end
|
108
104
|
|
109
|
-
# Get a single line from STDIN
|
110
|
-
#
|
111
|
-
#
|
105
|
+
# Get a single line from STDIN. Each key pressed is echoed
|
106
|
+
# back to the shell. The input terminates when enter or
|
107
|
+
# return key is pressed.
|
112
108
|
#
|
113
109
|
# @param [Boolean] echo
|
114
110
|
# if true echo back characters, output nothing otherwise
|
@@ -120,10 +116,14 @@ module TTY
|
|
120
116
|
line = ''
|
121
117
|
buffer do
|
122
118
|
mode.echo(echo) do
|
123
|
-
while (char =
|
124
|
-
!(
|
125
|
-
emit_key_event(
|
126
|
-
|
119
|
+
while (char = read_char) && (char_byte = char.unpack('c*')[0]) &&
|
120
|
+
!(char_byte == CARRIAGE_RETURN || char_byte == NEWLINE)
|
121
|
+
emit_key_event(char)
|
122
|
+
if char_byte == BACKSPACE || char_byte == DELETE
|
123
|
+
line = line.slice(-1, 1) unless line.empty?
|
124
|
+
else
|
125
|
+
line << char
|
126
|
+
end
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -168,26 +168,6 @@ module TTY
|
|
168
168
|
|
169
169
|
private
|
170
170
|
|
171
|
-
# Convert byte to unicode character
|
172
|
-
#
|
173
|
-
# @return [String]
|
174
|
-
#
|
175
|
-
# @api private
|
176
|
-
def convert_byte(byte)
|
177
|
-
Array(byte).pack('U*')
|
178
|
-
end
|
179
|
-
|
180
|
-
# Handle single character by appending to or removing from output
|
181
|
-
#
|
182
|
-
# @api private
|
183
|
-
def handle_char(line, char)
|
184
|
-
if char == BACKSPACE || char == DELETE
|
185
|
-
line.empty? ? line : line.slice(-1, 1)
|
186
|
-
else
|
187
|
-
line << char
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
171
|
# Handle input interrupt based on provided value
|
192
172
|
#
|
193
173
|
# @api private
|
data/lib/tty/prompt/version.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
RSpec.describe TTY::Prompt::EnumPaginator, '#paginate' do
|
4
|
+
it "paginates items matching per_page count" do
|
5
|
+
list = %w(a b c d e f)
|
6
|
+
paginator = described_class.new({per_page: 3})
|
7
|
+
|
8
|
+
expect(paginator.paginate(list, 1).to_a).to eq([['a',0], ['b',1], ['c',2]])
|
9
|
+
expect(paginator.paginate(list, 2).to_a).to eq([['a',0], ['b',1], ['c',2]])
|
10
|
+
expect(paginator.paginate(list, 3).to_a).to eq([['a',0], ['b',1], ['c',2]])
|
11
|
+
expect(paginator.paginate(list, 4).to_a).to eq([['d',3], ['e',4], ['f',5]])
|
12
|
+
expect(paginator.paginate(list, 5).to_a).to eq([['d',3], ['e',4], ['f',5]])
|
13
|
+
expect(paginator.paginate(list, 6).to_a).to eq([['d',3], ['e',4], ['f',5]])
|
14
|
+
expect(paginator.paginate(list, 7).to_a).to eq([['d',3], ['e',4], ['f',5]])
|
15
|
+
end
|
16
|
+
|
17
|
+
it "paginates items not matching per_page count" do
|
18
|
+
list = %w(a b c d e f g)
|
19
|
+
paginator = described_class.new({per_page: 3})
|
20
|
+
|
21
|
+
expect(paginator.paginate(list, 1).to_a).to eq([['a',0], ['b',1], ['c',2]])
|
22
|
+
expect(paginator.paginate(list, 2).to_a).to eq([['a',0], ['b',1], ['c',2]])
|
23
|
+
expect(paginator.paginate(list, 3).to_a).to eq([['a',0], ['b',1], ['c',2]])
|
24
|
+
expect(paginator.paginate(list, 4).to_a).to eq([['d',3], ['e',4], ['f',5]])
|
25
|
+
expect(paginator.paginate(list, 5).to_a).to eq([['d',3], ['e',4], ['f',5]])
|
26
|
+
expect(paginator.paginate(list, 6).to_a).to eq([['d',3], ['e',4], ['f',5]])
|
27
|
+
expect(paginator.paginate(list, 7).to_a).to eq([['g',6]])
|
28
|
+
expect(paginator.paginate(list, 8).to_a).to eq([['g',6]])
|
29
|
+
end
|
30
|
+
|
31
|
+
it "finds maximum index for current selection" do
|
32
|
+
list = %w(a b c d e f g)
|
33
|
+
paginator = described_class.new({per_page: 3, default: 0})
|
34
|
+
|
35
|
+
paginator.paginate(list, 4)
|
36
|
+
expect(paginator.max_index).to eq(5)
|
37
|
+
paginator.paginate(list, 5)
|
38
|
+
expect(paginator.max_index).to eq(5)
|
39
|
+
paginator.paginate(list, 7)
|
40
|
+
expect(paginator.max_index).to eq(8)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "starts with default selection" do
|
44
|
+
list = %w(a b c d e f g)
|
45
|
+
paginator = described_class.new({per_page: 3, default: 3})
|
46
|
+
|
47
|
+
expect(paginator.paginate(list, 4).to_a).to eq([['d',3], ['e',4], ['f',5]])
|
48
|
+
end
|
49
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
RSpec.describe TTY::Prompt do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
let(:page_help) {
|
5
|
+
}
|
7
6
|
it "selects default option when return pressed immediately" do
|
7
|
+
prompt = TTY::TestPrompt.new
|
8
8
|
choices = %w(/bin/nano /usr/bin/vim.basic /usr/bin/vim.tiny)
|
9
9
|
prompt.input << "\n"
|
10
10
|
prompt.input.rewind
|
@@ -16,13 +16,13 @@ RSpec.describe TTY::Prompt do
|
|
16
16
|
" 2) /usr/bin/vim.basic\n",
|
17
17
|
" 3) /usr/bin/vim.tiny\n",
|
18
18
|
" Choose 1-3 [1]: ",
|
19
|
-
"\e[1000D\e[K\e[1A" * 4,
|
20
|
-
"\e[1000D\e[K\e[J",
|
19
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
21
20
|
"Select an editor? \e[32m/bin/nano\e[0m\n"
|
22
21
|
].join)
|
23
22
|
end
|
24
23
|
|
25
24
|
it "selects option by index from the list" do
|
25
|
+
prompt = TTY::TestPrompt.new
|
26
26
|
choices = %w(/bin/nano /usr/bin/vim.basic /usr/bin/vim.tiny)
|
27
27
|
prompt.input << "3\n"
|
28
28
|
prompt.input.rewind
|
@@ -34,18 +34,19 @@ RSpec.describe TTY::Prompt do
|
|
34
34
|
"\e[32m 2) /usr/bin/vim.basic\e[0m\n",
|
35
35
|
" 3) /usr/bin/vim.tiny\n",
|
36
36
|
" Choose 1-3 [2]: ",
|
37
|
-
"\e[1000D\e[K\e[1A\e[1000D\e[K\e[
|
37
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
38
38
|
"Select an editor? \n",
|
39
39
|
" 1) /bin/nano\n",
|
40
40
|
" 2) /usr/bin/vim.basic\n",
|
41
41
|
"\e[32m 3) /usr/bin/vim.tiny\e[0m\n",
|
42
42
|
" Choose 1-3 [2]: 3",
|
43
|
-
"\e[1000D\e[K\e[1A\e[1000D\e[K\e[
|
43
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
44
44
|
"Select an editor? \e[32m/usr/bin/vim.tiny\e[0m\n"
|
45
45
|
].join)
|
46
46
|
end
|
47
47
|
|
48
48
|
it "selects option through DSL" do
|
49
|
+
prompt = TTY::TestPrompt.new
|
49
50
|
prompt.input << "\n"
|
50
51
|
prompt.input.rewind
|
51
52
|
value = prompt.enum_select("Select an editor?") do |menu|
|
@@ -70,6 +71,7 @@ RSpec.describe TTY::Prompt do
|
|
70
71
|
end
|
71
72
|
|
72
73
|
it "selects option through DSL with key and value" do
|
74
|
+
prompt = TTY::TestPrompt.new
|
73
75
|
prompt.input << "\n"
|
74
76
|
prompt.input.rewind
|
75
77
|
value = prompt.enum_select("Select an editor?") do |menu|
|
@@ -93,6 +95,7 @@ RSpec.describe TTY::Prompt do
|
|
93
95
|
end
|
94
96
|
|
95
97
|
it "changes colors for selection, hint and error" do
|
98
|
+
prompt = TTY::TestPrompt.new
|
96
99
|
choices = %w(/bin/nano /usr/bin/vim.basic /usr/bin/vim.tiny)
|
97
100
|
prompt.input << "\n"
|
98
101
|
prompt.input.rewind
|
@@ -109,4 +112,155 @@ RSpec.describe TTY::Prompt do
|
|
109
112
|
"Select an editor? \e[31m/bin/nano\e[0m\n"
|
110
113
|
].join)
|
111
114
|
end
|
115
|
+
|
116
|
+
it "displays error with unrecognized input" do
|
117
|
+
prompt = TTY::TestPrompt.new
|
118
|
+
choices = %w(/bin/nano /usr/bin/vim.basic /usr/bin/vim.tiny)
|
119
|
+
prompt.input << "11\n2\n"
|
120
|
+
prompt.input.rewind
|
121
|
+
value = prompt.enum_select("Select an editor?", choices)
|
122
|
+
expect(value).to eq('/usr/bin/vim.basic')
|
123
|
+
expect(prompt.output.string).to eq([
|
124
|
+
"Select an editor? \n",
|
125
|
+
"\e[32m 1) /bin/nano\e[0m\n",
|
126
|
+
" 2) /usr/bin/vim.basic\n",
|
127
|
+
" 3) /usr/bin/vim.tiny\n",
|
128
|
+
" Choose 1-3 [1]: ",
|
129
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
130
|
+
"Select an editor? \n",
|
131
|
+
"\e[32m 1) /bin/nano\e[0m\n",
|
132
|
+
" 2) /usr/bin/vim.basic\n",
|
133
|
+
" 3) /usr/bin/vim.tiny\n",
|
134
|
+
" Choose 1-3 [1]: 1",
|
135
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
136
|
+
"Select an editor? \n",
|
137
|
+
"\e[32m 1) /bin/nano\e[0m\n",
|
138
|
+
" 2) /usr/bin/vim.basic\n",
|
139
|
+
" 3) /usr/bin/vim.tiny\n",
|
140
|
+
" Choose 1-3 [1]: 11",
|
141
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
142
|
+
"Select an editor? \n",
|
143
|
+
"\e[32m 1) /bin/nano\e[0m\n",
|
144
|
+
" 2) /usr/bin/vim.basic\n",
|
145
|
+
" 3) /usr/bin/vim.tiny\n",
|
146
|
+
" Choose 1-3 [1]: \n",
|
147
|
+
"\e[31m>>\e[0m Please enter a valid number",
|
148
|
+
"\e[A\e[1G\e[18C",
|
149
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
150
|
+
"Select an editor? \n",
|
151
|
+
" 1) /bin/nano\n",
|
152
|
+
"\e[32m 2) /usr/bin/vim.basic\e[0m\n",
|
153
|
+
" 3) /usr/bin/vim.tiny\n",
|
154
|
+
" Choose 1-3 [1]: 2\n",
|
155
|
+
"\e[31m>>\e[0m Please enter a valid number",
|
156
|
+
"\e[A\e[1G\e[19C",
|
157
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
158
|
+
"Select an editor? \e[32m/usr/bin/vim.basic\e[0m\n"
|
159
|
+
].join)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "paginates long selections" do
|
163
|
+
prompt = TTY::TestPrompt.new
|
164
|
+
choices = %w(A B C D E F G H)
|
165
|
+
prompt.input << "\n"
|
166
|
+
prompt.input.rewind
|
167
|
+
value = prompt.enum_select("What letter?", choices, per_page: 3, default: 4)
|
168
|
+
expect(value).to eq('D')
|
169
|
+
expect(prompt.output.string).to eq([
|
170
|
+
"What letter? \n",
|
171
|
+
"\e[32m 4) D\e[0m\n",
|
172
|
+
" 5) E\n",
|
173
|
+
" 6) F\n",
|
174
|
+
" Choose 1-8 [4]: ",
|
175
|
+
"\n\e[90m(Press tab/right or left to reveal more choices)\e[0m",
|
176
|
+
"\e[A\e[1G\e[18C",
|
177
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
178
|
+
"What letter? \e[32mD\e[0m\n"
|
179
|
+
].join)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "shows pages matching input" do
|
183
|
+
prompt = TTY::TestPrompt.new
|
184
|
+
choices = %w(A B C D E F G H)
|
185
|
+
prompt.input << "11\n\b\n"
|
186
|
+
prompt.input.rewind
|
187
|
+
value = prompt.enum_select("What letter?", choices, per_page: 3)
|
188
|
+
expect(value).to eq('A')
|
189
|
+
expect(prompt.output.string).to eq([
|
190
|
+
"What letter? \n",
|
191
|
+
"\e[32m 1) A\e[0m\n",
|
192
|
+
" 2) B\n",
|
193
|
+
" 3) C\n",
|
194
|
+
" Choose 1-8 [1]: ",
|
195
|
+
"\n\e[90m(Press tab/right or left to reveal more choices)\e[0m",
|
196
|
+
"\e[A\e[1G\e[18C",
|
197
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
198
|
+
"What letter? \n",
|
199
|
+
"\e[32m 1) A\e[0m\n",
|
200
|
+
" 2) B\n",
|
201
|
+
" 3) C\n",
|
202
|
+
" Choose 1-8 [1]: 1",
|
203
|
+
"\n\e[90m(Press tab/right or left to reveal more choices)\e[0m",
|
204
|
+
"\e[A\e[1G\e[19C",
|
205
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
206
|
+
"What letter? \n",
|
207
|
+
"\e[32m 1) A\e[0m\n",
|
208
|
+
" 2) B\n",
|
209
|
+
" 3) C\n",
|
210
|
+
" Choose 1-8 [1]: 11",
|
211
|
+
"\n\e[90m(Press tab/right or left to reveal more choices)\e[0m",
|
212
|
+
"\e[A\e[1G\e[20C",
|
213
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
214
|
+
"What letter? \n",
|
215
|
+
"\e[32m 1) A\e[0m\n",
|
216
|
+
" 2) B\n",
|
217
|
+
" 3) C\n",
|
218
|
+
" Choose 1-8 [1]: \n",
|
219
|
+
"\e[31m>>\e[0m Please enter a valid number",
|
220
|
+
"\n\e[90m(Press tab/right or left to reveal more choices)\e[0m",
|
221
|
+
"\e[A\e[1G\e[A\e[1G\e[18C",
|
222
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
223
|
+
"What letter? \n",
|
224
|
+
"\e[32m 1) A\e[0m\n",
|
225
|
+
" 2) B\n",
|
226
|
+
" 3) C\n",
|
227
|
+
" Choose 1-8 [1]: \n",
|
228
|
+
"\e[31m>>\e[0m Please enter a valid number",
|
229
|
+
"\n\e[90m(Press tab/right or left to reveal more choices)\e[0m",
|
230
|
+
"\e[A\e[1G\e[A\e[1G\e[18C",
|
231
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
232
|
+
"What letter? \e[32mA\e[0m\n"
|
233
|
+
].join)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "switches through pages with tab key" do
|
237
|
+
prompt = TTY::TestPrompt.new
|
238
|
+
choices = %w(A B C D E F G H)
|
239
|
+
prompt.input << "\t\n"
|
240
|
+
prompt.input.rewind
|
241
|
+
value = prompt.enum_select("What letter?") do |menu|
|
242
|
+
menu.default 4
|
243
|
+
menu.per_page 3
|
244
|
+
menu.choices choices
|
245
|
+
end
|
246
|
+
expect(value).to eq('D')
|
247
|
+
expect(prompt.output.string).to eq([
|
248
|
+
"What letter? \n",
|
249
|
+
"\e[32m 4) D\e[0m\n",
|
250
|
+
" 5) E\n",
|
251
|
+
" 6) F\n",
|
252
|
+
" Choose 1-8 [4]: ",
|
253
|
+
"\n\e[90m(Press tab/right or left to reveal more choices)\e[0m",
|
254
|
+
"\e[A\e[1G\e[18C",
|
255
|
+
"\e[1000D\e[K\e[1A" * 4 + "\e[1000D\e[K\e[J",
|
256
|
+
"What letter? \n",
|
257
|
+
" 7) G\n",
|
258
|
+
" 8) H\n",
|
259
|
+
" Choose 1-8 [4]: ",
|
260
|
+
"\n\e[90m(Press tab/right or left to reveal more choices)\e[0m",
|
261
|
+
"\e[A\e[1G\e[18C",
|
262
|
+
"\e[1000D\e[K\e[1A" * 3 + "\e[1000D\e[K\e[J",
|
263
|
+
"What letter? \e[32mD\e[0m\n"
|
264
|
+
].join)
|
265
|
+
end
|
112
266
|
end
|