tty-reader 0.7.0 → 0.8.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 +8 -0
- data/README.md +1 -0
- data/lib/tty/reader.rb +21 -21
- data/lib/tty/reader/console.rb +5 -5
- data/lib/tty/reader/history.rb +2 -2
- data/lib/tty/reader/key_event.rb +2 -2
- data/lib/tty/reader/keys.rb +15 -15
- data/lib/tty/reader/line.rb +7 -7
- data/lib/tty/reader/mode.rb +1 -1
- data/lib/tty/reader/version.rb +1 -1
- data/lib/tty/reader/win_api.rb +1 -1
- data/lib/tty/reader/win_console.rb +5 -5
- metadata +18 -54
- data/Rakefile +0 -10
- data/benchmarks/speed_read_char.rb +0 -34
- data/benchmarks/speed_read_line.rb +0 -34
- data/bin/console +0 -6
- data/bin/setup +0 -8
- data/examples/keypress.rb +0 -16
- data/examples/keypress_nonblock.rb +0 -17
- data/examples/line.rb +0 -7
- data/examples/multi_prompt.rb +0 -9
- data/examples/multiline.rb +0 -7
- data/examples/noecho.rb +0 -6
- data/examples/shell.rb +0 -12
- data/spec/spec_helper.rb +0 -51
- data/spec/unit/history_spec.rb +0 -177
- data/spec/unit/key_event_spec.rb +0 -102
- data/spec/unit/line_spec.rb +0 -158
- data/spec/unit/publish_keypress_event_spec.rb +0 -109
- data/spec/unit/read_keypress_spec.rb +0 -96
- data/spec/unit/read_line_spec.rb +0 -119
- data/spec/unit/read_multiline_spec.rb +0 -76
- data/spec/unit/subscribe_spec.rb +0 -74
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-reader.gemspec +0 -40
data/spec/unit/line_spec.rb
DELETED
@@ -1,158 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe TTY::Reader::Line do
|
4
|
-
it "provides access to the prompt" do
|
5
|
-
line = described_class.new('aaa', prompt: '>> ')
|
6
|
-
expect(line.prompt).to eq('>> ')
|
7
|
-
expect(line.text).to eq('aaa')
|
8
|
-
expect(line.size).to eq(6)
|
9
|
-
expect(line.to_s).to eq(">> aaa")
|
10
|
-
end
|
11
|
-
|
12
|
-
it "inserts characters inside a line" do
|
13
|
-
line = described_class.new('aaaaa')
|
14
|
-
|
15
|
-
line[0] = 'test'
|
16
|
-
expect(line.text).to eq('testaaaaa')
|
17
|
-
|
18
|
-
line[4..6] = ''
|
19
|
-
expect(line.text).to eq('testaa')
|
20
|
-
end
|
21
|
-
|
22
|
-
it "moves cursor left and right" do
|
23
|
-
line = described_class.new('aaaaa')
|
24
|
-
|
25
|
-
5.times { line.left }
|
26
|
-
expect(line.cursor).to eq(0)
|
27
|
-
expect(line.start?).to eq(true)
|
28
|
-
|
29
|
-
line.left(5)
|
30
|
-
expect(line.cursor).to eq(0)
|
31
|
-
|
32
|
-
line.right(20)
|
33
|
-
expect(line.cursor).to eq(5)
|
34
|
-
expect(line.end?).to eq(true)
|
35
|
-
end
|
36
|
-
|
37
|
-
it "inserts char at start of the line" do
|
38
|
-
line = described_class.new('aaaaa')
|
39
|
-
expect(line.cursor).to eq(5)
|
40
|
-
|
41
|
-
line[0] = 'b'
|
42
|
-
expect(line.cursor).to eq(1)
|
43
|
-
expect(line.text).to eq('baaaaa')
|
44
|
-
|
45
|
-
line.insert('b')
|
46
|
-
expect(line.text).to eq('bbaaaaa')
|
47
|
-
end
|
48
|
-
|
49
|
-
it "inserts char at end of the line" do
|
50
|
-
line = described_class.new('aaaaa')
|
51
|
-
expect(line.cursor).to eq(5)
|
52
|
-
|
53
|
-
line[4] = 'b'
|
54
|
-
expect(line.cursor).to eq(5)
|
55
|
-
expect(line.text).to eq('aaaaba')
|
56
|
-
end
|
57
|
-
|
58
|
-
it "inserts char inside the line" do
|
59
|
-
line = described_class.new('aaaaa')
|
60
|
-
expect(line.cursor).to eq(5)
|
61
|
-
|
62
|
-
line[2] = 'b'
|
63
|
-
expect(line.cursor).to eq(3)
|
64
|
-
expect(line.text).to eq('aabaaa')
|
65
|
-
end
|
66
|
-
|
67
|
-
it "inserts char outside of the line size" do
|
68
|
-
line = described_class.new('aaaaa')
|
69
|
-
expect(line.cursor).to eq(5)
|
70
|
-
|
71
|
-
line[10] = 'b'
|
72
|
-
expect(line.cursor).to eq(11)
|
73
|
-
expect(line.text).to eq('aaaaa b')
|
74
|
-
end
|
75
|
-
|
76
|
-
it "inserts chars in empty string" do
|
77
|
-
line = described_class.new('')
|
78
|
-
expect(line.cursor).to eq(0)
|
79
|
-
|
80
|
-
line.insert('a')
|
81
|
-
expect(line.cursor).to eq(1)
|
82
|
-
|
83
|
-
line.insert('b')
|
84
|
-
expect(line.cursor).to eq(2)
|
85
|
-
expect(line.to_s).to eq('ab')
|
86
|
-
|
87
|
-
line.insert('cc')
|
88
|
-
expect(line.cursor).to eq(4)
|
89
|
-
expect(line.to_s).to eq('abcc')
|
90
|
-
end
|
91
|
-
|
92
|
-
it "inserts characters with #insert call" do
|
93
|
-
line = described_class.new('aaaaa')
|
94
|
-
expect(line.cursor).to eq(5)
|
95
|
-
|
96
|
-
line.left(2)
|
97
|
-
expect(line.cursor).to eq(3)
|
98
|
-
|
99
|
-
line.insert(' test ')
|
100
|
-
expect(line.text).to eq('aaa test aa')
|
101
|
-
expect(line.cursor).to eq(9)
|
102
|
-
|
103
|
-
line.right
|
104
|
-
expect(line.cursor).to eq(10)
|
105
|
-
end
|
106
|
-
|
107
|
-
it "removes char before current cursor position" do
|
108
|
-
line = described_class.new('abcdef')
|
109
|
-
expect(line.cursor).to eq(6)
|
110
|
-
|
111
|
-
line.remove(2)
|
112
|
-
expect(line.text).to eq('abcd')
|
113
|
-
expect(line.cursor).to eq(4)
|
114
|
-
|
115
|
-
line.left
|
116
|
-
line.left
|
117
|
-
line.remove
|
118
|
-
expect(line.text).to eq('acd')
|
119
|
-
expect(line.cursor).to eq(1)
|
120
|
-
|
121
|
-
line.insert('x')
|
122
|
-
expect(line.text).to eq('axcd')
|
123
|
-
end
|
124
|
-
|
125
|
-
it "deletes char under current cursor position" do
|
126
|
-
line = described_class.new('abcdef')
|
127
|
-
|
128
|
-
line.left(3)
|
129
|
-
line.delete
|
130
|
-
expect(line.text).to eq('abcef')
|
131
|
-
|
132
|
-
line.right
|
133
|
-
line.delete
|
134
|
-
expect(line.text).to eq('abce')
|
135
|
-
|
136
|
-
line.left(4)
|
137
|
-
line.delete
|
138
|
-
expect(line.text).to eq('bce')
|
139
|
-
end
|
140
|
-
|
141
|
-
it "replaces current line with new preserving cursor" do
|
142
|
-
line = described_class.new('x' * 6)
|
143
|
-
expect(line.text).to eq('xxxxxx')
|
144
|
-
expect(line.cursor).to eq(6)
|
145
|
-
expect(line.mode).to eq(:edit)
|
146
|
-
expect(line.editing?).to eq(true)
|
147
|
-
|
148
|
-
line.replace('y' * 8)
|
149
|
-
expect(line.text).to eq('y' * 8)
|
150
|
-
expect(line.cursor).to eq(8)
|
151
|
-
expect(line.replacing?).to eq(true)
|
152
|
-
|
153
|
-
line.insert('z')
|
154
|
-
expect(line.text).to eq('y' * 8 + 'z')
|
155
|
-
expect(line.cursor).to eq(9)
|
156
|
-
expect(line.editing?).to eq(true)
|
157
|
-
end
|
158
|
-
end
|
@@ -1,109 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe TTY::Reader, '#publish_keypress_event' do
|
4
|
-
let(:input) { StringIO.new }
|
5
|
-
let(:out) { StringIO.new }
|
6
|
-
let(:env) { { "TTY_TEST" => true } }
|
7
|
-
|
8
|
-
let(:reader) { described_class.new(input: input, output: out, env: env) }
|
9
|
-
|
10
|
-
it "publishes :keypress events" do
|
11
|
-
input << "abc\n"
|
12
|
-
input.rewind
|
13
|
-
chars = []
|
14
|
-
lines = []
|
15
|
-
reader.on(:keypress) { |event| chars << event.value; lines << event.line }
|
16
|
-
answer = reader.read_line
|
17
|
-
|
18
|
-
expect(chars).to eq(%W(a b c \n))
|
19
|
-
expect(lines).to eq(%W(a ab abc abc\n))
|
20
|
-
expect(answer).to eq("abc\n")
|
21
|
-
end
|
22
|
-
|
23
|
-
it "publishes :keyescape events" do
|
24
|
-
input << "a\e"
|
25
|
-
input.rewind
|
26
|
-
keys = []
|
27
|
-
reader.on(:keypress) { |event| keys << "keypress_#{event.value}"}
|
28
|
-
reader.on(:keyescape) { |event| keys << "keyescape_#{event.value}" }
|
29
|
-
|
30
|
-
answer = reader.read_line
|
31
|
-
expect(keys).to eq(["keypress_a", "keyescape_\e", "keypress_\e"])
|
32
|
-
expect(answer).to eq("a\e")
|
33
|
-
end
|
34
|
-
|
35
|
-
it "publishes :keyup for read_keypress" do
|
36
|
-
input << "\e[Aaa"
|
37
|
-
input.rewind
|
38
|
-
keys = []
|
39
|
-
reader.on(:keypress) { |event| keys << "keypress_#{event.value}" }
|
40
|
-
reader.on(:keyup) { |event| keys << "keyup_#{event.value}" }
|
41
|
-
reader.on(:keydown) { |event| keys << "keydown_#{event.value}" }
|
42
|
-
|
43
|
-
answer = reader.read_keypress
|
44
|
-
expect(keys).to eq(["keyup_\e[A", "keypress_\e[A"])
|
45
|
-
expect(answer).to eq("\e[A")
|
46
|
-
end
|
47
|
-
|
48
|
-
it "publishes :keydown event for read_keypress" do
|
49
|
-
input << "\e[Baa"
|
50
|
-
input.rewind
|
51
|
-
keys = []
|
52
|
-
reader.on(:keypress) { |event| keys << "keypress_#{event.value}" }
|
53
|
-
reader.on(:keyup) { |event| keys << "keyup_#{event.value}" }
|
54
|
-
reader.on(:keydown) { |event| keys << "keydown_#{event.value}" }
|
55
|
-
|
56
|
-
answer = reader.read_keypress
|
57
|
-
expect(keys).to eq(["keydown_\e[B", "keypress_\e[B"])
|
58
|
-
expect(answer).to eq("\e[B")
|
59
|
-
end
|
60
|
-
|
61
|
-
it "publishes :keynum event" do
|
62
|
-
input << "5aa"
|
63
|
-
input.rewind
|
64
|
-
keys = []
|
65
|
-
reader.on(:keypress) { |event| keys << "keypress_#{event.value}" }
|
66
|
-
reader.on(:keyup) { |event| keys << "keyup_#{event.value}" }
|
67
|
-
reader.on(:keynum) { |event| keys << "keynum_#{event.value}" }
|
68
|
-
|
69
|
-
answer = reader.read_keypress
|
70
|
-
expect(keys).to eq(["keynum_5", "keypress_5"])
|
71
|
-
expect(answer).to eq("5")
|
72
|
-
end
|
73
|
-
|
74
|
-
it "publishes :keyreturn event" do
|
75
|
-
input << "\r"
|
76
|
-
input.rewind
|
77
|
-
keys = []
|
78
|
-
reader.on(:keypress) { |event| keys << "keypress" }
|
79
|
-
reader.on(:keyup) { |event| keys << "keyup" }
|
80
|
-
reader.on(:keyreturn) { |event| keys << "keyreturn" }
|
81
|
-
|
82
|
-
answer = reader.read_keypress
|
83
|
-
expect(keys).to eq(["keyreturn", "keypress"])
|
84
|
-
expect(answer).to eq("\r")
|
85
|
-
end
|
86
|
-
|
87
|
-
it "subscribes to multiple events" do
|
88
|
-
input << "\n"
|
89
|
-
input.rewind
|
90
|
-
keys = []
|
91
|
-
reader.on(:keyenter) { |event| keys << "keyenter" }
|
92
|
-
.on(:keypress) { |event| keys << "keypress" }
|
93
|
-
|
94
|
-
answer = reader.read_keypress
|
95
|
-
expect(keys).to eq(["keyenter", "keypress"])
|
96
|
-
expect(answer).to eq("\n")
|
97
|
-
end
|
98
|
-
|
99
|
-
it "subscribes to ctrl+X type of event event" do
|
100
|
-
input << ?\C-z
|
101
|
-
input.rewind
|
102
|
-
keys = []
|
103
|
-
reader.on(:keyctrl_z) { |event| keys << "ctrl_z" }
|
104
|
-
|
105
|
-
answer = reader.read_keypress
|
106
|
-
expect(keys).to eq(['ctrl_z'])
|
107
|
-
expect(answer).to eq(?\C-z)
|
108
|
-
end
|
109
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe TTY::Reader, '#read_keypress' do
|
4
|
-
let(:input) { StringIO.new }
|
5
|
-
let(:out) { StringIO.new }
|
6
|
-
let(:env) { { "TTY_TEST" => true } }
|
7
|
-
|
8
|
-
it "reads single key press" do
|
9
|
-
reader = described_class.new(input: input, output: out, env: env)
|
10
|
-
input << "\e[Aaaaaaa\n"
|
11
|
-
input.rewind
|
12
|
-
|
13
|
-
answer = reader.read_keypress
|
14
|
-
|
15
|
-
expect(answer).to eq("\e[A")
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'reads multibyte key press' do
|
19
|
-
reader = described_class.new(input: input, output: out, env: env)
|
20
|
-
input << "ㄱ"
|
21
|
-
input.rewind
|
22
|
-
|
23
|
-
answer = reader.read_keypress
|
24
|
-
|
25
|
-
expect(answer).to eq("ㄱ")
|
26
|
-
end
|
27
|
-
|
28
|
-
context 'when Ctrl+C pressed' do
|
29
|
-
it "defaults to raising InputInterrupt" do
|
30
|
-
reader = described_class.new(input: input, output: out, env: env)
|
31
|
-
input << "\x03"
|
32
|
-
input.rewind
|
33
|
-
|
34
|
-
expect {
|
35
|
-
reader.read_keypress
|
36
|
-
}.to raise_error(TTY::Reader::InputInterrupt)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "sends interrupt signal when :signal option is chosen" do
|
40
|
-
reader = described_class.new(
|
41
|
-
input: input,
|
42
|
-
output: out,
|
43
|
-
interrupt: :signal,
|
44
|
-
env: env)
|
45
|
-
input << "\x03"
|
46
|
-
input.rewind
|
47
|
-
|
48
|
-
allow(Process).to receive(:pid).and_return(666)
|
49
|
-
allow(Process).to receive(:kill)
|
50
|
-
expect(Process).to receive(:kill).with('SIGINT', 666)
|
51
|
-
|
52
|
-
reader.read_keypress
|
53
|
-
end
|
54
|
-
|
55
|
-
it "exits with 130 code when :exit option is chosen" do
|
56
|
-
reader = described_class.new(
|
57
|
-
input: input,
|
58
|
-
output: out,
|
59
|
-
interrupt: :exit,
|
60
|
-
env: env)
|
61
|
-
input << "\x03"
|
62
|
-
input.rewind
|
63
|
-
|
64
|
-
expect {
|
65
|
-
reader.read_keypress
|
66
|
-
}.to raise_error(SystemExit)
|
67
|
-
end
|
68
|
-
|
69
|
-
it "evaluates custom handler when proc object is provided" do
|
70
|
-
handler = proc { raise ArgumentError }
|
71
|
-
reader = described_class.new(
|
72
|
-
input: input,
|
73
|
-
output: out,
|
74
|
-
interrupt: handler,
|
75
|
-
env: env)
|
76
|
-
input << "\x03"
|
77
|
-
input.rewind
|
78
|
-
|
79
|
-
expect {
|
80
|
-
reader.read_keypress
|
81
|
-
}.to raise_error(ArgumentError)
|
82
|
-
end
|
83
|
-
|
84
|
-
it "skips handler when handler is nil" do
|
85
|
-
reader = described_class.new(
|
86
|
-
input: input,
|
87
|
-
output: out,
|
88
|
-
interrupt: :noop,
|
89
|
-
env: env)
|
90
|
-
input << "\x03"
|
91
|
-
input.rewind
|
92
|
-
|
93
|
-
expect(reader.read_keypress).to eq("\x03")
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
data/spec/unit/read_line_spec.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe TTY::Reader, '#read_line' do
|
4
|
-
let(:input) { StringIO.new }
|
5
|
-
let(:output) { StringIO.new }
|
6
|
-
let(:env) { { "TTY_TEST" => true } }
|
7
|
-
|
8
|
-
subject(:reader) { described_class.new(input: input, output: output, env: env) }
|
9
|
-
|
10
|
-
it 'masks characters' do
|
11
|
-
input << "password\n"
|
12
|
-
input.rewind
|
13
|
-
|
14
|
-
answer = reader.read_line(echo: false)
|
15
|
-
|
16
|
-
expect(answer).to eq("password\n")
|
17
|
-
end
|
18
|
-
|
19
|
-
it "echoes characters back" do
|
20
|
-
input << "password\n"
|
21
|
-
input.rewind
|
22
|
-
|
23
|
-
answer = reader.read_line
|
24
|
-
|
25
|
-
expect(answer).to eq("password\n")
|
26
|
-
expect(output.string).to eq([
|
27
|
-
"\e[2K\e[1Gp",
|
28
|
-
"\e[2K\e[1Gpa",
|
29
|
-
"\e[2K\e[1Gpas",
|
30
|
-
"\e[2K\e[1Gpass",
|
31
|
-
"\e[2K\e[1Gpassw",
|
32
|
-
"\e[2K\e[1Gpasswo",
|
33
|
-
"\e[2K\e[1Gpasswor",
|
34
|
-
"\e[2K\e[1Gpassword",
|
35
|
-
"\e[2K\e[1Gpassword\n"
|
36
|
-
].join)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "doesn't echo characters back" do
|
40
|
-
input << "password\n"
|
41
|
-
input.rewind
|
42
|
-
|
43
|
-
answer = reader.read_line(echo: false)
|
44
|
-
|
45
|
-
expect(answer).to eq("password\n")
|
46
|
-
expect(output.string).to eq("\n")
|
47
|
-
end
|
48
|
-
|
49
|
-
it "displays a prompt before input" do
|
50
|
-
input << "aa\n"
|
51
|
-
input.rewind
|
52
|
-
|
53
|
-
answer = reader.read_line('>> ')
|
54
|
-
|
55
|
-
expect(answer).to eq("aa\n")
|
56
|
-
expect(output.string).to eq([
|
57
|
-
">> ",
|
58
|
-
"\e[2K\e[1G>> a",
|
59
|
-
"\e[2K\e[1G>> aa",
|
60
|
-
"\e[2K\e[1G>> aa\n"
|
61
|
-
].join)
|
62
|
-
end
|
63
|
-
|
64
|
-
it "displays custom input with a prompt" do
|
65
|
-
input << "aa\n"
|
66
|
-
input.rewind
|
67
|
-
|
68
|
-
answer = reader.read_line("> ", value: "xx")
|
69
|
-
|
70
|
-
expect(answer).to eq("xxaa\n")
|
71
|
-
expect(output.string).to eq([
|
72
|
-
"> xx",
|
73
|
-
"\e[2K\e[1G> xxa",
|
74
|
-
"\e[2K\e[1G> xxaa",
|
75
|
-
"\e[2K\e[1G> xxaa\n"
|
76
|
-
].join)
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'deletes characters when backspace pressed' do
|
80
|
-
input << "aa\ba\bcc\n"
|
81
|
-
input.rewind
|
82
|
-
|
83
|
-
answer = reader.read_line
|
84
|
-
|
85
|
-
expect(answer).to eq("acc\n")
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'reads multibyte line' do
|
89
|
-
input << "한글"
|
90
|
-
input.rewind
|
91
|
-
|
92
|
-
answer = reader.read_line
|
93
|
-
|
94
|
-
expect(answer).to eq("한글")
|
95
|
-
end
|
96
|
-
|
97
|
-
it "supports multiline prompts" do
|
98
|
-
allow(TTY::Screen).to receive(:width).and_return(50)
|
99
|
-
prompt = "one\ntwo\nthree"
|
100
|
-
input << "aa\n"
|
101
|
-
input.rewind
|
102
|
-
|
103
|
-
answer = reader.read_line(prompt)
|
104
|
-
|
105
|
-
expect(answer).to eq("aa\n")
|
106
|
-
expect(output.string).to eq([
|
107
|
-
prompt,
|
108
|
-
"\e[2K\e[1G\e[1A" * 2,
|
109
|
-
"\e[2K\e[1G",
|
110
|
-
prompt + "a",
|
111
|
-
"\e[2K\e[1G\e[1A" * 2,
|
112
|
-
"\e[2K\e[1G",
|
113
|
-
prompt + "aa",
|
114
|
-
"\e[2K\e[1G\e[1A" * 2,
|
115
|
-
"\e[2K\e[1G",
|
116
|
-
prompt + "aa\n"
|
117
|
-
].join)
|
118
|
-
end
|
119
|
-
end
|