tty-prompt 0.11.0 → 0.12.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 +25 -0
- data/README.md +66 -7
- data/examples/key_events.rb +11 -0
- data/examples/keypress.rb +3 -5
- data/examples/multiline.rb +9 -0
- data/examples/pause.rb +7 -0
- data/lib/tty/prompt.rb +82 -44
- data/lib/tty/prompt/confirm_question.rb +20 -36
- data/lib/tty/prompt/enum_list.rb +32 -23
- data/lib/tty/prompt/expander.rb +35 -31
- data/lib/tty/prompt/keypress.rb +91 -0
- data/lib/tty/prompt/list.rb +38 -23
- data/lib/tty/prompt/mask_question.rb +4 -7
- data/lib/tty/prompt/multi_list.rb +3 -1
- data/lib/tty/prompt/multiline.rb +71 -0
- data/lib/tty/prompt/question.rb +33 -35
- data/lib/tty/prompt/reader.rb +154 -38
- data/lib/tty/prompt/reader/codes.rb +4 -4
- data/lib/tty/prompt/reader/console.rb +1 -1
- data/lib/tty/prompt/reader/history.rb +145 -0
- data/lib/tty/prompt/reader/key_event.rb +4 -0
- data/lib/tty/prompt/reader/line.rb +162 -0
- data/lib/tty/prompt/reader/mode.rb +2 -2
- data/lib/tty/prompt/reader/win_console.rb +5 -1
- data/lib/tty/prompt/slider.rb +18 -12
- data/lib/tty/prompt/timeout.rb +48 -0
- data/lib/tty/prompt/version.rb +1 -1
- data/spec/unit/ask_spec.rb +15 -0
- data/spec/unit/converters/convert_bool_spec.rb +1 -0
- data/spec/unit/keypress_spec.rb +35 -6
- data/spec/unit/multi_select_spec.rb +18 -0
- data/spec/unit/multiline_spec.rb +67 -9
- data/spec/unit/question/default_spec.rb +1 -0
- data/spec/unit/question/echo_spec.rb +8 -0
- data/spec/unit/question/in_spec.rb +13 -0
- data/spec/unit/question/required_spec.rb +31 -2
- data/spec/unit/question/validate_spec.rb +39 -9
- data/spec/unit/reader/history_spec.rb +172 -0
- data/spec/unit/reader/key_event_spec.rb +12 -8
- data/spec/unit/reader/line_spec.rb +110 -0
- data/spec/unit/reader/publish_keypress_event_spec.rb +11 -0
- data/spec/unit/reader/read_line_spec.rb +32 -2
- data/spec/unit/reader/read_multiline_spec.rb +21 -7
- data/spec/unit/select_spec.rb +40 -1
- data/spec/unit/yes_no_spec.rb +48 -4
- metadata +14 -3
- data/lib/tty/prompt/history.rb +0 -16
@@ -0,0 +1,172 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
RSpec.describe TTY::Prompt::Reader::History do
|
4
|
+
it "has no lines" do
|
5
|
+
history = described_class.new
|
6
|
+
expect(history.size).to eq(0)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "doesn't navigate through empty buffer" do
|
10
|
+
history = described_class.new
|
11
|
+
expect(history.next?).to eq(false)
|
12
|
+
expect(history.previous?).to eq(false)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "allows to cycle through non-empty buffer" do
|
16
|
+
history = described_class.new(3, {cycle: true})
|
17
|
+
history << "line"
|
18
|
+
expect(history.next?).to eq(true)
|
19
|
+
expect(history.previous?).to eq(true)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "defaults maximum size" do
|
23
|
+
history = described_class.new
|
24
|
+
expect(history.max_size).to eq(512)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "presents string representation" do
|
28
|
+
history = described_class.new
|
29
|
+
expect(history.to_s).to eq("[]")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "adds items to history without overflowing" do
|
33
|
+
history = described_class.new(3)
|
34
|
+
history << "line #1"
|
35
|
+
history << "line #2"
|
36
|
+
history << "line #3"
|
37
|
+
history << "line #4"
|
38
|
+
|
39
|
+
expect(history.to_a).to eq(["line #2", "line #3", "line #4"])
|
40
|
+
expect(history.index).to eq(2)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "excludes items" do
|
44
|
+
exclude = proc { |line| /line #[23]/.match(line) }
|
45
|
+
history = described_class.new(3, exclude: exclude)
|
46
|
+
history << "line #1"
|
47
|
+
history << "line #2"
|
48
|
+
history << "line #3"
|
49
|
+
|
50
|
+
expect(history.to_a).to eq(["line #1"])
|
51
|
+
expect(history.index).to eq(0)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "allows duplicates" do
|
55
|
+
history = described_class.new(3)
|
56
|
+
history << "line #1"
|
57
|
+
history << "line #1"
|
58
|
+
history << "line #1"
|
59
|
+
|
60
|
+
expect(history.to_a).to eq(["line #1", "line #1", "line #1"])
|
61
|
+
end
|
62
|
+
|
63
|
+
it "prevents duplicates" do
|
64
|
+
history = described_class.new(3, duplicates: false)
|
65
|
+
history << "line #1"
|
66
|
+
history << "line #1"
|
67
|
+
history << "line #1"
|
68
|
+
|
69
|
+
expect(history.to_a).to eq(["line #1"])
|
70
|
+
end
|
71
|
+
|
72
|
+
it "navigates through history buffer without cycling" do
|
73
|
+
history = described_class.new(3)
|
74
|
+
history << "line #1"
|
75
|
+
history << "line #2"
|
76
|
+
history << "line #3"
|
77
|
+
|
78
|
+
expect(history.index).to eq(2)
|
79
|
+
history.previous
|
80
|
+
history.previous
|
81
|
+
expect(history.index).to eq(0)
|
82
|
+
history.previous
|
83
|
+
expect(history.index).to eq(0)
|
84
|
+
history.next
|
85
|
+
history.next
|
86
|
+
expect(history.index).to eq(2)
|
87
|
+
history.next
|
88
|
+
expect(history.next?).to eq(false)
|
89
|
+
expect(history.index).to eq(2)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "navigates through history buffer with cycling" do
|
93
|
+
history = described_class.new(3, cycle: true)
|
94
|
+
history << "line #1"
|
95
|
+
history << "line #2"
|
96
|
+
history << "line #3"
|
97
|
+
|
98
|
+
expect(history.index).to eq(2)
|
99
|
+
history.previous
|
100
|
+
history.previous
|
101
|
+
expect(history.index).to eq(0)
|
102
|
+
history.previous
|
103
|
+
expect(history.index).to eq(2)
|
104
|
+
expect(history.next?).to eq(true)
|
105
|
+
history.next
|
106
|
+
history.next
|
107
|
+
expect(history.index).to eq(1)
|
108
|
+
history.next
|
109
|
+
expect(history.index).to eq(2)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "checks if navigation is possible" do
|
113
|
+
history = described_class.new(3)
|
114
|
+
expect(history.previous?).to eq(false)
|
115
|
+
expect(history.next?).to eq(false)
|
116
|
+
|
117
|
+
history << "line #1"
|
118
|
+
history << "line #2"
|
119
|
+
expect(history.previous?).to eq(true)
|
120
|
+
expect(history.next?).to eq(false)
|
121
|
+
|
122
|
+
history.previous
|
123
|
+
expect(history.previous?).to eq(true)
|
124
|
+
expect(history.next?).to eq(true)
|
125
|
+
|
126
|
+
history.previous
|
127
|
+
expect(history.previous?).to eq(true)
|
128
|
+
expect(history.next?).to eq(true)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "gets line based on index" do
|
132
|
+
history = described_class.new(3, cycle: true)
|
133
|
+
history << "line #1"
|
134
|
+
history << "line #2"
|
135
|
+
history << "line #3"
|
136
|
+
|
137
|
+
expect(history[-1]).to eq('line #3')
|
138
|
+
expect(history[1]).to eq('line #2')
|
139
|
+
expect {
|
140
|
+
history[11]
|
141
|
+
}.to raise_error(IndexError, 'invalid index')
|
142
|
+
end
|
143
|
+
|
144
|
+
it "retrieves current line" do
|
145
|
+
history = described_class.new(3, cycle: true)
|
146
|
+
expect(history.get).to eq(nil)
|
147
|
+
|
148
|
+
history << "line #1"
|
149
|
+
history << "line #2"
|
150
|
+
history << "line #3"
|
151
|
+
|
152
|
+
expect(history.get).to eq("line #3")
|
153
|
+
history.previous
|
154
|
+
history.previous
|
155
|
+
expect(history.get).to eq("line #1")
|
156
|
+
history.next
|
157
|
+
expect(history.get).to eq("line #2")
|
158
|
+
end
|
159
|
+
|
160
|
+
it "clears all lines" do
|
161
|
+
history = described_class.new(3)
|
162
|
+
|
163
|
+
history << "line #1"
|
164
|
+
history << "line #2"
|
165
|
+
history << "line #3"
|
166
|
+
|
167
|
+
expect(history.size).to eq(3)
|
168
|
+
history.clear
|
169
|
+
expect(history.size).to eq(0)
|
170
|
+
expect(history.index).to eq(0)
|
171
|
+
end
|
172
|
+
end
|
@@ -59,15 +59,19 @@ RSpec.describe TTY::Prompt::Reader::KeyEvent, '#from' do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
# arrow keys &
|
62
|
+
# arrow keys & text editing
|
63
63
|
{
|
64
|
-
up:
|
65
|
-
down:
|
66
|
-
right:
|
67
|
-
left:
|
68
|
-
clear:
|
69
|
-
|
70
|
-
|
64
|
+
up: ["\e[A"],
|
65
|
+
down: ["\e[B"],
|
66
|
+
right: ["\e[C"],
|
67
|
+
left: ["\e[D"],
|
68
|
+
clear: ["\e[E"],
|
69
|
+
home: ["\e[1~"],
|
70
|
+
insert: ["\e[2~"],
|
71
|
+
delete: ["\e[3~"],
|
72
|
+
end: ["\e[4~"],
|
73
|
+
page_up: ["\e[5~"],
|
74
|
+
page_down: ["\e[6~"]
|
71
75
|
}.each do |name, codes|
|
72
76
|
codes.each do |code|
|
73
77
|
it "parses #{Shellwords.escape(code)} as #{name} key" do
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
RSpec.describe TTY::Prompt::Reader::Line do
|
4
|
+
it "inserts characters in line" do
|
5
|
+
line = described_class.new('aaaaa')
|
6
|
+
line[0] = 'test'
|
7
|
+
expect(line.text).to eq('testaaaaa')
|
8
|
+
line[4..6] = ''
|
9
|
+
expect(line.text).to eq('testaa')
|
10
|
+
end
|
11
|
+
|
12
|
+
it "moves cursor left and right" do
|
13
|
+
line = described_class.new('aaaaa')
|
14
|
+
5.times { line.left }
|
15
|
+
expect(line.cursor).to eq(0)
|
16
|
+
expect(line.start?).to eq(true)
|
17
|
+
line.left(5)
|
18
|
+
expect(line.cursor).to eq(0)
|
19
|
+
line.right(20)
|
20
|
+
expect(line.cursor).to eq(5)
|
21
|
+
expect(line.end?).to eq(true)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "inserts char at start of the line" do
|
25
|
+
line = described_class.new('aaaaa')
|
26
|
+
expect(line.cursor).to eq(5)
|
27
|
+
line[0] = 'b'
|
28
|
+
expect(line.cursor).to eq(1)
|
29
|
+
expect(line.text).to eq('baaaaa')
|
30
|
+
line.insert('b')
|
31
|
+
expect(line.text).to eq('bbaaaaa')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "inserts char at end of the line" do
|
35
|
+
line = described_class.new('aaaaa')
|
36
|
+
expect(line.cursor).to eq(5)
|
37
|
+
line[4] = 'b'
|
38
|
+
expect(line.cursor).to eq(5)
|
39
|
+
expect(line.text).to eq('aaaaab')
|
40
|
+
end
|
41
|
+
|
42
|
+
it "inserts char inside the line" do
|
43
|
+
line = described_class.new('aaaaa')
|
44
|
+
expect(line.cursor).to eq(5)
|
45
|
+
line[2] = 'b'
|
46
|
+
expect(line.cursor).to eq(3)
|
47
|
+
expect(line.text).to eq('aabaaa')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "inserts char outside of the line size" do
|
51
|
+
line = described_class.new('aaaaa')
|
52
|
+
expect(line.cursor).to eq(5)
|
53
|
+
line[10] = 'b'
|
54
|
+
expect(line.cursor).to eq(11)
|
55
|
+
expect(line.text).to eq('aaaaa b')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "inserts chars on empty string" do
|
59
|
+
line = described_class.new('')
|
60
|
+
expect(line.cursor).to eq(0)
|
61
|
+
line.insert('a')
|
62
|
+
expect(line.cursor).to eq(1)
|
63
|
+
line.insert('b')
|
64
|
+
expect(line.cursor).to eq(2)
|
65
|
+
expect(line.to_s).to eq('ab')
|
66
|
+
line.insert('cc')
|
67
|
+
expect(line.cursor).to eq(4)
|
68
|
+
expect(line.to_s).to eq('abcc')
|
69
|
+
end
|
70
|
+
|
71
|
+
it "inserts characters with #insert call" do
|
72
|
+
line = described_class.new('aaaaa')
|
73
|
+
line.left(2)
|
74
|
+
expect(line.cursor).to eq(3)
|
75
|
+
line.insert(' test ')
|
76
|
+
expect(line.text).to eq('aaa test aa')
|
77
|
+
expect(line.cursor).to eq(9)
|
78
|
+
line.right
|
79
|
+
expect(line.cursor).to eq(10)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "removes char before current cursor position" do
|
83
|
+
line = described_class.new('abcdef')
|
84
|
+
expect(line.cursor).to eq(6)
|
85
|
+
line.remove
|
86
|
+
line.remove
|
87
|
+
expect(line.text).to eq('abcd')
|
88
|
+
expect(line.cursor).to eq(4)
|
89
|
+
line.left
|
90
|
+
line.left
|
91
|
+
line.remove
|
92
|
+
expect(line.text).to eq('acd')
|
93
|
+
expect(line.cursor).to eq(1)
|
94
|
+
line.insert('x')
|
95
|
+
expect(line.text).to eq('axcd')
|
96
|
+
end
|
97
|
+
|
98
|
+
it "deletes char under current cursor position" do
|
99
|
+
line = described_class.new('abcdef')
|
100
|
+
line.left(3)
|
101
|
+
line.delete
|
102
|
+
expect(line.text).to eq('abcef')
|
103
|
+
line.right
|
104
|
+
line.delete
|
105
|
+
expect(line.text).to eq('abce')
|
106
|
+
line.left(4)
|
107
|
+
line.delete
|
108
|
+
expect(line.text).to eq('bce')
|
109
|
+
end
|
110
|
+
end
|
@@ -80,4 +80,15 @@ RSpec.describe TTY::Prompt::Reader, '#publish_keypress_event' do
|
|
80
80
|
expect(keys).to eq(["keyenter", "keypress"])
|
81
81
|
expect(answer).to eq("\n")
|
82
82
|
end
|
83
|
+
|
84
|
+
it "subscribes to ctrl+X type of event event" do
|
85
|
+
input << ?\C-z
|
86
|
+
input.rewind
|
87
|
+
keys = []
|
88
|
+
reader.on(:keyctrl_z) { |event| keys << "ctrl_z" }
|
89
|
+
|
90
|
+
answer = reader.read_keypress
|
91
|
+
expect(keys).to eq(['ctrl_z'])
|
92
|
+
expect(answer).to eq(?\C-z)
|
93
|
+
end
|
83
94
|
end
|
@@ -19,10 +19,40 @@ RSpec.describe TTY::Prompt::Reader, '#read_line' do
|
|
19
19
|
input.rewind
|
20
20
|
answer = reader.read_line
|
21
21
|
expect(answer).to eq("password\n")
|
22
|
-
expect(output.string).to eq(
|
22
|
+
expect(output.string).to eq([
|
23
|
+
"\e[2K\e[1Gp",
|
24
|
+
"\e[2K\e[1Gpa",
|
25
|
+
"\e[2K\e[1Gpas",
|
26
|
+
"\e[2K\e[1Gpass",
|
27
|
+
"\e[2K\e[1Gpassw",
|
28
|
+
"\e[2K\e[1Gpasswo",
|
29
|
+
"\e[2K\e[1Gpasswor",
|
30
|
+
"\e[2K\e[1Gpassword",
|
31
|
+
"\e[2K\e[1Gpassword\n"
|
32
|
+
].join)
|
23
33
|
end
|
24
34
|
|
25
|
-
it '
|
35
|
+
it "doesn't echo characters back" do
|
36
|
+
input << "password\n"
|
37
|
+
input.rewind
|
38
|
+
answer = reader.read_line(echo: false)
|
39
|
+
expect(answer).to eq("password\n")
|
40
|
+
expect(output.string).to eq('')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "displays a prompt before input" do
|
44
|
+
input << "aa\n"
|
45
|
+
input.rewind
|
46
|
+
answer = reader.read_line('>> ')
|
47
|
+
expect(answer).to eq("aa\n")
|
48
|
+
expect(output.string).to eq([
|
49
|
+
"\e[2K\e[1G>> a",
|
50
|
+
"\e[2K\e[1G>> aa",
|
51
|
+
"\e[2K\e[1G>> aa\n"
|
52
|
+
].join)
|
53
|
+
end
|
54
|
+
|
55
|
+
xit 'deletes characters when backspace pressed' do
|
26
56
|
input << "aa\ba\bcc\n"
|
27
57
|
input.rewind
|
28
58
|
answer = reader.read_line
|
@@ -8,28 +8,42 @@ RSpec.describe TTY::Prompt::Reader, '#read_multiline' do
|
|
8
8
|
subject(:reader) { described_class.new(input, output, env: env) }
|
9
9
|
|
10
10
|
it 'reads no lines' do
|
11
|
-
input <<
|
11
|
+
input << "\C-d"
|
12
12
|
input.rewind
|
13
13
|
answer = reader.read_multiline
|
14
14
|
expect(answer).to eq([])
|
15
15
|
end
|
16
16
|
|
17
|
-
it "reads a line" do
|
18
|
-
input << "Single line\
|
17
|
+
it "reads a line and terminates on Ctrl+d" do
|
18
|
+
input << "Single line\C-d"
|
19
19
|
input.rewind
|
20
20
|
answer = reader.read_multiline
|
21
|
-
expect(answer).to eq(["Single line
|
21
|
+
expect(answer).to eq(["Single line"])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "reads a line and terminates on Ctrl+z" do
|
25
|
+
input << "Single line\C-z"
|
26
|
+
input.rewind
|
27
|
+
answer = reader.read_multiline
|
28
|
+
expect(answer).to eq(["Single line"])
|
22
29
|
end
|
23
30
|
|
24
31
|
it 'reads few lines' do
|
25
|
-
input << "First line\nSecond line\nThird line\n"
|
32
|
+
input << "First line\nSecond line\nThird line\n\C-d"
|
26
33
|
input.rewind
|
27
34
|
answer = reader.read_multiline
|
28
35
|
expect(answer).to eq(["First line\n", "Second line\n", "Third line\n"])
|
29
36
|
end
|
30
37
|
|
38
|
+
it "skips empty lines" do
|
39
|
+
input << "\n\nFirst line\n\n\n\n\nSecond line\C-d"
|
40
|
+
input.rewind
|
41
|
+
answer = reader.read_multiline
|
42
|
+
expect(answer).to eq(["First line\n", "Second line"])
|
43
|
+
end
|
44
|
+
|
31
45
|
it 'reads and yiels every line' do
|
32
|
-
input << "First line\nSecond line\nThird line"
|
46
|
+
input << "First line\nSecond line\nThird line\C-z"
|
33
47
|
input.rewind
|
34
48
|
lines = []
|
35
49
|
reader.read_multiline { |line| lines << line }
|
@@ -37,7 +51,7 @@ RSpec.describe TTY::Prompt::Reader, '#read_multiline' do
|
|
37
51
|
end
|
38
52
|
|
39
53
|
it 'reads multibyte lines' do
|
40
|
-
input << "국경의 긴 터널을 빠져나오자\n
|
54
|
+
input << "국경의 긴 터널을 빠져나오자\n설국이었다.\C-d"
|
41
55
|
input.rewind
|
42
56
|
lines = []
|
43
57
|
reader.read_multiline { |line| lines << line }
|
data/spec/unit/select_spec.rb
CHANGED
@@ -51,7 +51,7 @@ RSpec.describe TTY::Prompt, '#select' do
|
|
51
51
|
choices = {large: 1, medium: 2, small: 3}
|
52
52
|
prompt.input << " "
|
53
53
|
prompt.input.rewind
|
54
|
-
expect(prompt.select('What size?', choices)).to eq(1)
|
54
|
+
expect(prompt.select('What size?', choices, default: 1)).to eq(1)
|
55
55
|
expect(prompt.output.string).to eq([
|
56
56
|
"\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
|
57
57
|
"\e[32m#{symbols[:pointer]} large\e[0m\n",
|
@@ -216,6 +216,26 @@ RSpec.describe TTY::Prompt, '#select' do
|
|
216
216
|
].join)
|
217
217
|
end
|
218
218
|
|
219
|
+
it "changes help text through DSL" do
|
220
|
+
choices = %w(Large Medium Small)
|
221
|
+
prompt.input << " "
|
222
|
+
prompt.input.rewind
|
223
|
+
value = prompt.select('What size?') do |menu|
|
224
|
+
menu.help "(Bash keyboard)"
|
225
|
+
menu.choices choices
|
226
|
+
end
|
227
|
+
expect(value).to eq('Large')
|
228
|
+
expect(prompt.output.string).to eq([
|
229
|
+
"\e[?25lWhat size? \e[90m(Bash keyboard)\e[0m\n",
|
230
|
+
"\e[32m#{symbols[:pointer]} Large\e[0m\n",
|
231
|
+
" Medium\n",
|
232
|
+
" Small",
|
233
|
+
"\e[2K\e[1G\e[1A" * 3,
|
234
|
+
"\e[2K\e[1G",
|
235
|
+
"What size? \e[32mLarge\e[0m\n\e[?25h"
|
236
|
+
].join)
|
237
|
+
end
|
238
|
+
|
219
239
|
it "sets prompt prefix" do
|
220
240
|
prompt = TTY::TestPrompt.new(prefix: '[?] ')
|
221
241
|
choices = %w(Large Medium Small)
|
@@ -251,6 +271,25 @@ RSpec.describe TTY::Prompt, '#select' do
|
|
251
271
|
].join)
|
252
272
|
end
|
253
273
|
|
274
|
+
it "paginates choices as hash object" do
|
275
|
+
prompt = TTY::TestPrompt.new
|
276
|
+
choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8}
|
277
|
+
prompt.input << "\r"
|
278
|
+
prompt.input.rewind
|
279
|
+
value = prompt.select("What letter?", choices, per_page: 3, default: 4)
|
280
|
+
expect(value).to eq(4)
|
281
|
+
expect(prompt.output.string).to eq([
|
282
|
+
"\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
|
283
|
+
"\e[32m#{symbols[:pointer]} D\e[0m\n",
|
284
|
+
" E\n",
|
285
|
+
" F\n",
|
286
|
+
"\e[90m(Move up or down to reveal more choices)\e[0m",
|
287
|
+
"\e[2K\e[1G\e[1A" * 4,
|
288
|
+
"\e[2K\e[1G",
|
289
|
+
"What letter? \e[32mD\e[0m\n\e[?25h",
|
290
|
+
].join)
|
291
|
+
end
|
292
|
+
|
254
293
|
it "paginates long selections through DSL" do
|
255
294
|
prompt = TTY::TestPrompt.new
|
256
295
|
choices = %w(A B C D E F G H)
|