reline 0.2.5 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -0
- data/lib/reline/ansi.rb +153 -62
- data/lib/reline/config.rb +62 -14
- data/lib/reline/general_io.rb +14 -4
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_stroke.rb +64 -14
- data/lib/reline/line_editor.rb +638 -74
- data/lib/reline/terminfo.rb +171 -0
- data/lib/reline/unicode.rb +42 -3
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +281 -112
- data/lib/reline.rb +150 -35
- metadata +4 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1009c9156a8c15c1f3c64a3cb89a0d6a1acef037185bfb76c24ed9df7e49cb2c
|
4
|
+
data.tar.gz: 983ad1d570617a47d732402485a79eba8bd1acc6f3555f117ca6822cee52030d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 424c026623c896ccd59fed73b5f61e7bb067dfdc8fe34a13bdb317895fb512eb666b25c76439e940915307e658e85dbbfcb8339ecfae5c5b72fa9239d1599778
|
7
|
+
data.tar.gz: 93077ced9c8009d5c2f7f59eb013f8134394347c57660e7958244a2adfe60b2a09463ac2062d9ce31e5d75974b19665f7e800f2078bf9b461dfbada76c3e572e
|
data/README.md
CHANGED
@@ -8,6 +8,52 @@ This is a screen capture of *IRB improved by Reline*.
|
|
8
8
|
|
9
9
|
Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and Editline by pure Ruby implementation.
|
10
10
|
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
### Single line editing mode
|
14
|
+
|
15
|
+
It's compatible with the readline standard library.
|
16
|
+
|
17
|
+
See [the document of readline stdlib](https://ruby-doc.org/stdlib/libdoc/readline/rdoc/Readline.html) or [bin/example](https://github.com/ruby/reline/blob/master/bin/example).
|
18
|
+
|
19
|
+
### Multi-line editing mode
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require "reline"
|
23
|
+
|
24
|
+
prompt = 'prompt> '
|
25
|
+
use_history = true
|
26
|
+
|
27
|
+
begin
|
28
|
+
while true
|
29
|
+
text = Reline.readmultiline(prompt, use_history) do |multiline_input|
|
30
|
+
# Accept the input until `end` is entered
|
31
|
+
multiline_input.split.last == "end"
|
32
|
+
end
|
33
|
+
|
34
|
+
puts 'You entered:'
|
35
|
+
puts text
|
36
|
+
end
|
37
|
+
# If you want to exit, type Ctrl-C
|
38
|
+
rescue Interrupt
|
39
|
+
puts '^C'
|
40
|
+
exit 0
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
```bash
|
45
|
+
$ ruby example.rb
|
46
|
+
prompt> aaa
|
47
|
+
prompt> bbb
|
48
|
+
prompt> end
|
49
|
+
You entered:
|
50
|
+
aaa
|
51
|
+
bbb
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
See also: [test/reline/yamatanooroti/multiline_repl](https://github.com/ruby/reline/blob/master/test/reline/yamatanooroti/multiline_repl)
|
56
|
+
|
11
57
|
## License
|
12
58
|
|
13
59
|
The gem is available as open source under the terms of the [Ruby License](https://www.ruby-lang.org/en/about/license.txt).
|
data/lib/reline/ansi.rb
CHANGED
@@ -1,7 +1,26 @@
|
|
1
1
|
require 'io/console'
|
2
|
+
require 'io/wait'
|
2
3
|
require 'timeout'
|
4
|
+
require_relative 'terminfo'
|
3
5
|
|
4
6
|
class Reline::ANSI
|
7
|
+
CAPNAME_KEY_BINDINGS = {
|
8
|
+
'khome' => :ed_move_to_beg,
|
9
|
+
'kend' => :ed_move_to_end,
|
10
|
+
'kcuu1' => :ed_prev_history,
|
11
|
+
'kcud1' => :ed_next_history,
|
12
|
+
'kcuf1' => :ed_next_char,
|
13
|
+
'kcub1' => :ed_prev_char,
|
14
|
+
'cuu' => :ed_prev_history,
|
15
|
+
'cud' => :ed_next_history,
|
16
|
+
'cuf' => :ed_next_char,
|
17
|
+
'cub' => :ed_prev_char,
|
18
|
+
}
|
19
|
+
|
20
|
+
if Reline::Terminfo.enabled?
|
21
|
+
Reline::Terminfo.setupterm(0, 2)
|
22
|
+
end
|
23
|
+
|
5
24
|
def self.encoding
|
6
25
|
Encoding.default_external
|
7
26
|
end
|
@@ -10,52 +29,108 @@ class Reline::ANSI
|
|
10
29
|
false
|
11
30
|
end
|
12
31
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
32
|
+
def self.set_default_key_bindings(config)
|
33
|
+
if Reline::Terminfo.enabled?
|
34
|
+
set_default_key_bindings_terminfo(config)
|
35
|
+
else
|
36
|
+
set_default_key_bindings_comprehensive_list(config)
|
37
|
+
end
|
38
|
+
{
|
39
|
+
# extended entries of terminfo
|
40
|
+
[27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→, extended entry
|
41
|
+
[27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←, extended entry
|
42
|
+
[27, 91, 49, 59, 51, 67] => :em_next_word, # Meta+→, extended entry
|
43
|
+
[27, 91, 49, 59, 51, 68] => :ed_prev_word, # Meta+←, extended entry
|
44
|
+
}.each_pair do |key, func|
|
45
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
46
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
47
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
48
|
+
end
|
49
|
+
{
|
50
|
+
[27, 91, 90] => :completion_journey_up, # S-Tab
|
51
|
+
}.each_pair do |key, func|
|
52
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
53
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
54
|
+
end
|
55
|
+
{
|
56
|
+
# default bindings
|
57
|
+
[27, 32] => :em_set_mark, # M-<space>
|
58
|
+
[24, 24] => :em_exchange_mark, # C-x C-x
|
59
|
+
}.each_pair do |key, func|
|
60
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.set_default_key_bindings_terminfo(config)
|
65
|
+
key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
|
66
|
+
begin
|
67
|
+
key_code = Reline::Terminfo.tigetstr(capname)
|
68
|
+
case capname
|
69
|
+
# Escape sequences that omit the move distance and are set to defaults
|
70
|
+
# value 1 may be sometimes sent by pressing the arrow-key.
|
71
|
+
when 'cuu', 'cud', 'cuf', 'cub'
|
72
|
+
[ key_code.sub(/%p1%d/, '').bytes, key_binding ]
|
73
|
+
else
|
74
|
+
[ key_code.bytes, key_binding ]
|
75
|
+
end
|
76
|
+
rescue Reline::Terminfo::TerminfoError
|
77
|
+
# capname is undefined
|
78
|
+
end
|
79
|
+
end.compact.to_h
|
80
|
+
|
81
|
+
key_bindings.each_pair do |key, func|
|
82
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
83
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
84
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.set_default_key_bindings_comprehensive_list(config)
|
89
|
+
{
|
90
|
+
# Console (80x25)
|
91
|
+
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
92
|
+
[27, 91, 52, 126] => :ed_move_to_end, # End
|
93
|
+
[27, 91, 51, 126] => :key_delete, # Del
|
94
|
+
[27, 91, 65] => :ed_prev_history, # ↑
|
95
|
+
[27, 91, 66] => :ed_next_history, # ↓
|
96
|
+
[27, 91, 67] => :ed_next_char, # →
|
97
|
+
[27, 91, 68] => :ed_prev_char, # ←
|
98
|
+
|
99
|
+
# KDE
|
100
|
+
[27, 91, 72] => :ed_move_to_beg, # Home
|
101
|
+
[27, 91, 70] => :ed_move_to_end, # End
|
102
|
+
# Del is 0x08
|
103
|
+
[27, 71, 65] => :ed_prev_history, # ↑
|
104
|
+
[27, 71, 66] => :ed_next_history, # ↓
|
105
|
+
[27, 71, 67] => :ed_next_char, # →
|
106
|
+
[27, 71, 68] => :ed_prev_char, # ←
|
107
|
+
|
108
|
+
# urxvt / exoterm
|
109
|
+
[27, 91, 55, 126] => :ed_move_to_beg, # Home
|
110
|
+
[27, 91, 56, 126] => :ed_move_to_end, # End
|
111
|
+
|
112
|
+
# GNOME
|
113
|
+
[27, 79, 72] => :ed_move_to_beg, # Home
|
114
|
+
[27, 79, 70] => :ed_move_to_end, # End
|
115
|
+
# Del is 0x08
|
116
|
+
# Arrow keys are the same of KDE
|
117
|
+
|
118
|
+
# iTerm2
|
119
|
+
[27, 27, 91, 67] => :em_next_word, # Option+→, extended entry
|
120
|
+
[27, 27, 91, 68] => :ed_prev_word, # Option+←, extended entry
|
121
|
+
[195, 166] => :em_next_word, # Option+f
|
122
|
+
[195, 162] => :ed_prev_word, # Option+b
|
123
|
+
|
124
|
+
[27, 79, 65] => :ed_prev_history, # ↑
|
125
|
+
[27, 79, 66] => :ed_next_history, # ↓
|
126
|
+
[27, 79, 67] => :ed_next_char, # →
|
127
|
+
[27, 79, 68] => :ed_prev_char, # ←
|
128
|
+
}.each_pair do |key, func|
|
129
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
130
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
131
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
132
|
+
end
|
133
|
+
end
|
59
134
|
|
60
135
|
@@input = STDIN
|
61
136
|
def self.input=(val)
|
@@ -72,13 +147,15 @@ class Reline::ANSI
|
|
72
147
|
unless @@buf.empty?
|
73
148
|
return @@buf.shift
|
74
149
|
end
|
75
|
-
until c = @@input.raw(intr: true
|
76
|
-
|
150
|
+
until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
|
151
|
+
Reline.core.line_editor.resize
|
77
152
|
end
|
78
153
|
(c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
|
79
154
|
rescue Errno::EIO
|
80
155
|
# Maybe the I/O has been closed.
|
81
156
|
nil
|
157
|
+
rescue Errno::ENOTTY
|
158
|
+
nil
|
82
159
|
end
|
83
160
|
|
84
161
|
@@in_bracketed_paste_mode = false
|
@@ -129,12 +206,7 @@ class Reline::ANSI
|
|
129
206
|
unless @@buf.empty?
|
130
207
|
return false
|
131
208
|
end
|
132
|
-
|
133
|
-
if rs and rs[0]
|
134
|
-
false
|
135
|
-
else
|
136
|
-
true
|
137
|
-
end
|
209
|
+
!@@input.wait_readable(0)
|
138
210
|
end
|
139
211
|
|
140
212
|
def self.ungetc(c)
|
@@ -143,8 +215,7 @@ class Reline::ANSI
|
|
143
215
|
|
144
216
|
def self.retrieve_keybuffer
|
145
217
|
begin
|
146
|
-
|
147
|
-
return if result.nil?
|
218
|
+
return unless @@input.wait_readable(0.001)
|
148
219
|
str = @@input.read_nonblock(1024)
|
149
220
|
str.bytes.each do |c|
|
150
221
|
@@buf.push(c)
|
@@ -211,7 +282,7 @@ class Reline::ANSI
|
|
211
282
|
|
212
283
|
def self.move_cursor_up(x)
|
213
284
|
if x > 0
|
214
|
-
@@output.write "\e[#{x}A"
|
285
|
+
@@output.write "\e[#{x}A"
|
215
286
|
elsif x < 0
|
216
287
|
move_cursor_down(-x)
|
217
288
|
end
|
@@ -219,12 +290,36 @@ class Reline::ANSI
|
|
219
290
|
|
220
291
|
def self.move_cursor_down(x)
|
221
292
|
if x > 0
|
222
|
-
@@output.write "\e[#{x}B"
|
293
|
+
@@output.write "\e[#{x}B"
|
223
294
|
elsif x < 0
|
224
295
|
move_cursor_up(-x)
|
225
296
|
end
|
226
297
|
end
|
227
298
|
|
299
|
+
def self.hide_cursor
|
300
|
+
if Reline::Terminfo.enabled?
|
301
|
+
begin
|
302
|
+
@@output.write Reline::Terminfo.tigetstr('civis')
|
303
|
+
rescue Reline::Terminfo::TerminfoError
|
304
|
+
# civis is undefined
|
305
|
+
end
|
306
|
+
else
|
307
|
+
# ignored
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def self.show_cursor
|
312
|
+
if Reline::Terminfo.enabled?
|
313
|
+
begin
|
314
|
+
@@output.write Reline::Terminfo.tigetstr('cnorm')
|
315
|
+
rescue Reline::Terminfo::TerminfoError
|
316
|
+
# cnorm is undefined
|
317
|
+
end
|
318
|
+
else
|
319
|
+
# ignored
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
228
323
|
def self.erase_after_cursor
|
229
324
|
@@output.write "\e[K"
|
230
325
|
end
|
@@ -246,14 +341,10 @@ class Reline::ANSI
|
|
246
341
|
|
247
342
|
def self.prep
|
248
343
|
retrieve_keybuffer
|
249
|
-
int_handle = Signal.trap('INT', 'IGNORE')
|
250
|
-
Signal.trap('INT', int_handle)
|
251
344
|
nil
|
252
345
|
end
|
253
346
|
|
254
347
|
def self.deprep(otio)
|
255
|
-
int_handle = Signal.trap('INT', 'IGNORE')
|
256
|
-
Signal.trap('INT', int_handle)
|
257
348
|
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
258
349
|
end
|
259
350
|
end
|
data/lib/reline/config.rb
CHANGED
@@ -47,7 +47,10 @@ class Reline::Config
|
|
47
47
|
|
48
48
|
def initialize
|
49
49
|
@additional_key_bindings = {} # from inputrc
|
50
|
-
@
|
50
|
+
@additional_key_bindings[:emacs] = {}
|
51
|
+
@additional_key_bindings[:vi_insert] = {}
|
52
|
+
@additional_key_bindings[:vi_command] = {}
|
53
|
+
@oneshot_key_bindings = {}
|
51
54
|
@skip_section = nil
|
52
55
|
@if_stack = nil
|
53
56
|
@editing_mode_label = :emacs
|
@@ -63,14 +66,19 @@ class Reline::Config
|
|
63
66
|
@history_size = -1 # unlimited
|
64
67
|
@keyseq_timeout = 500
|
65
68
|
@test_mode = false
|
69
|
+
@autocompletion = false
|
70
|
+
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
|
66
71
|
end
|
67
72
|
|
68
73
|
def reset
|
69
74
|
if editing_mode_is?(:vi_command)
|
70
75
|
@editing_mode_label = :vi_insert
|
71
76
|
end
|
72
|
-
@additional_key_bindings
|
73
|
-
|
77
|
+
@additional_key_bindings.keys.each do |key|
|
78
|
+
@additional_key_bindings[key].clear
|
79
|
+
end
|
80
|
+
@oneshot_key_bindings.clear
|
81
|
+
reset_default_key_bindings
|
74
82
|
end
|
75
83
|
|
76
84
|
def editing_mode
|
@@ -85,6 +93,14 @@ class Reline::Config
|
|
85
93
|
(val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
|
86
94
|
end
|
87
95
|
|
96
|
+
def autocompletion=(val)
|
97
|
+
@autocompletion = val
|
98
|
+
end
|
99
|
+
|
100
|
+
def autocompletion
|
101
|
+
@autocompletion
|
102
|
+
end
|
103
|
+
|
88
104
|
def keymap
|
89
105
|
@key_actors[@keymap_label]
|
90
106
|
end
|
@@ -115,8 +131,12 @@ class Reline::Config
|
|
115
131
|
return home_rc_path
|
116
132
|
end
|
117
133
|
|
134
|
+
private def default_inputrc_path
|
135
|
+
@default_inputrc_path ||= inputrc_path
|
136
|
+
end
|
137
|
+
|
118
138
|
def read(file = nil)
|
119
|
-
file ||=
|
139
|
+
file ||= default_inputrc_path
|
120
140
|
begin
|
121
141
|
if file.respond_to?(:readlines)
|
122
142
|
lines = file.readlines
|
@@ -135,19 +155,46 @@ class Reline::Config
|
|
135
155
|
end
|
136
156
|
|
137
157
|
def key_bindings
|
138
|
-
#
|
139
|
-
@default_key_bindings.
|
158
|
+
# The key bindings for each editing mode will be overwritten by the user-defined ones.
|
159
|
+
kb = @key_actors[@editing_mode_label].default_key_bindings.dup
|
160
|
+
kb.merge!(@additional_key_bindings[@editing_mode_label])
|
161
|
+
kb.merge!(@oneshot_key_bindings)
|
162
|
+
kb
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_oneshot_key_binding(keystroke, target)
|
166
|
+
@oneshot_key_bindings[keystroke] = target
|
167
|
+
end
|
168
|
+
|
169
|
+
def reset_oneshot_key_bindings
|
170
|
+
@oneshot_key_bindings.clear
|
171
|
+
end
|
172
|
+
|
173
|
+
def add_default_key_binding_by_keymap(keymap, keystroke, target)
|
174
|
+
@key_actors[keymap].default_key_bindings[keystroke] = target
|
140
175
|
end
|
141
176
|
|
142
177
|
def add_default_key_binding(keystroke, target)
|
143
|
-
@default_key_bindings[keystroke] = target
|
178
|
+
@key_actors[@keymap_label].default_key_bindings[keystroke] = target
|
144
179
|
end
|
145
180
|
|
146
181
|
def reset_default_key_bindings
|
147
|
-
@
|
182
|
+
@key_actors.values.each do |ka|
|
183
|
+
ka.reset_default_key_bindings
|
184
|
+
end
|
148
185
|
end
|
149
186
|
|
150
187
|
def read_lines(lines, file = nil)
|
188
|
+
if not lines.empty? and lines.first.encoding != Reline.encoding_system_needs
|
189
|
+
begin
|
190
|
+
lines = lines.map do |l|
|
191
|
+
l.encode(Reline.encoding_system_needs)
|
192
|
+
rescue Encoding::UndefinedConversionError
|
193
|
+
mes = "The inputrc encoded in #{lines.first.encoding.name} can't be converted to the locale #{Reline.encoding_system_needs.name}."
|
194
|
+
raise Reline::ConfigEncodingConversionError.new(mes)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
151
198
|
conditions = [@skip_section, @if_stack]
|
152
199
|
@skip_section = nil
|
153
200
|
@if_stack = []
|
@@ -174,7 +221,7 @@ class Reline::Config
|
|
174
221
|
key, func_name = $1, $2
|
175
222
|
keystroke, func = bind_key(key, func_name)
|
176
223
|
next unless keystroke
|
177
|
-
@additional_key_bindings[keystroke] = func
|
224
|
+
@additional_key_bindings[@keymap_label][keystroke] = func
|
178
225
|
end
|
179
226
|
end
|
180
227
|
unless @if_stack.empty?
|
@@ -282,11 +329,8 @@ class Reline::Config
|
|
282
329
|
end
|
283
330
|
|
284
331
|
def retrieve_string(str)
|
285
|
-
if str =~ /\A"(.*)"\z/
|
286
|
-
|
287
|
-
else
|
288
|
-
parse_keyseq(str).map(&:chr).join
|
289
|
-
end
|
332
|
+
str = $1 if str =~ /\A"(.*)"\z/
|
333
|
+
parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
|
290
334
|
end
|
291
335
|
|
292
336
|
def bind_key(key, func_name)
|
@@ -344,4 +388,8 @@ class Reline::Config
|
|
344
388
|
end
|
345
389
|
ret
|
346
390
|
end
|
391
|
+
|
392
|
+
private def seven_bit_encoding?(encoding)
|
393
|
+
encoding == Encoding::US_ASCII
|
394
|
+
end
|
347
395
|
end
|
data/lib/reline/general_io.rb
CHANGED
@@ -1,21 +1,31 @@
|
|
1
1
|
require 'timeout'
|
2
|
+
require 'io/wait'
|
2
3
|
|
3
4
|
class Reline::GeneralIO
|
4
|
-
def self.reset
|
5
|
+
def self.reset(encoding: nil)
|
5
6
|
@@pasting = false
|
7
|
+
@@encoding = encoding
|
6
8
|
end
|
7
9
|
|
8
10
|
def self.encoding
|
9
|
-
|
11
|
+
if defined?(@@encoding)
|
12
|
+
@@encoding
|
13
|
+
elsif RUBY_PLATFORM =~ /mswin|mingw/
|
14
|
+
Encoding::UTF_8
|
15
|
+
else
|
16
|
+
Encoding::default_external
|
17
|
+
end
|
10
18
|
end
|
11
19
|
|
12
20
|
def self.win?
|
13
21
|
false
|
14
22
|
end
|
15
23
|
|
16
|
-
|
24
|
+
def self.set_default_key_bindings(_)
|
25
|
+
end
|
17
26
|
|
18
27
|
@@buf = []
|
28
|
+
@@input = STDIN
|
19
29
|
|
20
30
|
def self.input=(val)
|
21
31
|
@@input = val
|
@@ -27,7 +37,7 @@ class Reline::GeneralIO
|
|
27
37
|
end
|
28
38
|
c = nil
|
29
39
|
loop do
|
30
|
-
result =
|
40
|
+
result = @@input.wait_readable(0.1)
|
31
41
|
next if result.nil?
|
32
42
|
c = @@input.read(1)
|
33
43
|
break
|
@@ -4,4 +4,16 @@ class Reline::KeyActor::Base
|
|
4
4
|
def get_method(key)
|
5
5
|
self.class::MAPPING[key]
|
6
6
|
end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@default_key_bindings = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_key_bindings
|
13
|
+
@default_key_bindings
|
14
|
+
end
|
15
|
+
|
16
|
+
def reset_default_key_bindings
|
17
|
+
@default_key_bindings.clear
|
18
|
+
end
|
7
19
|
end
|
data/lib/reline/key_stroke.rb
CHANGED
@@ -1,38 +1,88 @@
|
|
1
1
|
class Reline::KeyStroke
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
def initialize(config)
|
3
|
+
@config = config
|
4
|
+
end
|
5
|
+
|
6
|
+
def compress_meta_key(ary)
|
7
|
+
return ary unless @config.convert_meta
|
8
|
+
ary.inject([]) { |result, key|
|
9
|
+
if result.size > 0 and result.last == "\e".ord
|
10
|
+
result[result.size - 1] = Reline::Key.new(key, key | 0b10000000, true)
|
11
|
+
else
|
12
|
+
result << key
|
6
13
|
end
|
14
|
+
result
|
15
|
+
}
|
16
|
+
end
|
7
17
|
|
8
|
-
|
9
|
-
|
18
|
+
def start_with?(me, other)
|
19
|
+
compressed_me = compress_meta_key(me)
|
20
|
+
compressed_other = compress_meta_key(other)
|
21
|
+
i = 0
|
22
|
+
loop do
|
23
|
+
my_c = compressed_me[i]
|
24
|
+
other_c = compressed_other[i]
|
25
|
+
other_is_last = (i + 1) == compressed_other.size
|
26
|
+
me_is_last = (i + 1) == compressed_me.size
|
27
|
+
if my_c != other_c
|
28
|
+
if other_c == "\e".ord and other_is_last and my_c.is_a?(Reline::Key) and my_c.with_meta
|
29
|
+
return true
|
30
|
+
else
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
elsif other_is_last
|
34
|
+
return true
|
35
|
+
elsif me_is_last
|
36
|
+
return false
|
10
37
|
end
|
38
|
+
i += 1
|
11
39
|
end
|
12
|
-
|
40
|
+
end
|
13
41
|
|
14
|
-
def
|
15
|
-
|
42
|
+
def equal?(me, other)
|
43
|
+
case me
|
44
|
+
when Array
|
45
|
+
compressed_me = compress_meta_key(me)
|
46
|
+
compressed_other = compress_meta_key(other)
|
47
|
+
compressed_me.size == compressed_other.size and [compressed_me, compressed_other].transpose.all?{ |i| equal?(i[0], i[1]) }
|
48
|
+
when Integer
|
49
|
+
if other.is_a?(Reline::Key)
|
50
|
+
if other.combined_char == "\e".ord
|
51
|
+
false
|
52
|
+
else
|
53
|
+
other.combined_char == me
|
54
|
+
end
|
55
|
+
else
|
56
|
+
me == other
|
57
|
+
end
|
58
|
+
when Reline::Key
|
59
|
+
if other.is_a?(Integer)
|
60
|
+
me.combined_char == other
|
61
|
+
else
|
62
|
+
me == other
|
63
|
+
end
|
64
|
+
end
|
16
65
|
end
|
17
66
|
|
18
67
|
def match_status(input)
|
19
68
|
key_mapping.keys.select { |lhs|
|
20
|
-
|
69
|
+
start_with?(lhs, input)
|
21
70
|
}.tap { |it|
|
22
|
-
return :matched if it.size == 1 && (it
|
23
|
-
return :matching if it.size == 1 && (it
|
71
|
+
return :matched if it.size == 1 && equal?(it[0], input)
|
72
|
+
return :matching if it.size == 1 && !equal?(it[0], input)
|
24
73
|
return :matched if it.max_by(&:size)&.size&.< input.size
|
25
74
|
return :matching if it.size > 1
|
26
75
|
}
|
27
76
|
key_mapping.keys.select { |lhs|
|
28
|
-
|
77
|
+
start_with?(input, lhs)
|
29
78
|
}.tap { |it|
|
30
79
|
return it.size > 0 ? :matched : :unmatched
|
31
80
|
}
|
32
81
|
end
|
33
82
|
|
34
83
|
def expand(input)
|
35
|
-
|
84
|
+
input = compress_meta_key(input)
|
85
|
+
lhs = key_mapping.keys.select { |item| start_with?(input, item) }.sort_by(&:size).last
|
36
86
|
return input unless lhs
|
37
87
|
rhs = key_mapping[lhs]
|
38
88
|
|