reline 0.2.5 → 0.3.1
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/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
|
|