reline 0.5.10 → 0.6.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/config.rb +22 -26
- data/lib/reline/history.rb +3 -3
- data/lib/reline/io/ansi.rb +64 -111
- data/lib/reline/io/dumb.rb +16 -2
- data/lib/reline/io/windows.rb +77 -60
- data/lib/reline/io.rb +14 -0
- data/lib/reline/key_actor/base.rb +10 -4
- data/lib/reline/key_actor/emacs.rb +96 -96
- data/lib/reline/key_actor/vi_command.rb +182 -182
- data/lib/reline/key_actor/vi_insert.rb +137 -137
- data/lib/reline/key_stroke.rb +26 -16
- data/lib/reline/line_editor.rb +238 -404
- data/lib/reline/unicode.rb +140 -396
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +18 -18
- metadata +6 -4
- data/lib/reline/terminfo.rb +0 -158
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8fb968cf5a8c86917a35ba517a627df5a5dc51cb9b75cef5fdd3183042cd3224
|
4
|
+
data.tar.gz: b02916eae200fc26a65aafde67e450e97d26450d7a1b694db37d1dce8c43c7a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c35aff3260b914d6137b87ba0d7031ce80ddf10896191dc79302b6e79749d55590b997b7705eac4ec03046fa95f19d2ff3be4d6f566491c9f40e99813e221ff
|
7
|
+
data.tar.gz: cd15de1099bcb91eb69881c20f969e6ece3e4d8548565d97d743e01d082d63aa4554381f86fbb7d29068ca84e8699ed1a91cc30a77e2b7aadb58d1d2338998c8
|
data/lib/reline/config.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Reline::Config
|
2
2
|
attr_reader :test_mode
|
3
3
|
|
4
|
-
KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)
|
4
|
+
KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)-\\(?:M|Meta)-[A-Za-z_]|\\(?:M|Meta)-\\(?:C|Control)-[A-Za-z_]|\\e|\\[\\\"\'abdfnrtv]|\\\d{1,3}|\\x\h{1,2}|./
|
5
5
|
|
6
6
|
class InvalidInputrc < RuntimeError
|
7
7
|
attr_accessor :file, :lineno
|
@@ -194,13 +194,14 @@ class Reline::Config
|
|
194
194
|
# value ignores everything after a space, raw_value does not.
|
195
195
|
var, value, raw_value = $1.downcase, $2.partition(' ').first, $2
|
196
196
|
bind_variable(var, value, raw_value)
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
197
|
+
when /^\s*(?:M|Meta)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
198
|
+
bind_key("\"\\M-#$1\"", $2)
|
199
|
+
when /^\s*(?:C|Control)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
200
|
+
bind_key("\"\\C-#$1\"", $2)
|
201
|
+
when /^\s*(?:(?:C|Control)-(?:M|Meta)|(?:M|Meta)-(?:C|Control))-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
202
|
+
bind_key("\"\\M-\\C-#$1\"", $2)
|
203
|
+
when /^\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
204
|
+
bind_key($1, $2)
|
204
205
|
end
|
205
206
|
end
|
206
207
|
unless if_stack.empty?
|
@@ -310,7 +311,12 @@ class Reline::Config
|
|
310
311
|
parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
|
311
312
|
end
|
312
313
|
|
313
|
-
def bind_key(key,
|
314
|
+
def bind_key(key, value)
|
315
|
+
keystroke, func = parse_key_binding(key, value)
|
316
|
+
@additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func) if keystroke
|
317
|
+
end
|
318
|
+
|
319
|
+
def parse_key_binding(key, func_name)
|
314
320
|
if key =~ /\A"(.*)"\z/
|
315
321
|
keyseq = parse_keyseq($1)
|
316
322
|
else
|
@@ -319,27 +325,19 @@ class Reline::Config
|
|
319
325
|
if func_name =~ /"(.*)"/
|
320
326
|
func = parse_keyseq($1)
|
321
327
|
else
|
322
|
-
func = func_name.tr(?-, ?_).to_sym # It must be macro.
|
328
|
+
func = func_name.split.first.tr(?-, ?_).to_sym # It must be macro.
|
323
329
|
end
|
324
330
|
[keyseq, func]
|
325
331
|
end
|
326
332
|
|
327
333
|
def key_notation_to_code(notation)
|
328
334
|
case notation
|
335
|
+
when /(?:\\(?:C|Control)-\\(?:M|Meta)|\\(?:M|Meta)-\\(?:C|Control))-([A-Za-z_])/
|
336
|
+
[?\e.ord, $1.ord % 32]
|
329
337
|
when /\\(?:C|Control)-([A-Za-z_])/
|
330
|
-
(
|
338
|
+
($1.upcase.ord % 32)
|
331
339
|
when /\\(?:M|Meta)-([0-9A-Za-z_])/
|
332
|
-
|
333
|
-
case $1
|
334
|
-
when /[0-9]/
|
335
|
-
?\M-0.bytes.first + (modified_key.ord - ?0.ord)
|
336
|
-
when /[A-Z]/
|
337
|
-
?\M-A.bytes.first + (modified_key.ord - ?A.ord)
|
338
|
-
when /[a-z]/
|
339
|
-
?\M-a.bytes.first + (modified_key.ord - ?a.ord)
|
340
|
-
end
|
341
|
-
when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/
|
342
|
-
# 129 M-^A
|
340
|
+
[?\e.ord, $1.ord]
|
343
341
|
when /\\(\d{1,3})/ then $1.to_i(8) # octal
|
344
342
|
when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
|
345
343
|
when "\\e" then ?\e.ord
|
@@ -359,11 +357,9 @@ class Reline::Config
|
|
359
357
|
end
|
360
358
|
|
361
359
|
def parse_keyseq(str)
|
362
|
-
|
363
|
-
|
364
|
-
ret << key_notation_to_code($&)
|
360
|
+
str.scan(KEYSEQ_PATTERN).flat_map do |notation|
|
361
|
+
key_notation_to_code(notation)
|
365
362
|
end
|
366
|
-
ret
|
367
363
|
end
|
368
364
|
|
369
365
|
def reload
|
data/lib/reline/history.rb
CHANGED
@@ -19,7 +19,7 @@ class Reline::History < Array
|
|
19
19
|
|
20
20
|
def []=(index, val)
|
21
21
|
index = check_index(index)
|
22
|
-
super(index,
|
22
|
+
super(index, Reline::Unicode.safe_encode(val, Reline.encoding_system_needs))
|
23
23
|
end
|
24
24
|
|
25
25
|
def concat(*val)
|
@@ -45,7 +45,7 @@ class Reline::History < Array
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
super(*(val.map{ |v|
|
48
|
-
|
48
|
+
Reline::Unicode.safe_encode(v, Reline.encoding_system_needs)
|
49
49
|
}))
|
50
50
|
end
|
51
51
|
|
@@ -56,7 +56,7 @@ class Reline::History < Array
|
|
56
56
|
if @config.history_size.positive?
|
57
57
|
shift if size + 1 > @config.history_size
|
58
58
|
end
|
59
|
-
super(
|
59
|
+
super(Reline::Unicode.safe_encode(val, Reline.encoding_system_needs))
|
60
60
|
end
|
61
61
|
|
62
62
|
private def check_index(index)
|
data/lib/reline/io/ansi.rb
CHANGED
@@ -29,29 +29,27 @@ class Reline::ANSI < Reline::IO
|
|
29
29
|
'H' => [:ed_move_to_beg, {}],
|
30
30
|
}
|
31
31
|
|
32
|
-
|
33
|
-
Reline::Terminfo.setupterm(0, 2)
|
34
|
-
end
|
32
|
+
attr_writer :input, :output
|
35
33
|
|
36
34
|
def initialize
|
37
35
|
@input = STDIN
|
38
36
|
@output = STDOUT
|
39
37
|
@buf = []
|
38
|
+
@output_buffer = nil
|
40
39
|
@old_winch_handler = nil
|
41
40
|
end
|
42
41
|
|
43
42
|
def encoding
|
43
|
+
@input.external_encoding || Encoding.default_external
|
44
|
+
rescue IOError
|
45
|
+
# STDIN.external_encoding raises IOError in Ruby <= 3.0 when STDIN is closed
|
44
46
|
Encoding.default_external
|
45
47
|
end
|
46
48
|
|
47
|
-
def set_default_key_bindings(config
|
49
|
+
def set_default_key_bindings(config)
|
48
50
|
set_bracketed_paste_key_bindings(config)
|
49
51
|
set_default_key_bindings_ansi_cursor(config)
|
50
|
-
|
51
|
-
set_default_key_bindings_terminfo(config)
|
52
|
-
else
|
53
|
-
set_default_key_bindings_comprehensive_list(config)
|
54
|
-
end
|
52
|
+
set_default_key_bindings_comprehensive_list(config)
|
55
53
|
{
|
56
54
|
[27, 91, 90] => :completion_journey_up, # S-Tab
|
57
55
|
}.each_pair do |key, func|
|
@@ -75,7 +73,10 @@ class Reline::ANSI < Reline::IO
|
|
75
73
|
|
76
74
|
def set_default_key_bindings_ansi_cursor(config)
|
77
75
|
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
|
78
|
-
bindings = [
|
76
|
+
bindings = [
|
77
|
+
["\e[#{char}", default_func], # CSI + char
|
78
|
+
["\eO#{char}", default_func] # SS3 + char, application cursor key mode
|
79
|
+
]
|
79
80
|
if modifiers[:ctrl]
|
80
81
|
# CSI + ctrl_key_modifier + char
|
81
82
|
bindings << ["\e[1;5#{char}", modifiers[:ctrl]]
|
@@ -95,23 +96,6 @@ class Reline::ANSI < Reline::IO
|
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
98
|
-
def set_default_key_bindings_terminfo(config)
|
99
|
-
key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
|
100
|
-
begin
|
101
|
-
key_code = Reline::Terminfo.tigetstr(capname)
|
102
|
-
[ key_code.bytes, key_binding ]
|
103
|
-
rescue Reline::Terminfo::TerminfoError
|
104
|
-
# capname is undefined
|
105
|
-
end
|
106
|
-
end.compact.to_h
|
107
|
-
|
108
|
-
key_bindings.each_pair do |key, func|
|
109
|
-
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
110
|
-
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
111
|
-
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
99
|
def set_default_key_bindings_comprehensive_list(config)
|
116
100
|
{
|
117
101
|
# xterm
|
@@ -123,27 +107,9 @@ class Reline::ANSI < Reline::IO
|
|
123
107
|
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
124
108
|
[27, 91, 52, 126] => :ed_move_to_end, # End
|
125
109
|
|
126
|
-
# KDE
|
127
|
-
# Del is 0x08
|
128
|
-
[27, 71, 65] => :ed_prev_history, # ↑
|
129
|
-
[27, 71, 66] => :ed_next_history, # ↓
|
130
|
-
[27, 71, 67] => :ed_next_char, # →
|
131
|
-
[27, 71, 68] => :ed_prev_char, # ←
|
132
|
-
|
133
110
|
# urxvt / exoterm
|
134
111
|
[27, 91, 55, 126] => :ed_move_to_beg, # Home
|
135
112
|
[27, 91, 56, 126] => :ed_move_to_end, # End
|
136
|
-
|
137
|
-
# GNOME
|
138
|
-
[27, 79, 72] => :ed_move_to_beg, # Home
|
139
|
-
[27, 79, 70] => :ed_move_to_end, # End
|
140
|
-
# Del is 0x08
|
141
|
-
# Arrow keys are the same of KDE
|
142
|
-
|
143
|
-
[27, 79, 65] => :ed_prev_history, # ↑
|
144
|
-
[27, 79, 66] => :ed_next_history, # ↓
|
145
|
-
[27, 79, 67] => :ed_next_char, # →
|
146
|
-
[27, 79, 68] => :ed_prev_char, # ←
|
147
113
|
}.each_pair do |key, func|
|
148
114
|
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
149
115
|
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
@@ -151,14 +117,6 @@ class Reline::ANSI < Reline::IO
|
|
151
117
|
end
|
152
118
|
end
|
153
119
|
|
154
|
-
def input=(val)
|
155
|
-
@input = val
|
156
|
-
end
|
157
|
-
|
158
|
-
def output=(val)
|
159
|
-
@output = val
|
160
|
-
end
|
161
|
-
|
162
120
|
def with_raw_input
|
163
121
|
if @input.tty?
|
164
122
|
@input.raw(intr: true) { yield }
|
@@ -245,52 +203,59 @@ class Reline::ANSI < Reline::IO
|
|
245
203
|
self
|
246
204
|
end
|
247
205
|
|
248
|
-
def
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
@
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
break if m
|
261
|
-
end
|
262
|
-
(m.pre_match + m.post_match).chars.reverse_each do |ch|
|
263
|
-
stdin.ungetc ch
|
206
|
+
private def cursor_pos_internal(timeout:)
|
207
|
+
match = nil
|
208
|
+
@input.raw do |stdin|
|
209
|
+
@output << "\e[6n"
|
210
|
+
@output.flush
|
211
|
+
timeout_at = Time.now + timeout
|
212
|
+
buf = +''
|
213
|
+
while (wait = timeout_at - Time.now) > 0 && stdin.wait_readable(wait)
|
214
|
+
buf << stdin.readpartial(1024)
|
215
|
+
if (match = buf.match(/\e\[(?<row>\d+);(?<column>\d+)R/))
|
216
|
+
buf = match.pre_match + match.post_match
|
217
|
+
break
|
264
218
|
end
|
265
219
|
end
|
266
|
-
|
267
|
-
|
268
|
-
else
|
269
|
-
begin
|
270
|
-
buf = @output.pread(@output.pos, 0)
|
271
|
-
row = buf.count("\n")
|
272
|
-
column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
|
273
|
-
rescue Errno::ESPIPE, IOError
|
274
|
-
# Just returns column 1 for ambiguous width because this I/O is not
|
275
|
-
# tty and can't seek.
|
276
|
-
row = 0
|
277
|
-
column = 1
|
220
|
+
buf.chars.reverse_each do |ch|
|
221
|
+
stdin.ungetc ch
|
278
222
|
end
|
279
223
|
end
|
280
|
-
|
224
|
+
[match[:column].to_i - 1, match[:row].to_i - 1] if match
|
225
|
+
end
|
226
|
+
|
227
|
+
def cursor_pos
|
228
|
+
col, row = cursor_pos_internal(timeout: 0.5) if both_tty?
|
229
|
+
Reline::CursorPos.new(col || 0, row || 0)
|
281
230
|
end
|
282
231
|
|
283
232
|
def both_tty?
|
284
233
|
@input.tty? && @output.tty?
|
285
234
|
end
|
286
235
|
|
236
|
+
def write(string)
|
237
|
+
if @output_buffer
|
238
|
+
@output_buffer << string
|
239
|
+
else
|
240
|
+
@output.write(string)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def buffered_output
|
245
|
+
@output_buffer = +''
|
246
|
+
yield
|
247
|
+
@output.write(@output_buffer)
|
248
|
+
ensure
|
249
|
+
@output_buffer = nil
|
250
|
+
end
|
251
|
+
|
287
252
|
def move_cursor_column(x)
|
288
|
-
|
253
|
+
write "\e[#{x + 1}G"
|
289
254
|
end
|
290
255
|
|
291
256
|
def move_cursor_up(x)
|
292
257
|
if x > 0
|
293
|
-
|
258
|
+
write "\e[#{x}A"
|
294
259
|
elsif x < 0
|
295
260
|
move_cursor_down(-x)
|
296
261
|
end
|
@@ -298,38 +263,22 @@ class Reline::ANSI < Reline::IO
|
|
298
263
|
|
299
264
|
def move_cursor_down(x)
|
300
265
|
if x > 0
|
301
|
-
|
266
|
+
write "\e[#{x}B"
|
302
267
|
elsif x < 0
|
303
268
|
move_cursor_up(-x)
|
304
269
|
end
|
305
270
|
end
|
306
271
|
|
307
272
|
def hide_cursor
|
308
|
-
|
309
|
-
if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
|
310
|
-
begin
|
311
|
-
seq = Reline::Terminfo.tigetstr('civis')
|
312
|
-
rescue Reline::Terminfo::TerminfoError
|
313
|
-
# civis is undefined
|
314
|
-
end
|
315
|
-
end
|
316
|
-
@output.write seq
|
273
|
+
write "\e[?25l"
|
317
274
|
end
|
318
275
|
|
319
276
|
def show_cursor
|
320
|
-
|
321
|
-
if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
|
322
|
-
begin
|
323
|
-
seq = Reline::Terminfo.tigetstr('cnorm')
|
324
|
-
rescue Reline::Terminfo::TerminfoError
|
325
|
-
# cnorm is undefined
|
326
|
-
end
|
327
|
-
end
|
328
|
-
@output.write seq
|
277
|
+
write "\e[?25h"
|
329
278
|
end
|
330
279
|
|
331
280
|
def erase_after_cursor
|
332
|
-
|
281
|
+
write "\e[K"
|
333
282
|
end
|
334
283
|
|
335
284
|
# This only works when the cursor is at the bottom of the scroll range
|
@@ -337,20 +286,24 @@ class Reline::ANSI < Reline::IO
|
|
337
286
|
def scroll_down(x)
|
338
287
|
return if x.zero?
|
339
288
|
# We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
|
340
|
-
|
289
|
+
write "\n" * x
|
341
290
|
end
|
342
291
|
|
343
292
|
def clear_screen
|
344
|
-
|
345
|
-
|
293
|
+
write "\e[2J"
|
294
|
+
write "\e[1;1H"
|
346
295
|
end
|
347
296
|
|
348
297
|
def set_winch_handler(&handler)
|
349
|
-
@old_winch_handler = Signal.trap('WINCH'
|
350
|
-
|
298
|
+
@old_winch_handler = Signal.trap('WINCH') do |arg|
|
299
|
+
handler.call
|
300
|
+
@old_winch_handler.call(arg) if @old_winch_handler.respond_to?(:call)
|
301
|
+
end
|
302
|
+
@old_cont_handler = Signal.trap('CONT') do |arg|
|
351
303
|
@input.raw!(intr: true) if @input.tty?
|
352
304
|
# Rerender the screen. Note that screen size might be changed while suspended.
|
353
305
|
handler.call
|
306
|
+
@old_cont_handler.call(arg) if @old_cont_handler.respond_to?(:call)
|
354
307
|
end
|
355
308
|
rescue ArgumentError
|
356
309
|
# Signal.trap may raise an ArgumentError if the platform doesn't support the signal.
|
@@ -358,14 +311,14 @@ class Reline::ANSI < Reline::IO
|
|
358
311
|
|
359
312
|
def prep
|
360
313
|
# Enable bracketed paste
|
361
|
-
|
314
|
+
write "\e[?2004h" if Reline.core.config.enable_bracketed_paste && both_tty?
|
362
315
|
retrieve_keybuffer
|
363
316
|
nil
|
364
317
|
end
|
365
318
|
|
366
319
|
def deprep(otio)
|
367
320
|
# Disable bracketed paste
|
368
|
-
|
321
|
+
write "\e[?2004l" if Reline.core.config.enable_bracketed_paste && both_tty?
|
369
322
|
Signal.trap('WINCH', @old_winch_handler) if @old_winch_handler
|
370
323
|
Signal.trap('CONT', @old_cont_handler) if @old_cont_handler
|
371
324
|
end
|
data/lib/reline/io/dumb.rb
CHANGED
@@ -3,8 +3,11 @@ require 'io/wait'
|
|
3
3
|
class Reline::Dumb < Reline::IO
|
4
4
|
RESET_COLOR = '' # Do not send color reset sequence
|
5
5
|
|
6
|
+
attr_writer :output
|
7
|
+
|
6
8
|
def initialize(encoding: nil)
|
7
9
|
@input = STDIN
|
10
|
+
@output = STDOUT
|
8
11
|
@buf = []
|
9
12
|
@pasting = false
|
10
13
|
@encoding = encoding
|
@@ -21,8 +24,11 @@ class Reline::Dumb < Reline::IO
|
|
21
24
|
elsif RUBY_PLATFORM =~ /mswin|mingw/
|
22
25
|
Encoding::UTF_8
|
23
26
|
else
|
24
|
-
Encoding
|
27
|
+
@input.external_encoding || Encoding.default_external
|
25
28
|
end
|
29
|
+
rescue IOError
|
30
|
+
# STDIN.external_encoding raises IOError in Ruby <= 3.0 when STDIN is closed
|
31
|
+
Encoding.default_external
|
26
32
|
end
|
27
33
|
|
28
34
|
def set_default_key_bindings(_)
|
@@ -36,6 +42,14 @@ class Reline::Dumb < Reline::IO
|
|
36
42
|
yield
|
37
43
|
end
|
38
44
|
|
45
|
+
def write(string)
|
46
|
+
@output.write(string)
|
47
|
+
end
|
48
|
+
|
49
|
+
def buffered_output
|
50
|
+
yield
|
51
|
+
end
|
52
|
+
|
39
53
|
def getc(_timeout_second)
|
40
54
|
unless @buf.empty?
|
41
55
|
return @buf.shift
|
@@ -60,7 +74,7 @@ class Reline::Dumb < Reline::IO
|
|
60
74
|
end
|
61
75
|
|
62
76
|
def cursor_pos
|
63
|
-
Reline::CursorPos.new(
|
77
|
+
Reline::CursorPos.new(0, 0)
|
64
78
|
end
|
65
79
|
|
66
80
|
def hide_cursor
|