tty-prompt 0.18.1 → 0.19.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/README.md +174 -63
  4. data/Rakefile +2 -2
  5. data/examples/ask_blank.rb +9 -0
  6. data/examples/enum_select_disabled.rb +1 -1
  7. data/examples/enum_select_paged.rb +1 -1
  8. data/examples/expand_auto.rb +29 -0
  9. data/examples/mask.rb +1 -1
  10. data/examples/multi_select.rb +1 -1
  11. data/examples/multi_select_disabled_paged.rb +22 -0
  12. data/examples/multi_select_paged.rb +1 -1
  13. data/examples/select_disabled_paged.rb +22 -0
  14. data/examples/select_paginated.rb +1 -1
  15. data/lib/tty/prompt.rb +46 -10
  16. data/lib/tty/prompt/{enum_paginator.rb → block_paginator.rb} +19 -18
  17. data/lib/tty/prompt/choice.rb +1 -3
  18. data/lib/tty/prompt/enum_list.rb +31 -9
  19. data/lib/tty/prompt/expander.rb +19 -1
  20. data/lib/tty/prompt/keypress.rb +30 -35
  21. data/lib/tty/prompt/list.rb +112 -40
  22. data/lib/tty/prompt/mask_question.rb +2 -3
  23. data/lib/tty/prompt/multi_list.rb +36 -12
  24. data/lib/tty/prompt/paginator.rb +37 -25
  25. data/lib/tty/prompt/question.rb +29 -5
  26. data/lib/tty/prompt/slider.rb +16 -8
  27. data/lib/tty/prompt/symbols.rb +30 -6
  28. data/lib/tty/prompt/timer.rb +75 -0
  29. data/lib/tty/prompt/version.rb +1 -1
  30. data/spec/spec_helper.rb +18 -2
  31. data/spec/unit/ask_spec.rb +45 -4
  32. data/spec/unit/{enum_paginator_spec.rb → block_paginator_spec.rb} +14 -5
  33. data/spec/unit/choice/from_spec.rb +16 -0
  34. data/spec/unit/enum_select_spec.rb +104 -32
  35. data/spec/unit/expand_spec.rb +104 -12
  36. data/spec/unit/keypress_spec.rb +2 -8
  37. data/spec/unit/mask_spec.rb +9 -1
  38. data/spec/unit/multi_select_spec.rb +348 -118
  39. data/spec/unit/paginator_spec.rb +29 -10
  40. data/spec/unit/select_spec.rb +390 -108
  41. data/spec/unit/slider_spec.rb +48 -6
  42. data/spec/unit/timer_spec.rb +29 -0
  43. data/tty-prompt.gemspec +4 -6
  44. metadata +17 -46
  45. data/lib/tty/prompt/timeout.rb +0 -78
@@ -23,9 +23,9 @@ RSpec.describe TTY::Prompt::Paginator, '#paginate' do
23
23
 
24
24
  expect(paginator.paginate(list, 1).to_a).to eq([['a',0], ['b',1], ['c',2]])
25
25
  expect(paginator.paginate(list, 2).to_a).to eq([['a',0], ['b',1], ['c',2]])
26
- expect(paginator.paginate(list, 3).to_a).to eq([['a',0], ['b',1], ['c',2]])
27
- expect(paginator.paginate(list, 4).to_a).to eq([['b',1], ['c',2], ['d',3]])
28
- expect(paginator.paginate(list, 5).to_a).to eq([['c',2], ['d',3], ['e',4]])
26
+ expect(paginator.paginate(list, 3).to_a).to eq([['b',1], ['c',2], ['d',3]])
27
+ expect(paginator.paginate(list, 4).to_a).to eq([['c',2], ['d',3], ['e',4]])
28
+ expect(paginator.paginate(list, 5).to_a).to eq([['d',3], ['e',4], ['f',5]])
29
29
  expect(paginator.paginate(list, 6).to_a).to eq([['d',3], ['e',4], ['f',5]])
30
30
  expect(paginator.paginate(list, 7).to_a).to eq([['d',3], ['e',4], ['f',5]])
31
31
  end
@@ -36,22 +36,41 @@ RSpec.describe TTY::Prompt::Paginator, '#paginate' do
36
36
 
37
37
  expect(paginator.paginate(list, 1).to_a).to eq([['a',0], ['b',1], ['c',2]])
38
38
  expect(paginator.paginate(list, 2).to_a).to eq([['a',0], ['b',1], ['c',2]])
39
- expect(paginator.paginate(list, 3).to_a).to eq([['a',0], ['b',1], ['c',2]])
40
- expect(paginator.paginate(list, 4).to_a).to eq([['b',1], ['c',2], ['d',3]])
41
- expect(paginator.paginate(list, 5).to_a).to eq([['c',2], ['d',3], ['e',4]])
42
- expect(paginator.paginate(list, 6).to_a).to eq([['d',3], ['e',4], ['f',5]])
39
+ expect(paginator.paginate(list, 3).to_a).to eq([['b',1], ['c',2], ['d',3]])
40
+ expect(paginator.paginate(list, 4).to_a).to eq([['c',2], ['d',3], ['e',4]])
41
+ expect(paginator.paginate(list, 5).to_a).to eq([['d',3], ['e',4], ['f',5]])
42
+ expect(paginator.paginate(list, 6).to_a).to eq([['e',4], ['f',5], ['g',6]])
43
43
  expect(paginator.paginate(list, 7).to_a).to eq([['e',4], ['f',5], ['g',6]])
