tty-reader 0.7.0 → 0.8.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 +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/Rakefile
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'benchmark/ips'
|
2
|
-
require 'tty-reader'
|
3
|
-
|
4
|
-
input = StringIO.new("a")
|
5
|
-
output = StringIO.new
|
6
|
-
$stdin = input
|
7
|
-
reader = TTY::Reader.new(input, output)
|
8
|
-
|
9
|
-
Benchmark.ips do |x|
|
10
|
-
x.report('getc') do
|
11
|
-
input.rewind
|
12
|
-
$stdin.getc
|
13
|
-
end
|
14
|
-
|
15
|
-
x.report('read_char') do
|
16
|
-
input.rewind
|
17
|
-
reader.read_char
|
18
|
-
end
|
19
|
-
|
20
|
-
x.compare!
|
21
|
-
end
|
22
|
-
|
23
|
-
# v0.1.0
|
24
|
-
#
|
25
|
-
# Calculating -------------------------------------
|
26
|
-
# getc 52462 i/100ms
|
27
|
-
# read_char 751 i/100ms
|
28
|
-
# -------------------------------------------------
|
29
|
-
# getc 2484819.4 (±4.1%) i/s - 12433494 in 5.013438s
|
30
|
-
# read_char 7736.4 (±2.9%) i/s - 39052 in 5.052628s
|
31
|
-
#
|
32
|
-
# Comparison:
|
33
|
-
# getc: 2484819.4 i/s
|
34
|
-
# read_char: 7736.4 i/s - 321.19x slower
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'benchmark/ips'
|
2
|
-
require 'tty-reader'
|
3
|
-
|
4
|
-
input = StringIO.new("abc\n")
|
5
|
-
output = StringIO.new
|
6
|
-
$stdin = input
|
7
|
-
reader = TTY::Reader.new(input, output)
|
8
|
-
|
9
|
-
Benchmark.ips do |x|
|
10
|
-
x.report('gets') do
|
11
|
-
input.rewind
|
12
|
-
$stdin.gets
|
13
|
-
end
|
14
|
-
|
15
|
-
x.report('read_line') do
|
16
|
-
input.rewind
|
17
|
-
reader.read_line
|
18
|
-
end
|
19
|
-
|
20
|
-
x.compare!
|
21
|
-
end
|
22
|
-
|
23
|
-
# v0.1.0
|
24
|
-
#
|
25
|
-
# Calculating -------------------------------------
|
26
|
-
# gets 51729 i/100ms
|
27
|
-
# read_line 164 i/100ms
|
28
|
-
# -------------------------------------------------
|
29
|
-
# gets 1955255.2 (±3.7%) i/s - 9776781 in 5.008004s
|
30
|
-
# read_line 1215.1 (±33.1%) i/s - 5248 in 5.066569s
|
31
|
-
#
|
32
|
-
# Comparison:
|
33
|
-
# gets: 1955255.2 i/s
|
34
|
-
# read_line: 1215.1 i/s - 1609.19x slower
|
data/bin/console
DELETED
data/bin/setup
DELETED
data/examples/keypress.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require_relative '../lib/tty-reader'
|
2
|
-
|
3
|
-
reader = TTY::Reader.new
|
4
|
-
|
5
|
-
puts "Press a key (or Ctrl-X to exit)"
|
6
|
-
|
7
|
-
loop do
|
8
|
-
print "=> "
|
9
|
-
char = reader.read_keypress
|
10
|
-
if ?\C-x == char
|
11
|
-
puts "Exiting..."
|
12
|
-
exit
|
13
|
-
else
|
14
|
-
puts "#{char.inspect} [#{char.ord}] (hex: #{char.ord.to_s(16)})"
|
15
|
-
end
|
16
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require_relative '../lib/tty-reader'
|
2
|
-
|
3
|
-
reader = TTY::Reader.new
|
4
|
-
|
5
|
-
puts "Press a key (or Ctrl-X to exit)"
|
6
|
-
|
7
|
-
loop do
|
8
|
-
print reader.cursor.clear_line
|
9
|
-
print "=> "
|
10
|
-
char = reader.read_keypress(nonblock: true)
|
11
|
-
if ?\C-x == char
|
12
|
-
puts "Exiting..."
|
13
|
-
exit
|
14
|
-
elsif char
|
15
|
-
puts "#{char.inspect} [#{char.ord}] (hex: #{char.ord.to_s(16)})"
|
16
|
-
end
|
17
|
-
end
|
data/examples/line.rb
DELETED
data/examples/multi_prompt.rb
DELETED
data/examples/multiline.rb
DELETED
data/examples/noecho.rb
DELETED
data/examples/shell.rb
DELETED
data/spec/spec_helper.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
if ENV['COVERAGE'] || ENV['TRAVIS']
|
4
|
-
require 'simplecov'
|
5
|
-
require 'coveralls'
|
6
|
-
|
7
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
|
8
|
-
SimpleCov::Formatter::HTMLFormatter,
|
9
|
-
Coveralls::SimpleCov::Formatter
|
10
|
-
])
|
11
|
-
|
12
|
-
SimpleCov.start do
|
13
|
-
command_name 'spec'
|
14
|
-
add_filter 'spec'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
require "bundler/setup"
|
19
|
-
require "tty-reader"
|
20
|
-
|
21
|
-
class StringIO
|
22
|
-
def wait_readable(*)
|
23
|
-
true
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
RSpec.configure do |config|
|
28
|
-
# Enable flags like --only-failures and --next-failure
|
29
|
-
config.example_status_persistence_file_path = ".rspec_status"
|
30
|
-
|
31
|
-
config.mock_with :rspec do |mocks|
|
32
|
-
mocks.verify_partial_doubles = true
|
33
|
-
end
|
34
|
-
|
35
|
-
# Disable RSpec exposing methods globally on `Module` and `main`
|
36
|
-
config.disable_monkey_patching!
|
37
|
-
|
38
|
-
# This setting enables warnings. It's recommended, but in some cases may
|
39
|
-
# be too noisy due to issues in dependencies.
|
40
|
-
config.warnings = true
|
41
|
-
|
42
|
-
if config.files_to_run.one?
|
43
|
-
config.default_formatter = 'doc'
|
44
|
-
end
|
45
|
-
|
46
|
-
config.profile_examples = 2
|
47
|
-
|
48
|
-
config.order = :random
|
49
|
-
|
50
|
-
Kernel.srand config.seed
|
51
|
-
end
|
data/spec/unit/history_spec.rb
DELETED
@@ -1,177 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe TTY::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(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
|
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(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
|
-
|
115
|
-
expect(history.index).to eq(nil)
|
116
|
-
expect(history.previous?).to eq(false)
|
117
|
-
expect(history.next?).to eq(false)
|
118
|
-
|
119
|
-
history << "line #1"
|
120
|
-
history << "line #2"
|
121
|
-
expect(history.index).to eq(1)
|
122
|
-
expect(history.previous?).to eq(true)
|
123
|
-
expect(history.next?).to eq(false)
|
124
|
-
|
125
|
-
history.previous
|
126
|
-
expect(history.index).to eq(0)
|
127
|
-
expect(history.previous?).to eq(true)
|
128
|
-
expect(history.next?).to eq(true)
|
129
|
-
|
130
|
-
history.previous
|
131
|
-
expect(history.index).to eq(0)
|
132
|
-
expect(history.previous?).to eq(true)
|
133
|
-
expect(history.next?).to eq(true)
|
134
|
-
end
|
135
|
-
|
136
|
-
it "gets line based on index" do
|
137
|
-
history = described_class.new(3, cycle: true)
|
138
|
-
history << "line #1"
|
139
|
-
history << "line #2"
|
140
|
-
history << "line #3"
|
141
|
-
|
142
|
-
expect(history[-1]).to eq('line #3')
|
143
|
-
expect(history[1]).to eq('line #2')
|
144
|
-
expect {
|
145
|
-
history[11]
|
146
|
-
}.to raise_error(IndexError, 'invalid index')
|
147
|
-
end
|
148
|
-
|
149
|
-
it "retrieves current line" do
|
150
|
-
history = described_class.new(3, cycle: true)
|
151
|
-
expect(history.get).to eq(nil)
|
152
|
-
|
153
|
-
history << "line #1"
|
154
|
-
history << "line #2"
|
155
|
-
history << "line #3"
|
156
|
-
|
157
|
-
expect(history.get).to eq("line #3")
|
158
|
-
history.previous
|
159
|
-
history.previous
|
160
|
-
expect(history.get).to eq("line #1")
|
161
|
-
history.next
|
162
|
-
expect(history.get).to eq("line #2")
|
163
|
-
end
|
164
|
-
|
165
|
-
it "clears all lines" do
|
166
|
-
history = described_class.new(3)
|
167
|
-
|
168
|
-
history << "line #1"
|
169
|
-
history << "line #2"
|
170
|
-
history << "line #3"
|
171
|
-
|
172
|
-
expect(history.size).to eq(3)
|
173
|
-
history.clear
|
174
|
-
expect(history.size).to eq(0)
|
175
|
-
expect(history.index).to eq(0)
|
176
|
-
end
|
177
|
-
end
|
data/spec/unit/key_event_spec.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'shellwords'
|
4
|
-
|
5
|
-
RSpec.describe TTY::Reader::KeyEvent, '#from' do
|
6
|
-
let(:keys) { TTY::Reader::Keys.keys }
|
7
|
-
|
8
|
-
it "parses backspace" do
|
9
|
-
event = described_class.from(keys, "\x7f")
|
10
|
-
expect(event.key.name).to eq(:backspace)
|
11
|
-
expect(event.value).to eq("\x7f")
|
12
|
-
end
|
13
|
-
|
14
|
-
it "parses lowercase char" do
|
15
|
-
event = described_class.from(keys, 'a')
|
16
|
-
expect(event.key.name).to eq(:alpha)
|
17
|
-
expect(event.value).to eq('a')
|
18
|
-
end
|
19
|
-
|
20
|
-
it "parses uppercase char" do
|
21
|
-
event = described_class.from(keys, 'A')
|
22
|
-
expect(event.key.name).to eq(:alpha)
|
23
|
-
expect(event.value).to eq('A')
|
24
|
-
end
|
25
|
-
|
26
|
-
it "parses number char" do
|
27
|
-
event = described_class.from(keys, '666')
|
28
|
-
expect(event.key.name).to eq(:num)
|
29
|
-
expect(event.value).to eq('666')
|
30
|
-
end
|
31
|
-
|
32
|
-
it "parses ctrl-a to ctrl-z inputs" do
|
33
|
-
(1..26).zip('a'..'z').each do |code, char|
|
34
|
-
event = described_class.from(TTY::Reader::Keys.ctrl_keys, code.chr)
|
35
|
-
expect(event.key.name).to eq(:"ctrl_#{char}")
|
36
|
-
expect(event.value).to eq(code.chr)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
it "parses uknown key" do
|
41
|
-
no_keys = {}
|
42
|
-
event = described_class.from(no_keys, '*')
|
43
|
-
expect(event.key.name).to eq(:ignore)
|
44
|
-
expect(event.value).to eq('*')
|
45
|
-
end
|
46
|
-
|
47
|
-
it "exposes line value" do
|
48
|
-
event = described_class.from(keys, 'c', 'ab')
|
49
|
-
expect(event.line).to eq('ab')
|
50
|
-
end
|
51
|
-
|
52
|
-
# F1-F12 keys
|
53
|
-
{
|
54
|
-
f1: ["\eOP","\e[[A","\e[11~"],
|
55
|
-
f2: ["\eOQ","\e[[B","\e[12~"],
|
56
|
-
f3: ["\eOR","\e[[C","\e[13~"],
|
57
|
-
f4: ["\eOS","\e[[D","\e[14~"],
|
58
|
-
f5: [ "\e[[E","\e[15~"],
|
59
|
-
f6: [ "\e[17~"],
|
60
|
-
f7: [ "\e[18~"],
|
61
|
-
f8: [ "\e[19~"],
|
62
|
-
f9: [ "\e[20~"],
|
63
|
-
f10: [ "\e[21~"],
|
64
|
-
f11: [ "\e[23~"],
|
65
|
-
f12: [ "\e[24~"]
|
66
|
-
}.each do |name, codes|
|
67
|
-
codes.each do |code|
|
68
|
-
it "parses #{Shellwords.escape(code)} as #{name} key" do
|
69
|
-
event = described_class.from(keys, code)
|
70
|
-
expect(event.key.name).to eq(name)
|
71
|
-
expect(event.key.meta).to eq(false)
|
72
|
-
expect(event.key.ctrl).to eq(false)
|
73
|
-
expect(event.key.shift).to eq(false)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# arrow keys & text editing
|
79
|
-
{
|
80
|
-
up: ["\e[A"],
|
81
|
-
down: ["\e[B"],
|
82
|
-
right: ["\e[C"],
|
83
|
-
left: ["\e[D"],
|
84
|
-
clear: ["\e[E"],
|
85
|
-
home: ["\e[1~", "\e[7~", "\e[H"],
|
86
|
-
end: ["\e[4~", "\eOF", "\e[F"],
|
87
|
-
insert: ["\e[2~"],
|
88
|
-
delete: ["\e[3~"],
|
89
|
-
page_up: ["\e[5~"],
|
90
|
-
page_down: ["\e[6~"]
|
91
|
-
}.each do |name, codes|
|
92
|
-
codes.each do |code|
|
93
|
-
it "parses #{Shellwords.escape(code)} as #{name} key" do
|
94
|
-
event = described_class.from(keys, code)
|
95
|
-
expect(event.key.name).to eq(name)
|
96
|
-
expect(event.key.meta).to eq(false)
|
97
|
-
expect(event.key.ctrl).to eq(false)
|
98
|
-
expect(event.key.shift).to eq(false)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|