reline 0.2.7 → 0.3.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/lib/reline/ansi.rb +64 -22
- data/lib/reline/config.rb +34 -3
- data/lib/reline/general_io.rb +3 -1
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_stroke.rb +64 -14
- data/lib/reline/line_editor.rb +630 -72
- data/lib/reline/terminfo.rb +12 -4
- data/lib/reline/unicode.rb +42 -3
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +151 -49
- data/lib/reline.rb +127 -22
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e8c2181e5eb21934546d6b768715cb47eab6e1ea1edb2fd2300f43af62dc2b1
|
4
|
+
data.tar.gz: 99a61e2729d4b5d150f6f59d99d6de235ea4f1aab6ac262c92fff6a77101b85f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96ceefe0bf71e7e44ccd2a5970b35fc44019af9d7da4d2e04ac1d827eb6bc6a201e679a5e40c4db976554159286f16e39cdeac68bcf69c2c5abeb00a2045e563
|
7
|
+
data.tar.gz: 251c2029df6b3073010ad6a591fb216990124ed4f9e00f533e79461be66c6a2383ea56c1554dd273e4af3cfc9358dee37b76a1b4bff25a1867972201bfebace3
|
data/lib/reline/ansi.rb
CHANGED
@@ -4,6 +4,19 @@ require 'timeout'
|
|
4
4
|
require_relative 'terminfo'
|
5
5
|
|
6
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
|
+
|
7
20
|
if Reline::Terminfo.enabled?
|
8
21
|
Reline::Terminfo.setupterm(0, 2)
|
9
22
|
end
|
@@ -33,6 +46,12 @@ class Reline::ANSI
|
|
33
46
|
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
34
47
|
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
35
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
|
36
55
|
{
|
37
56
|
# default bindings
|
38
57
|
[27, 32] => :em_set_mark, # M-<space>
|
@@ -43,20 +62,23 @@ class Reline::ANSI
|
|
43
62
|
end
|
44
63
|
|
45
64
|
def self.set_default_key_bindings_terminfo(config)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
Reline::Terminfo
|
58
|
-
|
59
|
-
|
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|
|
60
82
|
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
61
83
|
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
62
84
|
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
@@ -125,8 +147,8 @@ class Reline::ANSI
|
|
125
147
|
unless @@buf.empty?
|
126
148
|
return @@buf.shift
|
127
149
|
end
|
128
|
-
until c = @@input.raw(intr: true
|
129
|
-
|
150
|
+
until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
|
151
|
+
Reline.core.line_editor.resize
|
130
152
|
end
|
131
153
|
(c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
|
132
154
|
rescue Errno::EIO
|
@@ -260,7 +282,7 @@ class Reline::ANSI
|
|
260
282
|
|
261
283
|
def self.move_cursor_up(x)
|
262
284
|
if x > 0
|
263
|
-
@@output.write "\e[#{x}A"
|
285
|
+
@@output.write "\e[#{x}A"
|
264
286
|
elsif x < 0
|
265
287
|
move_cursor_down(-x)
|
266
288
|
end
|
@@ -268,12 +290,36 @@ class Reline::ANSI
|
|
268
290
|
|
269
291
|
def self.move_cursor_down(x)
|
270
292
|
if x > 0
|
271
|
-
@@output.write "\e[#{x}B"
|
293
|
+
@@output.write "\e[#{x}B"
|
272
294
|
elsif x < 0
|
273
295
|
move_cursor_up(-x)
|
274
296
|
end
|
275
297
|
end
|
276
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
|
+
|
277
323
|
def self.erase_after_cursor
|
278
324
|
@@output.write "\e[K"
|
279
325
|
end
|
@@ -295,14 +341,10 @@ class Reline::ANSI
|
|
295
341
|
|
296
342
|
def self.prep
|
297
343
|
retrieve_keybuffer
|
298
|
-
int_handle = Signal.trap('INT', 'IGNORE')
|
299
|
-
Signal.trap('INT', int_handle)
|
300
344
|
nil
|
301
345
|
end
|
302
346
|
|
303
347
|
def self.deprep(otio)
|
304
|
-
int_handle = Signal.trap('INT', 'IGNORE')
|
305
|
-
Signal.trap('INT', int_handle)
|
306
348
|
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
307
349
|
end
|
308
350
|
end
|
data/lib/reline/config.rb
CHANGED
@@ -50,6 +50,7 @@ class Reline::Config
|
|
50
50
|
@additional_key_bindings[:emacs] = {}
|
51
51
|
@additional_key_bindings[:vi_insert] = {}
|
52
52
|
@additional_key_bindings[:vi_command] = {}
|
53
|
+
@oneshot_key_bindings = {}
|
53
54
|
@skip_section = nil
|
54
55
|
@if_stack = nil
|
55
56
|
@editing_mode_label = :emacs
|
@@ -65,6 +66,8 @@ class Reline::Config
|
|
65
66
|
@history_size = -1 # unlimited
|
66
67
|
@keyseq_timeout = 500
|
67
68
|
@test_mode = false
|
69
|
+
@autocompletion = false
|
70
|
+
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
|
68
71
|
end
|
69
72
|
|
70
73
|
def reset
|
@@ -74,6 +77,7 @@ class Reline::Config
|
|
74
77
|
@additional_key_bindings.keys.each do |key|
|
75
78
|
@additional_key_bindings[key].clear
|
76
79
|
end
|
80
|
+
@oneshot_key_bindings.clear
|
77
81
|
reset_default_key_bindings
|
78
82
|
end
|
79
83
|
|
@@ -89,6 +93,14 @@ class Reline::Config
|
|
89
93
|
(val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
|
90
94
|
end
|
91
95
|
|
96
|
+
def autocompletion=(val)
|
97
|
+
@autocompletion = val
|
98
|
+
end
|
99
|
+
|
100
|
+
def autocompletion
|
101
|
+
@autocompletion
|
102
|
+
end
|
103
|
+
|
92
104
|
def keymap
|
93
105
|
@key_actors[@keymap_label]
|
94
106
|
end
|
@@ -119,8 +131,12 @@ class Reline::Config
|
|
119
131
|
return home_rc_path
|
120
132
|
end
|
121
133
|
|
134
|
+
private def default_inputrc_path
|
135
|
+
@default_inputrc_path ||= inputrc_path
|
136
|
+
end
|
137
|
+
|
122
138
|
def read(file = nil)
|
123
|
-
file ||=
|
139
|
+
file ||= default_inputrc_path
|
124
140
|
begin
|
125
141
|
if file.respond_to?(:readlines)
|
126
142
|
lines = file.readlines
|
@@ -139,8 +155,19 @@ class Reline::Config
|
|
139
155
|
end
|
140
156
|
|
141
157
|
def key_bindings
|
142
|
-
#
|
143
|
-
@key_actors[@editing_mode_label].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
|
144
171
|
end
|
145
172
|
|
146
173
|
def add_default_key_binding_by_keymap(keymap, keystroke, target)
|
@@ -361,4 +388,8 @@ class Reline::Config
|
|
361
388
|
end
|
362
389
|
ret
|
363
390
|
end
|
391
|
+
|
392
|
+
private def seven_bit_encoding?(encoding)
|
393
|
+
encoding == Encoding::US_ASCII
|
394
|
+
end
|
364
395
|
end
|
data/lib/reline/general_io.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'timeout'
|
2
|
+
require 'io/wait'
|
2
3
|
|
3
4
|
class Reline::GeneralIO
|
4
5
|
def self.reset(encoding: nil)
|
@@ -24,6 +25,7 @@ class Reline::GeneralIO
|
|
24
25
|
end
|
25
26
|
|
26
27
|
@@buf = []
|
28
|
+
@@input = STDIN
|
27
29
|
|
28
30
|
def self.input=(val)
|
29
31
|
@@input = val
|
@@ -35,7 +37,7 @@ class Reline::GeneralIO
|
|
35
37
|
end
|
36
38
|
c = nil
|
37
39
|
loop do
|
38
|
-
result =
|
40
|
+
result = @@input.wait_readable(0.1)
|
39
41
|
next if result.nil?
|
40
42
|
c = @@input.read(1)
|
41
43
|
break
|
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
|
|