44
44
  expect(paginator.paginate(list, 8).to_a).to eq([['e',4], ['f',5], ['g',6]])
45
45
  end
46
46
 
47
- it "finds maximum index for current selection" do
47
+ it "finds both start and end index for current selection" do
48
48
  list = %w(a b c d e f g)
49
49
  paginator = described_class.new({per_page: 3, default: 0})
50
50
 
51
+ paginator.paginate(list, 2)
52
+ expect(paginator.start_index).to eq(0)
53
+ expect(paginator.end_index).to eq(2)
54
+
55
+ paginator.paginate(list, 3)
56
+ expect(paginator.start_index).to eq(1)
57
+ expect(paginator.end_index).to eq(3)
58
+
51
59
  paginator.paginate(list, 4)
52
- expect(paginator.max_index).to eq(3)
60
+ expect(paginator.start_index).to eq(2)
61
+ expect(paginator.end_index).to eq(4)
62
+
53
63
  paginator.paginate(list, 5)
54
- expect(paginator.max_index).to eq(4)
64
+ expect(paginator.start_index).to eq(3)
65
+ expect(paginator.end_index).to eq(5)
66
+
67
+ paginator.paginate(list, 7)
68
+ expect(paginator.start_index).to eq(4)
69
+ expect(paginator.end_index).to eq(6)
70
+
71
+ paginator.paginate(list, 8)
72
+ expect(paginator.start_index).to eq(4)
73
+ expect(paginator.end_index).to eq(6)
55
74
  end
56
75
 
57
76
  it "starts with default selection" do
@@ -5,6 +5,8 @@ RSpec.describe TTY::Prompt, '#select' do
5
5
  subject(:prompt) { TTY::TestPrompt.new }
6
6
 
7
7
  let(:symbols) { TTY::Prompt::Symbols.symbols }
8
+ let(:up_down) { "#{symbols[:arrow_up]}/#{symbols[:arrow_down]}" }
9
+ let(:left_right) { "#{symbols[:arrow_left]}/#{symbols[:arrow_right]}"}
8
10
 
9
11
  def output_helper(prompt, choices, active, options = {})
10
12
  raise ":init requires :hint" if options[:init] && options[:hint].nil?
@@ -24,7 +26,7 @@ RSpec.describe TTY::Prompt, '#select' do
24
26
  if disabled
25
27
  "\e[31m#{symbols[:cross]}\e[0m #{num}#{name} #{disabled}"
26
28
  elsif name == active
27
- "\e[32m#{symbols[:pointer]} #{num}#{name}\e[0m"
29
+ "\e[32m#{symbols[:marker]} #{num}#{name}\e[0m"
28
30
  else
29
31
  " #{num}#{name}"
30
32
  end
@@ -49,8 +51,8 @@ RSpec.describe TTY::Prompt, '#select' do
49
51
 
50
52
  expect(prompt.select('What size?', choices)).to eq(:Large)
