reline 0.5.10 → 0.5.12
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 +31 -93
- data/lib/reline/io/dumb.rb +5 -2
- data/lib/reline/io/windows.rb +66 -59
- data/lib/reline/key_stroke.rb +19 -8
- data/lib/reline/line_editor.rb +163 -260
- data/lib/reline/unicode.rb +58 -68
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +6 -6
- metadata +3 -3
- 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: 14c0b8843530985ab335eb7dbb365d72708214a67b5a858d75d0aebe605050c3
|
4
|
+
data.tar.gz: ba60692647a18e520d1b8ed0fff6ac44b11048b3c8f2f2050a5651955db21c89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 242efcf7910a3981d12f7238df77b20d8c0a4fffb735bc59d53f8662e8fbb149d53c8081adf03bbb2b237f576a6f942ba66f01d6b46e1de29617fb29edb89a58
|
7
|
+
data.tar.gz: 9d80fef9e52a79300e6163b9dd5141fe412fc3caa32f108f9158108d54abef6ec23b93fc018afd5efdf374c38713e0973afe06adbbe148cae457ca45dd7024b7
|
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,10 +29,6 @@ class Reline::ANSI < Reline::IO
|
|
29
29
|
'H' => [:ed_move_to_beg, {}],
|
30
30
|
}
|
31
31
|
|
32
|
-
if Reline::Terminfo.enabled?
|
33
|
-
Reline::Terminfo.setupterm(0, 2)
|
34
|
-
end
|
35
|
-
|
36
32
|
def initialize
|
37
33
|
@input = STDIN
|
38
34
|
@output = STDOUT
|
@@ -41,17 +37,16 @@ class Reline::ANSI < Reline::IO
|
|
41
37
|
end
|
42
38
|
|
43
39
|
def encoding
|
40
|
+
@input.external_encoding || Encoding.default_external
|
41
|
+
rescue IOError
|
42
|
+
# STDIN.external_encoding raises IOError in Ruby <= 3.0 when STDIN is closed
|
44
43
|
Encoding.default_external
|
45
44
|
end
|
46
45
|
|
47
|
-
def set_default_key_bindings(config
|
46
|
+
def set_default_key_bindings(config)
|
48
47
|
set_bracketed_paste_key_bindings(config)
|
49
48
|
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
|
49
|
+
set_default_key_bindings_comprehensive_list(config)
|
55
50
|
{
|
56
51
|
[27, 91, 90] => :completion_journey_up, # S-Tab
|
57
52
|
}.each_pair do |key, func|
|
@@ -75,7 +70,10 @@ class Reline::ANSI < Reline::IO
|
|
75
70
|
|
76
71
|
def set_default_key_bindings_ansi_cursor(config)
|
77
72
|
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
|
78
|
-
bindings = [
|
73
|
+
bindings = [
|
74
|
+
["\e[#{char}", default_func], # CSI + char
|
75
|
+
["\eO#{char}", default_func] # SS3 + char, application cursor key mode
|
76
|
+
]
|
79
77
|
if modifiers[:ctrl]
|
80
78
|
# CSI + ctrl_key_modifier + char
|
81
79
|
bindings << ["\e[1;5#{char}", modifiers[:ctrl]]
|
@@ -95,23 +93,6 @@ class Reline::ANSI < Reline::IO
|
|
95
93
|
end
|
96
94
|
end
|
97
95
|
|
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
96
|
def set_default_key_bindings_comprehensive_list(config)
|
116
97
|
{
|
117
98
|
# xterm
|
@@ -123,27 +104,9 @@ class Reline::ANSI < Reline::IO
|
|
123
104
|
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
124
105
|
[27, 91, 52, 126] => :ed_move_to_end, # End
|
125
106
|
|
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
107
|
# urxvt / exoterm
|
134
108
|
[27, 91, 55, 126] => :ed_move_to_beg, # Home
|
135
109
|
[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
110
|
}.each_pair do |key, func|
|
148
111
|
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
149
112
|
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
@@ -245,39 +208,30 @@ class Reline::ANSI < Reline::IO
|
|
245
208
|
self
|
246
209
|
end
|
247
210
|
|
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
|
211
|
+
private def cursor_pos_internal(timeout:)
|
212
|
+
match = nil
|
213
|
+
@input.raw do |stdin|
|
214
|
+
@output << "\e[6n"
|
215
|
+
@output.flush
|
216
|
+
timeout_at = Time.now + timeout
|
217
|
+
buf = +''
|
218
|
+
while (wait = timeout_at - Time.now) > 0 && stdin.wait_readable(wait)
|
219
|
+
buf << stdin.readpartial(1024)
|
220
|
+
if (match = buf.match(/\e\[(?<row>\d+);(?<column>\d+)R/))
|
221
|
+
buf = match.pre_match + match.post_match
|
222
|
+
break
|
264
223
|
end
|
265
224
|
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
|
225
|
+
buf.chars.reverse_each do |ch|
|
226
|
+
stdin.ungetc ch
|
278
227
|
end
|
279
228
|
end
|
280
|
-
|
229
|
+
[match[:column].to_i - 1, match[:row].to_i - 1] if match
|
230
|
+
end
|
231
|
+
|
232
|
+
def cursor_pos
|
233
|
+
col, row = cursor_pos_internal(timeout: 0.5) if both_tty?
|
234
|
+
Reline::CursorPos.new(col || 0, row || 0)
|
281
235
|
end
|
282
236
|
|
283
237
|
def both_tty?
|
@@ -305,27 +259,11 @@ class Reline::ANSI < Reline::IO
|
|
305
259
|
end
|
306
260
|
|
307
261
|
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
|
262
|
+
@output.write "\e[?25l"
|
317
263
|
end
|
318
264
|
|
319
265
|
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
|
266
|
+
@output.write "\e[?25h"
|
329
267
|
end
|
330
268
|
|
331
269
|
def erase_after_cursor
|
data/lib/reline/io/dumb.rb
CHANGED
@@ -21,8 +21,11 @@ class Reline::Dumb < Reline::IO
|
|
21
21
|
elsif RUBY_PLATFORM =~ /mswin|mingw/
|
22
22
|
Encoding::UTF_8
|
23
23
|
else
|
24
|
-
Encoding
|
24
|
+
@input.external_encoding || Encoding.default_external
|
25
25
|
end
|
26
|
+
rescue IOError
|
27
|
+
# STDIN.external_encoding raises IOError in Ruby <= 3.0 when STDIN is closed
|
28
|
+
Encoding.default_external
|
26
29
|
end
|
27
30
|
|
28
31
|
def set_default_key_bindings(_)
|
@@ -60,7 +63,7 @@ class Reline::Dumb < Reline::IO
|
|
60
63
|
end
|
61
64
|
|
62
65
|
def cursor_pos
|
63
|
-
Reline::CursorPos.new(
|
66
|
+
Reline::CursorPos.new(0, 0)
|
64
67
|
end
|
65
68
|
|
66
69
|
def hide_cursor
|
data/lib/reline/io/windows.rb
CHANGED
@@ -157,6 +157,7 @@ class Reline::Windows < Reline::IO
|
|
157
157
|
STD_OUTPUT_HANDLE = -11
|
158
158
|
FILE_TYPE_PIPE = 0x0003
|
159
159
|
FILE_NAME_INFO = 2
|
160
|
+
ENABLE_WRAP_AT_EOL_OUTPUT = 2
|
160
161
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
161
162
|
|
162
163
|
# Calling Win32API with console handle is reported to fail after executing some external command.
|
@@ -170,7 +171,7 @@ class Reline::Windows < Reline::IO
|
|
170
171
|
end
|
171
172
|
|
172
173
|
private def getconsolemode
|
173
|
-
mode = "\
|
174
|
+
mode = +"\0\0\0\0"
|
174
175
|
call_with_console_handle(@GetConsoleMode, mode)
|
175
176
|
mode.unpack1('L')
|
176
177
|
end
|
@@ -252,7 +253,7 @@ class Reline::Windows < Reline::IO
|
|
252
253
|
|
253
254
|
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
254
255
|
|
255
|
-
match = KEY_MAP.find { |args,| key.
|
256
|
+
match = KEY_MAP.find { |args,| key.match?(**args) }
|
256
257
|
unless match.nil?
|
257
258
|
@output_buf.concat(match.last)
|
258
259
|
return
|
@@ -344,35 +345,38 @@ class Reline::Windows < Reline::IO
|
|
344
345
|
# [18,2] dwMaximumWindowSize.X
|
345
346
|
# [20,2] dwMaximumWindowSize.Y
|
346
347
|
csbi = 0.chr * 22
|
347
|
-
|
348
|
-
|
348
|
+
if call_with_console_handle(@GetConsoleScreenBufferInfo, csbi) != 0
|
349
|
+
# returns [width, height, x, y, attributes, left, top, right, bottom]
|
350
|
+
csbi.unpack("s9")
|
351
|
+
else
|
352
|
+
return nil
|
353
|
+
end
|
349
354
|
end
|
350
355
|
|
356
|
+
ALTERNATIVE_CSBI = [80, 24, 0, 0, 7, 0, 0, 79, 23].freeze
|
357
|
+
|
351
358
|
def get_screen_size
|
352
|
-
|
353
|
-
|
354
|
-
end
|
355
|
-
csbi[0, 4].unpack('SS').reverse
|
359
|
+
width, _, _, _, _, _, top, _, bottom = get_console_screen_buffer_info || ALTERNATIVE_CSBI
|
360
|
+
[bottom - top + 1, width]
|
356
361
|
end
|
357
362
|
|
358
363
|
def cursor_pos
|
359
|
-
|
360
|
-
|
361
|
-
end
|
362
|
-
x = csbi[4, 2].unpack1('s')
|
363
|
-
y = csbi[6, 2].unpack1('s')
|
364
|
-
Reline::CursorPos.new(x, y)
|
364
|
+
_, _, x, y, _, _, top, = get_console_screen_buffer_info || ALTERNATIVE_CSBI
|
365
|
+
Reline::CursorPos.new(x, y - top)
|
365
366
|
end
|
366
367
|
|
367
368
|
def move_cursor_column(val)
|
368
|
-
|
369
|
+
_, _, _, y, = get_console_screen_buffer_info
|
370
|
+
call_with_console_handle(@SetConsoleCursorPosition, y * 65536 + val) if y
|
369
371
|
end
|
370
372
|
|
371
373
|
def move_cursor_up(val)
|
372
374
|
if val > 0
|
373
|
-
|
375
|
+
_, _, x, y, _, _, top, = get_console_screen_buffer_info
|
376
|
+
return unless y
|
377
|
+
y = (y - top) - val
|
374
378
|
y = 0 if y < 0
|
375
|
-
call_with_console_handle(@SetConsoleCursorPosition, y * 65536 +
|
379
|
+
call_with_console_handle(@SetConsoleCursorPosition, (y + top) * 65536 + x)
|
376
380
|
elsif val < 0
|
377
381
|
move_cursor_down(-val)
|
378
382
|
end
|
@@ -380,58 +384,39 @@ class Reline::Windows < Reline::IO
|
|
380
384
|
|
381
385
|
def move_cursor_down(val)
|
382
386
|
if val > 0
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
y =
|
387
|
-
|
387
|
+
_, _, x, y, _, _, top, _, bottom = get_console_screen_buffer_info
|
388
|
+
return unless y
|
389
|
+
screen_height = bottom - top
|
390
|
+
y = (y - top) + val
|
391
|
+
y = screen_height if y > screen_height
|
392
|
+
call_with_console_handle(@SetConsoleCursorPosition, (y + top) * 65536 + x)
|
388
393
|
elsif val < 0
|
389
394
|
move_cursor_up(-val)
|
390
395
|
end
|
391
396
|
end
|
392
397
|
|
393
398
|
def erase_after_cursor
|
394
|
-
|
395
|
-
|
396
|
-
cursor = csbi[4, 4].unpack1('L')
|
399
|
+
width, _, x, y, attributes, = get_console_screen_buffer_info
|
400
|
+
return unless x
|
397
401
|
written = 0.chr * 4
|
398
|
-
call_with_console_handle(@FillConsoleOutputCharacter, 0x20,
|
399
|
-
call_with_console_handle(@FillConsoleOutputAttribute, attributes,
|
400
|
-
end
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
if @legacy_console || window_left != 0
|
410
|
-
# unless ENABLE_VIRTUAL_TERMINAL,
|
411
|
-
# if srWindow.Left != 0 then it's conhost.exe hosted console
|
412
|
-
# and puts "\n" causes horizontal scroll. its glitch.
|
413
|
-
# FYI irb write from culumn 1, so this gives no gain.
|
414
|
-
scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4')
|
415
|
-
destination_origin = 0 # y * 65536 + x
|
416
|
-
fill = [' '.ord, attributes].pack('SS')
|
417
|
-
call_with_console_handle(@ScrollConsoleScreenBuffer, scroll_rectangle, nil, destination_origin, fill)
|
418
|
-
else
|
419
|
-
origin_x = x + 1
|
420
|
-
origin_y = y - window_top + 1
|
421
|
-
@output.write [
|
422
|
-
(origin_y != screen_height) ? "\e[#{screen_height};H" : nil,
|
423
|
-
"\n" * val,
|
424
|
-
(origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil
|
425
|
-
].join
|
426
|
-
end
|
402
|
+
call_with_console_handle(@FillConsoleOutputCharacter, 0x20, width - x, y * 65536 + x, written)
|
403
|
+
call_with_console_handle(@FillConsoleOutputAttribute, attributes, width - x, y * 65536 + x, written)
|
404
|
+
end
|
405
|
+
|
406
|
+
# This only works when the cursor is at the bottom of the scroll range
|
407
|
+
# For more details, see https://github.com/ruby/reline/pull/577#issuecomment-1646679623
|
408
|
+
def scroll_down(x)
|
409
|
+
return if x.zero?
|
410
|
+
# We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
|
411
|
+
@output.write "\n" * x
|
427
412
|
end
|
428
413
|
|
429
414
|
def clear_screen
|
430
415
|
if @legacy_console
|
431
|
-
|
432
|
-
|
433
|
-
fill_length =
|
434
|
-
screen_topleft =
|
416
|
+
width, _, _, _, attributes, _, top, _, bottom = get_console_screen_buffer_info
|
417
|
+
return unless width
|
418
|
+
fill_length = width * (bottom - top + 1)
|
419
|
+
screen_topleft = top * 65536
|
435
420
|
written = 0.chr * 4
|
436
421
|
call_with_console_handle(@FillConsoleOutputCharacter, 0x20, fill_length, screen_topleft, written)
|
437
422
|
call_with_console_handle(@FillConsoleOutputAttribute, attributes, fill_length, screen_topleft, written)
|
@@ -472,6 +457,28 @@ class Reline::Windows < Reline::IO
|
|
472
457
|
# do nothing
|
473
458
|
end
|
474
459
|
|
460
|
+
def disable_auto_linewrap(setting = true, &block)
|
461
|
+
mode = getconsolemode
|
462
|
+
if 0 == (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
463
|
+
if block
|
464
|
+
begin
|
465
|
+
setconsolemode(mode & ~ENABLE_WRAP_AT_EOL_OUTPUT)
|
466
|
+
block.call
|
467
|
+
ensure
|
468
|
+
setconsolemode(mode | ENABLE_WRAP_AT_EOL_OUTPUT)
|
469
|
+
end
|
470
|
+
else
|
471
|
+
if setting
|
472
|
+
setconsolemode(mode & ~ENABLE_WRAP_AT_EOL_OUTPUT)
|
473
|
+
else
|
474
|
+
setconsolemode(mode | ENABLE_WRAP_AT_EOL_OUTPUT)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
else
|
478
|
+
block.call if block
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
475
482
|
class KeyEventRecord
|
476
483
|
|
477
484
|
attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys
|
@@ -501,7 +508,7 @@ class Reline::Windows < Reline::IO
|
|
501
508
|
# Verifies if the arguments match with this key event.
|
502
509
|
# Nil arguments are ignored, but at least one must be passed as non-nil.
|
503
510
|
# To verify that no control keys were pressed, pass an empty array: `control_keys: []`.
|
504
|
-
def
|
511
|
+
def match?(control_keys: nil, virtual_key_code: nil, char_code: nil)
|
505
512
|
raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil?
|
506
513
|
|
507
514
|
(control_keys.nil? || [*control_keys].sort == @control_keys) &&
|
data/lib/reline/key_stroke.rb
CHANGED
@@ -3,8 +3,11 @@ class Reline::KeyStroke
|
|
3
3
|
CSI_PARAMETER_BYTES_RANGE = 0x30..0x3f
|
4
4
|
CSI_INTERMEDIATE_BYTES_RANGE = (0x20..0x2f)
|
5
5
|
|
6
|
-
|
6
|
+
attr_accessor :encoding
|
7
|
+
|
8
|
+
def initialize(config, encoding)
|
7
9
|
@config = config
|
10
|
+
@encoding = encoding
|
8
11
|
end
|
9
12
|
|
10
13
|
# Input exactly matches to a key sequence
|
@@ -21,7 +24,7 @@ class Reline::KeyStroke
|
|
21
24
|
matched = key_mapping.get(input)
|
22
25
|
|
23
26
|
# FIXME: Workaround for single byte. remove this after MAPPING is merged into KeyActor.
|
24
|
-
matched ||= input.size == 1
|
27
|
+
matched ||= input.size == 1 && input[0] < 0x80
|
25
28
|
matching ||= input == [ESC_BYTE]
|
26
29
|
|
27
30
|
if matching && matched
|
@@ -32,10 +35,14 @@ class Reline::KeyStroke
|
|
32
35
|
MATCHED
|
33
36
|
elsif input[0] == ESC_BYTE
|
34
37
|
match_unknown_escape_sequence(input, vi_mode: @config.editing_mode_is?(:vi_insert, :vi_command))
|
35
|
-
elsif input.size == 1
|
36
|
-
MATCHED
|
37
38
|
else
|
38
|
-
|
39
|
+
s = input.pack('c*').force_encoding(@encoding)
|
40
|
+
if s.valid_encoding?
|
41
|
+
s.size == 1 ? MATCHED : UNMATCHED
|
42
|
+
else
|
43
|
+
# Invalid string is MATCHING (part of valid string) or MATCHED (invalid bytes to be ignored)
|
44
|
+
MATCHING_MATCHED
|
45
|
+
end
|
39
46
|
end
|
40
47
|
end
|
41
48
|
|
@@ -45,6 +52,7 @@ class Reline::KeyStroke
|
|
45
52
|
bytes = input.take(i)
|
46
53
|
status = match_status(bytes)
|
47
54
|
matched_bytes = bytes if status == MATCHED || status == MATCHING_MATCHED
|
55
|
+
break if status == MATCHED || status == UNMATCHED
|
48
56
|
end
|
49
57
|
return [[], []] unless matched_bytes
|
50
58
|
|
@@ -53,12 +61,15 @@ class Reline::KeyStroke
|
|
53
61
|
keys = func.map { |c| Reline::Key.new(c, c, false) }
|
54
62
|
elsif func
|
55
63
|
keys = [Reline::Key.new(func, func, false)]
|
56
|
-
elsif matched_bytes.size == 1
|
57
|
-
keys = [Reline::Key.new(matched_bytes.first, matched_bytes.first, false)]
|
58
64
|
elsif matched_bytes.size == 2 && matched_bytes[0] == ESC_BYTE
|
59
65
|
keys = [Reline::Key.new(matched_bytes[1], matched_bytes[1] | 0b10000000, true)]
|
60
66
|
else
|
61
|
-
|
67
|
+
s = matched_bytes.pack('c*').force_encoding(@encoding)
|
68
|
+
if s.valid_encoding? && s.size == 1
|
69
|
+
keys = [Reline::Key.new(s.ord, s.ord, false)]
|
70
|
+
else
|
71
|
+
keys = []
|
72
|
+
end
|
62
73
|
end
|
63
74
|
|
64
75
|
[keys, input.drop(matched_bytes.size)]
|