tty-prompt 0.8.0 → 0.9.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 +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
|