51
53
  expected_output = [
52
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
53
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
54
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
55
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
54
56
  " Medium\n",
55
57
  " Small",
56
58
  "\e[2K\e[1G\e[1A" * 3,
@@ -70,15 +72,15 @@ RSpec.describe TTY::Prompt, '#select' do
70
72
  end
71
73
  expect { prompt.select('What size?', choices) }.not_to output.to_stderr
72
74
  expect(prompt.output.string).to eq([
73
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
74
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
75
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
76
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
75
77
  " Medium\n",
76
78
  " Small",
77
79
  "\e[2K\e[1G\e[1A" * 3,
78
80
  "\e[2K\e[1G",
79
81
  "What size? \n",
80
82
  " Large\n",
81
- "\e[32m#{symbols[:pointer]} Medium\e[0m\n",
83
+ "\e[32m#{symbols[:marker]} Medium\e[0m\n",
82
84
  " Small",
83
85
  "\e[2K\e[1G\e[1A" * 3,
84
86
  "\e[2K\e[1G",
@@ -92,8 +94,8 @@ RSpec.describe TTY::Prompt, '#select' do
92
94
  prompt.input.rewind
93
95
  expect(prompt.select('What size?', choices, default: 1)).to eq(1)
94
96
  expect(prompt.output.string).to eq([
95
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
96
- "\e[32m#{symbols[:pointer]} large\e[0m\n",
97
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
98
+ "\e[32m#{symbols[:marker]} large\e[0m\n",
97
99
  " medium\n",
98
100
  " small",
99
101
  "\e[2K\e[1G\e[1A" * 3,
@@ -106,14 +108,16 @@ RSpec.describe TTY::Prompt, '#select' do
106
108
  prompt.input << " "
107
109
  prompt.input.rewind
108
110
  value = prompt.select('What size?') do |menu|
111
+ menu.symbols marker: '>'
112
+
109
113
  menu.choice "Large"
110
114
  menu.choice "Medium"
111
115
  menu.choice "Small"
112
116
  end
113
117
  expect(value).to eq('Large')
114
118
  expect(prompt.output.string).to eq([
115
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
116
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
119
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
120
+ "\e[32m> Large\e[0m\n",
117
121
  " Medium\n",
118
122
  " Small",
119
123
  "\e[2K\e[1G\e[1A" * 3,
@@ -123,6 +127,7 @@ RSpec.describe TTY::Prompt, '#select' do
123
127
  end
124
128
 
125
129
  it "sets choice name & value through DSL" do
130
+ prompt = TTY::TestPrompt.new(symbols: {marker: '>'})
126
131
  prompt.input << " "
127
132
  prompt.input.rewind
128
133
  value = prompt.select('What size?') do |menu|
@@ -132,8 +137,8 @@ RSpec.describe TTY::Prompt, '#select' do
132
137
  end
133
138
  expect(value).to eq(1)
134
139
  expect(prompt.output.string).to eq([
135
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
136
- "\e[32m#{symbols[:pointer]} large\e[0m\n",
140
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
141
+ "\e[32m> large\e[0m\n",
137
142
  " medium\n",
138
143
  " small",
139
144
  "\e[2K\e[1G\e[1A" * 3,
@@ -151,8 +156,8 @@ RSpec.describe TTY::Prompt, '#select' do
151
156
  end
152
157
  expect(value).to eq('Large')
153
158
  expect(prompt.output.string).to eq([
154
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
155
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
159
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
160
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
156
161
  " Medium\n",
157
162
  " Small",
158
163
  "\e[2K\e[1G\e[1A" * 3,
@@ -174,9 +179,9 @@ RSpec.describe TTY::Prompt, '#select' do
174
179
  end
175
180
  expect(value).to eq(2)
176
181
  expect(prompt.output.string).to eq([
177
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
182
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow or number (1-3) keys, press Enter to select)\e[0m\n",
178
183
  " 1. large\n",
179
- "\e[32m#{symbols[:pointer]} 2. medium\e[0m\n",
184
+ "\e[32m#{symbols[:marker]} 2. medium\e[0m\n",
180
185
  " 3. small",
181
186
  "\e[2K\e[1G\e[1A" * 3,
182
187
  "\e[2K\e[1G",
@@ -192,11 +197,12 @@ RSpec.describe TTY::Prompt, '#select' do
192
197
  menu.choice :medium do 'Good choice!' end
193
198
  menu.choice :small, 3
194
199
  end
200
+
195
201
  expect(value).to eq('Good choice!')
196
202
  expect(prompt.output.string).to eq([
197
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
203
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow or number (1-3) keys, press Enter to select)\e[0m\n",
198
204
  " 1) large\n",
199
- "\e[32m#{symbols[:pointer]} 2) medium\e[0m\n",
205
+ "\e[32m#{symbols[:marker]} 2) medium\e[0m\n",
200
206
  " 3) small",
201
207
  "\e[2K\e[1G\e[1A" * 3,
202
208
  "\e[2K\e[1G",
@@ -210,9 +216,9 @@ RSpec.describe TTY::Prompt, '#select' do
210
216
  prompt.input.rewind
211
217
  expect(prompt.select('What size?', choices, default: 2, enum: '.')).to eq('Medium')
212
218
  expect(prompt.output.string).to eq([
213
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
219
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow or number (1-3) keys, press Enter to select)\e[0m\n",
214
220
  " 1. Large\n",
215
- "\e[32m#{symbols[:pointer]} 2. Medium\e[0m\n",
221
+ "\e[32m#{symbols[:marker]} 2. Medium\e[0m\n",
216
222
  " 3. Small",
217
223
  "\e[2K\e[1G\e[1A" * 3,
218
224
  "\e[2K\e[1G",
@@ -222,13 +228,16 @@ RSpec.describe TTY::Prompt, '#select' do
222
228
 
223
229
  it "changes selected item color & marker" do
224
230
  choices = %w(Large Medium Small)
231
+ prompt = TTY::TestPrompt.new(symbols: {marker: '>'})
225
232
  prompt.input << " "
226
233
  prompt.input.rewind
227
- options = {active_color: :blue, help_color: :red, marker: '>'}
234
+ options = {active_color: :blue, help_color: :red, symbols: {marker: '>' }}
235
+
228
236
  value = prompt.select('What size?', choices, options)
237
+
229
238
  expect(value).to eq('Large')
230
239
  expect(prompt.output.string).to eq([
231
- "\e[?25lWhat size? \e[31m(Use arrow keys, press Enter to select)\e[0m\n",
240
+ "\e[?25lWhat size? \e[31m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
232
241
  "\e[34m> Large\e[0m\n",
233
242
  " Medium\n",
234
243
  " Small",
@@ -246,7 +255,7 @@ RSpec.describe TTY::Prompt, '#select' do
246
255
  expect(value).to eq('Large')
247
256
  expect(prompt.output.string).to eq([
248
257
  "\e[?25lWhat size? \e[90m(Bash keyboard)\e[0m\n",
249
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
258
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
250
259
  " Medium\n",
251
260
  " Small",
252
261
  "\e[2K\e[1G\e[1A" * 3,
@@ -266,7 +275,7 @@ RSpec.describe TTY::Prompt, '#select' do
266
275
  expect(value).to eq('Large')
267
276
  expect(prompt.output.string).to eq([
268
277
  "\e[?25lWhat size? \e[90m(Bash keyboard)\e[0m\n",
269
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
278
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
270
279
  " Medium\n",
271
280
  " Small",
272
281
  "\e[2K\e[1G\e[1A" * 3,
@@ -282,8 +291,8 @@ RSpec.describe TTY::Prompt, '#select' do
282
291
  prompt.input.rewind
283
292
  expect(prompt.select('What size?', choices)).to eq('Large')
284
293
  expect(prompt.output.string).to eq([
285
- "\e[?25l[?] What size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
286
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
294
+ "\e[?25l[?] What size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
295
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
287
296
  " Medium\n",
288
297
  " Small",
289
298
  "\e[2K\e[1G\e[1A" * 3,
@@ -292,66 +301,258 @@ RSpec.describe TTY::Prompt, '#select' do
292
301
  ].join)
293
302
  end
294
303
 
295
- it "paginates long selections" do
296
- choices = %w(A B C D E F G H)
297
- prompt.input << "\r"
298
- prompt.input.rewind
299
- value = prompt.select("What letter?", choices, per_page: 3, default: 4)
300
- expect(value).to eq('D')
301
- expect(prompt.output.string).to eq([
302
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
303
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
304
- " E\n",
305
- " F\n",
306
- "\e[90m(Move up or down to reveal more choices)\e[0m",
307
- "\e[2K\e[1G\e[1A" * 4,
308
- "\e[2K\e[1G",
309
- "What letter? \e[32mD\e[0m\n\e[?25h",
310
- ].join)
311
- end
304
+ context "when paginated" do
305
+ it "paginates long selections" do
306
+ choices = %w(A B C D E F G H)
307
+ prompt.input << "\r"
308
+ prompt.input.rewind
312
309
 
313
- it "paginates choices as hash object" do
314
- prompt = TTY::TestPrompt.new
315
- choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8}
316
- prompt.input << "\r"
317
- prompt.input.rewind
318
- value = prompt.select("What letter?", choices, per_page: 3, default: 4)
319
- expect(value).to eq(4)
320
- expect(prompt.output.string).to eq([
321
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
322
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
323
- " E\n",
324
- " F\n",
325
- "\e[90m(Move up or down to reveal more choices)\e[0m",
326
- "\e[2K\e[1G\e[1A" * 4,
327
- "\e[2K\e[1G",
328
- "What letter? \e[32mD\e[0m\n\e[?25h",
329
- ].join)
330
- end
310
+ answer = prompt.select("What letter?", choices, per_page: 3, default: 4)
331
311
 
332
- it "paginates long selections through DSL" do
333
- prompt = TTY::TestPrompt.new
334
- choices = %w(A B C D E F G H)
335
- prompt.input << "\r"
336
- prompt.input.rewind
337
- value = prompt.select("What letter?") do |menu|
338
- menu.per_page 3
339
- menu.page_help '(Wiggle thy finger up or down to see more)'
340
- menu.default 4
312
+ expect(answer).to eq('D')
313
+ expected_output = [
314
+ "\e[?25lWhat letter? \e[90m(Use #{up_down} and #{left_right} arrow keys, press Enter to select)\e[0m\n",
315
+ "\e[32m#{symbols[:marker]} D\e[0m\n",
316
+ " E\n",
317
+ " F",
318
+ "\e[2K\e[1G\e[1A" * 3,
319
+ "\e[2K\e[1G",
320
+ "What letter? \e[32mD\e[0m\n\e[?25h",
321
+ ].join
341
322
 
342
- menu.choices choices
343
- end
344
- expect(value).to eq('D')
345
- expect(prompt.output.string).to eq([
346
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
347
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
348
- " E\n",
349
- " F\n",
350
- "\e[90m(Wiggle thy finger up or down to see more)\e[0m",
351
- "\e[2K\e[1G\e[1A" * 4,
352
- "\e[2K\e[1G",
353
- "What letter? \e[32mD\e[0m\n\e[?25h",
354
- ].join)
323
+ expect(prompt.output.string).to eq(expected_output)
324
+ end
325
+
326
+ it "paginates choices as hash object" do
327
+ prompt = TTY::TestPrompt.new
328
+ choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8}
329
+ prompt.input << "\r"
330
+ prompt.input.rewind
331
+
332
+ answer = prompt.select("What letter?", choices, per_page: 3, default: 4)
333
+
334
+ expect(answer).to eq(4)
335
+ expected_output = [
336
+ "\e[?25lWhat letter? \e[90m(Use #{up_down} and #{left_right} arrow keys, press Enter to select)\e[0m\n",
337
+ "\e[32m#{symbols[:marker]} D\e[0m\n",
338
+ " E\n",
339
+ " F",
340
+ "\e[2K\e[1G\e[1A" * 3,
341
+ "\e[2K\e[1G",
342
+ "What letter? \e[32mD\e[0m\n\e[?25h",
343
+ ].join
344
+ expect(prompt.output.string).to eq(expected_output)
345
+ end
346
+
347
+ it "paginates long selections through DSL" do
348
+ prompt = TTY::TestPrompt.new
349
+ choices = %w(A B C D E F G H)
350
+ prompt.input << "\r"
351
+ prompt.input.rewind
352
+
353
+ answer = prompt.select("What letter?") do |menu|
354
+ menu.per_page 3
355
+ menu.default 4
356
+
357
+ menu.choices choices
358
+ end
359
+
360
+ expect(answer).to eq('D')
361
+
362
+
363
+ expected_output = [
364
+ "\e[?25lWhat letter? \e[90m(Use #{up_down} and #{left_right} arrow keys, press Enter to select)\e[0m\n",
365
+ "\e[32m#{symbols[:marker]} D\e[0m\n",
366
+ " E\n",
367
+ " F",
368
+ "\e[2K\e[1G\e[1A" * 3,
369
+ "\e[2K\e[1G",
370
+ "What letter? \e[32mD\e[0m\n\e[?25h",
371
+ ].join
372
+ expect(prompt.output.string).to eq(expected_output)
373
+ end
374
+
375
+ it "navigates evenly paged output with right arrow until end of selection" do
376
+ prompt = TTY::TestPrompt.new
377
+ choices = ('1'..'12').to_a
378
+ prompt.on(:keypress) { |e| prompt.trigger(:keyright) if e.value == "l" }
379
+ prompt.input << "l" << "l" << "l" << "\r"
380
+ prompt.input.rewind
381
+
382
+ answer = prompt.select("What number?", choices, per_page: 4)
383
+
384
+ expect(answer).to eq('9')
385
+
386
+ expected_output = [
387
+ output_helper('What number?', choices[0..3], "1", init: true,
388
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
389
+ output_helper('What number?', choices[4..7], "5"),
390
+ output_helper('What number?', choices[8..11], "9"),
391
+ output_helper('What number?', choices[8..11], "9"),
392
+ "What number? \e[32m9\e[0m\n\e[?25h",
393
+ ].join
394
+
395
+ expect(prompt.output.string).to eq(expected_output)
396
+ end
397
+
398
+ it "navigates unevenly paged output with right arrow until the end of selection" do
399
+ prompt = TTY::TestPrompt.new
400
+ choices = ('1'..'10').to_a
401
+ prompt.on(:keypress) { |e| prompt.trigger(:keyright) if e.value == "l" }
402
+ prompt.input << "l" << "l" << "l" << "\r"
403
+ prompt.input.rewind
404
+
405
+ answer = prompt.select("What number?", choices, default: 4, per_page: 4)
406
+
407
+ expect(answer).to eq('10')
408
+
409
+ expected_output = [
410
+ output_helper('What number?', choices[3..6], "4", init: true,
411
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
412
+ output_helper('What number?', choices[4..7], "8"),
413
+ output_helper('What number?', choices[8..9], "10"),
414
+ output_helper('What number?', choices[8..9], "10"),
415
+ "What number? \e[32m10\e[0m\n\e[?25h",
416
+ ].join
417
+
418
+ expect(prompt.output.string).to eq(expected_output)
419
+ end
420
+
421
+ it "navigates left and right" do
422
+ prompt = TTY::TestPrompt.new
423
+ choices = ('1'..'10').to_a
424
+ prompt.on(:keypress) { |e|
425
+ prompt.trigger(:keyright) if e.value == "l"
426
+ prompt.trigger(:keyleft) if e.value == "h"
427
+ }
428
+ prompt.input << "l" << "l" << "h" << "\r"
429
+ prompt.input.rewind
430
+
431
+ answer = prompt.select("What number?", choices, default: 2, per_page: 4)
432
+
433
+ expect(answer).to eq('6')
434
+
435
+ expected_output = [
436
+ output_helper('What number?', choices[0..3], "2", init: true,
437
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
438
+ output_helper('What number?', choices[4..7], "6"),
439
+ output_helper('What number?', choices[8..9], "10"),
440
+ output_helper('What number?', choices[4..7], "6"),
441
+ "What number? \e[32m6\e[0m\n\e[?25h",
442
+ ].join
443
+
444
+ expect(prompt.output.string).to eq(expected_output)
445
+ end
446
+
447
+ it "combines up/down navigation with left/right" do
448
+ prompt = TTY::TestPrompt.new
449
+ choices = ('1'..'11').to_a
450
+ prompt.on(:keypress) { |e|
451
+ prompt.trigger(:keyup) if e.value == "k"
452
+ prompt.trigger(:keydown) if e.value == "j"
453
+ prompt.trigger(:keyright) if e.value == "l"
454
+ prompt.trigger(:keyleft) if e.value == "h"
455
+ }
456
+ prompt.input << "j" << "l" << "k" << "k" << "h" << "\r"
457
+ prompt.input.rewind
458
+
459
+ answer = prompt.select("What number?", choices, default: 2, per_page: 4)
460
+
461
+ expect(answer).to eq('1')
462
+
463
+ expected_output = [
464
+ output_helper('What number?', choices[0..3], "2", init: true,
465
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
466
+ output_helper('What number?', choices[0..3], "3"),
467
+ output_helper('What number?', choices[4..7], "7"),
468
+ output_helper('What number?', choices[4..7], "6"),
469
+ output_helper('What number?', choices[3..6], "5"),
470
+ output_helper('What number?', choices[0..3], "1"),
471
+ "What number? \e[32m1\e[0m\n\e[?25h"
472
+ ].join
473
+
474
+ expect(prompt.output.string).to eq(expected_output)
475
+ end
476
+
477
+ it "navigates pages up/down with disabled items" do
478
+ prompt = TTY::TestPrompt.new
479
+ prompt.on(:keypress) { |e|
480
+ prompt.trigger(:keyup) if e.value == "k"
481
+ prompt.trigger(:keydown) if e.value == "j"
482
+ }
483
+ choices = [
484
+ '1',
485
+ {name: '2', disabled: 'out'},
486
+ '3',
487
+ {name: '4', disabled: 'out'},
488
+ '5',
489
+ {name: '6', disabled: 'out'},
490
+ {name: '7', disabled: 'out'},
491
+ '8',
492
+ '9',
493
+ {name: '10', disabled: 'out'},
494
+ ]
495
+
496
+ prompt.input << "j" << "j" << "j" << "j" << "\r"
497
+ prompt.input.rewind
498
+
499
+ answer = prompt.select("What number?", choices, per_page: 4)
500
+
501
+ expect(answer).to eq('9')
502
+
503
+ expected_output = [
504
+ output_helper('What number?', choices[0..3], "1", init: true,
505
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
506
+ output_helper('What number?', choices[0..3], "3"),
507
+ output_helper('What number?', choices[2..5], "5"),
508
+ output_helper('What number?', choices[5..8], "8"),
509
+ output_helper('What number?', choices[6..9], "9"),
510
+ "What number? \e[32m9\e[0m\n\e[?25h"
511
+ ].join('')
512
+
513
+ expect(prompt.output.string).to eq(expected_output)
514
+ end
515
+
516
+ it "navigates pages left/right with disabled items" do
517
+ prompt = TTY::TestPrompt.new
518
+ prompt.on(:keypress) { |e|
519
+ prompt.trigger(:keyright) if e.value == "l"
520
+ prompt.trigger(:keyleft) if e.value == "h"
521
+ }
522
+ choices = [
523
+ {name: '1', disabled: 'out'},
524
+ '2',
525
+ {name: '3', disabled: 'out'},
526
+ '4',
527
+ '5',
528
+ {name: '6', disabled: 'out'},
529
+ '7',
530
+ '8',
531
+ '9',
532
+ {name: '10', disabled: 'out'}
533
+ ]
534
+
535
+ prompt.input << "l" << "l" << "l" << "h" << "h" << "h" << "\r"
536
+ prompt.input.rewind
537
+
538
+ answer = prompt.select("What number?", choices, per_page: 4)
539
+
540
+ expect(answer).to eq('2')
541
+
542
+ expected_output = [
543
+ output_helper('What number?', choices[0..3], "2", init: true,
544
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
545
+ output_helper('What number?', choices[4..7], "7"),
546
+ output_helper('What number?', choices[8..9], "9"),
547
+ output_helper('What number?', choices[8..9], "9"),
548
+ output_helper('What number?', choices[4..7], "5"),
549
+ output_helper('What number?', choices[0..3], "2"),
550
+ output_helper('What number?', choices[0..3], "2"),
551
+ "What number? \e[32m2\e[0m\n\e[?25h"
552
+ ].join('')
553
+
554
+ expect(prompt.output.string).to eq(expected_output)
555
+ end
355
556
  end
356
557
 
357
558
  context 'with :cycle option' do
@@ -361,15 +562,19 @@ RSpec.describe TTY::Prompt, '#select' do
361
562
  prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
362
563
  prompt.input << "j" << "j" << "j" << "\r"
363
564
  prompt.input.rewind
565
+
364
566
  value = prompt.select("What letter?", choices)
567
+
365
568
  expect(value).to eq("C")
366
- expect(prompt.output.string).to eq(
367
- output_helper("What letter?", choices, "A", init: true, hint: "Use arrow keys, press Enter to select") +
368
- output_helper("What letter?", choices, "B") +
369
- output_helper("What letter?", choices, "C") +
370
- output_helper("What letter?", choices, "C") +
569
+ expected_output = [
570
+ output_helper("What letter?", choices, "A", init: true,
571
+ hint: "Use #{up_down} arrow keys, press Enter to select"),
572
+ output_helper("What letter?", choices, "B"),
573
+ output_helper("What letter?", choices, "C"),
574
+ output_helper("What letter?", choices, "C"),
371
575
  "What letter? \e[32mC\e[0m\n\e[?25h"
372
- )
576
+ ].join
577
+ expect(prompt.output.string).to eq(expected_output)
373
578
  end
374
579
 
375
580
  it "cycles around when configured to do so" do
@@ -378,15 +583,19 @@ RSpec.describe TTY::Prompt, '#select' do
378
583
  prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
379
584
  prompt.input << "j" << "j" << "j" << "\r"
380
585
  prompt.input.rewind
381
- value = prompt.select("What letter?", choices, cycle: true)
382
- expect(value).to eq("A")
383
- expect(prompt.output.string).to eq(
384
- output_helper("What letter?", choices, "A", init: true, hint: "Use arrow keys, press Enter to select") +
385
- output_helper("What letter?", choices, "B") +
386
- output_helper("What letter?", choices, "C") +
387
- output_helper("What letter?", choices, "A") +
586
+
587
+ answer = prompt.select("What letter?", choices, cycle: true)
588
+
589
+ expect(answer).to eq("A")
590
+ expected_output = [
591
+ output_helper("What letter?", choices, "A", init: true,
592
+ hint: "Use #{up_down} arrow keys, press Enter to select"),
593
+ output_helper("What letter?", choices, "B"),
594
+ output_helper("What letter?", choices, "C"),
595
+ output_helper("What letter?", choices, "A"),
388
596
  "What letter? \e[32mA\e[0m\n\e[?25h"
389
- )
597
+ ].join
598
+ expect(prompt.output.string).to eq(expected_output)
390
599
  end
391
600
 
392
601
  it "cycles around disabled items" do
@@ -406,7 +615,7 @@ RSpec.describe TTY::Prompt, '#select' do
406
615
 
407
616
  expected_output =
408
617
  output_helper("What letter?", choices, "B", init: true,
409
- hint: "Use arrow keys, press Enter to select") +
618
+ hint: "Use #{up_down} arrow keys, press Enter to select") +
410
619
  output_helper("What letter?", choices, "D") +
411
620
  output_helper("What letter?", choices, "B") +
412
621
  output_helper("What letter?", choices, "D") +
@@ -414,6 +623,74 @@ RSpec.describe TTY::Prompt, '#select' do
414
623
 
415
624
  expect(prompt.output.string).to eq(expected_output)
416
625
  end
626
+
627
+ it "cycles choices using left/right arrows" do
628
+ prompt = TTY::TestPrompt.new
629
+ choices = ('1'..'10').to_a
630
+ prompt.on(:keypress) { |e|
631
+ prompt.trigger(:keyright) if e.value == "l"
632
+ prompt.trigger(:keyleft) if e.value == "h"
633
+ }
634
+ prompt.input << "l" << "l" << "l" << "h" << "\r"
635
+ prompt.input.rewind
636
+
637
+ answer = prompt.select("What number?", choices, default: 2, per_page: 4, cycle: true)
638
+
639
+ expect(answer).to eq('10')
640
+
641
+ expected_output = [
642
+ output_helper('What number?', choices[0..3], "2", init: true,
643
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
644
+ output_helper('What number?', choices[4..7], "6"),
645
+ output_helper('What number?', choices[8..9], "10"),
646
+ output_helper('What number?', choices[0..3], "2"),
647
+ output_helper('What number?', choices[8..9], "10"),
648
+ "What number? \e[32m10\e[0m\n\e[?25h",
649
+ ].join
650
+
651
+ expect(prompt.output.string).to eq(expected_output)
652
+ end
653
+
654
+ it "cycles pages left/right with disabled items" do
655
+ prompt = TTY::TestPrompt.new
656
+ prompt.on(:keypress) { |e|
657
+ prompt.trigger(:keyright) if e.value == "l"
658
+ prompt.trigger(:keyleft) if e.value == "h"
659
+ }
660
+ choices = [
661
+ {name: '1', disabled: 'out'},
662
+ '2',
663
+ {name: '3', disabled: 'out'},
664
+ '4',
665
+ '5',
666
+ {name: '6', disabled: 'out'},
667
+ '7',
668
+ '8',
669
+ '9',
670
+ {name: '10', disabled: 'out'}
671
+ ]
672
+
673
+ prompt.input << "l" << "l" << "l" << "h" << "h" << "h" << "\r"
674
+ prompt.input.rewind
675
+
676
+ answer = prompt.select("What number?", choices, per_page: 4, cycle: true)
677
+
678
+ expect(answer).to eq('2')
679
+
680
+ expected_output = [
681
+ output_helper('What number?', choices[0..3], "2", init: true,
682
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
683
+ output_helper('What number?', choices[4..7], "7"),
684
+ output_helper('What number?', choices[8..9], "9"),
685
+ output_helper('What number?', choices[0..3], "2"),
686
+ output_helper('What number?', choices[8..9], "9"),
687
+ output_helper('What number?', choices[4..7], "5"),
688
+ output_helper('What number?', choices[0..3], "2"),
689
+ "What number? \e[32m2\e[0m\n\e[?25h"
690
+ ].join('')
691
+
692
+ expect(prompt.output.string).to eq(expected_output)
693
+ end
417
694
  end
418
695
 
419
696
  it "verifies default index format" do
@@ -436,8 +713,8 @@ RSpec.describe TTY::Prompt, '#select' do
436
713
  expect(value).to eq('A')
437
714
 
438
715
  expect(prompt.output.string).to eq([
439
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
440
- "\e[32m#{symbols[:pointer]} A\e[0m\n",
716
+ "\e[?25lWhat letter? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
717
+ "\e[32m#{symbols[:marker]} A\e[0m\n",
441
718
  " B\n",
442
719
  " C\n",
443
720
  " D",
@@ -478,7 +755,8 @@ RSpec.describe TTY::Prompt, '#select' do
478
755
  actual_prompt_output = prompt.output.string
479
756
 
480
757
  expected_prompt_output =
481
- output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
758
+ output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true,
759
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
482
760
  output_helper("What size?", %w(Medium Huge), "Medium", hint: 'Filter: "U"') +
483
761
  output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "Ug"') +
484
762
  exit_message("What size?", "Huge")
@@ -496,7 +774,8 @@ RSpec.describe TTY::Prompt, '#select' do
496
774
 
497
775
  actual_prompt_output = prompt.output.string
498
776
  expected_prompt_output =
499
- output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
777
+ output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true,
778
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
500
779
  output_helper("What size?", %w(Large Huge), "Large", hint: 'Filter: "g"') +
501
780
  exit_message("What size?", "Large")
502
781
 
@@ -513,7 +792,8 @@ RSpec.describe TTY::Prompt, '#select' do
513
792
 
514
793
  actual_prompt_output = prompt.output.string
515
794
  expected_prompt_output =
516
- output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
795
+ output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", init: true,
796
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
517
797
  output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", hint: 'Filter: "p"') +
518
798
  output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", hint: 'Filter: "p*"') +
519
799
  output_helper("What email?", %w(p*2@mail.com), "p*2@mail.com", hint: 'Filter: "p*2"') +
@@ -535,7 +815,8 @@ RSpec.describe TTY::Prompt, '#select' do
535
815
 
536
816
  actual_prompt_output = prompt.output.string
537
817
  expected_prompt_output =
538
- output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
818
+ output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", init: true,
819
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
539
820
  output_helper("What size?", %w(), "", hint: 'Filter: "z"') +
540
821
  output_helper("What size?", %w(), "", hint: 'Filter: "z"') +
541
822
  output_helper("What size?", %w(Large), "Large", hint: 'Filter: "a"') +
@@ -556,7 +837,8 @@ RSpec.describe TTY::Prompt, '#select' do
556
837
  expect(answer).to eql("Small")
557
838
 
558
839
  expected_prompt_output =
559
- output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
840
+ output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true,
841
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
560
842
  output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "H"') +
561
843
  output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "Hu"') +
562
844
  output_helper("What size?", %w(Small), "Small", hint: 'Filter: "S"') +
@@ -579,7 +861,7 @@ RSpec.describe TTY::Prompt, '#select' do
579
861
 
580
862
  expected_output =
581
863
  output_helper("What size?", choices, "Small", init: true,
582
- hint: "Use arrow keys, press Enter to select") +
864
+ hint: "Use #{up_down} arrow keys, press Enter to select") +
583
865
  output_helper("What size?", choices, "Medium") +
584
866
  output_helper("What size?", choices, "Huge") +
585
867
  "What size? \e[32mHuge\e[0m\n\e[?25h"
@@ -600,7 +882,7 @@ RSpec.describe TTY::Prompt, '#select' do
600
882
 
601
883
  expected_output =
602
884
  output_helper("What letter?", choices, "A", init: true,
603
- hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
885
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
604
886
  output_helper("What letter?", [], "", hint: 'Filter: "c"') +
605
887
  output_helper("What letter?", [], "", hint: 'Filter: "c"') +
606
888
  output_helper("What letter?", ['A'], "A", hint: 'Filter: "a"') +
@@ -625,7 +907,7 @@ RSpec.describe TTY::Prompt, '#select' do
625
907
 
626
908
  expected_output =
627
909
  output_helper("What size?", choices, "Small", init: true, enum: ') ',
628
- hint: "Use arrow or number (1-3) keys, press Enter to select") +
910
+ hint: "Use #{up_down} arrow or number (1-3) keys, press Enter to select") +
629
911
  output_helper("What size?", choices, "Small", enum: ') ') +
630
912
  "What size? \e[32mSmall\e[0m\n\e[?25h"
631
913