reline 0.3.9 → 0.6.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 +8 -1
- data/lib/reline/config.rb +95 -123
- data/lib/reline/face.rb +199 -0
- data/lib/reline/history.rb +4 -4
- data/lib/reline/io/ansi.rb +318 -0
- data/lib/reline/io/dumb.rb +120 -0
- data/lib/reline/{windows.rb → io/windows.rb} +182 -153
- data/lib/reline/io.rb +55 -0
- data/lib/reline/key_actor/base.rb +27 -9
- data/lib/reline/key_actor/composite.rb +17 -0
- data/lib/reline/key_actor/emacs.rb +103 -103
- data/lib/reline/key_actor/vi_command.rb +188 -188
- data/lib/reline/key_actor/vi_insert.rb +144 -144
- data/lib/reline/key_actor.rb +1 -0
- data/lib/reline/key_stroke.rb +75 -104
- data/lib/reline/kill_ring.rb +2 -2
- data/lib/reline/line_editor.rb +1175 -2121
- data/lib/reline/unicode/east_asian_width.rb +1289 -1192
- data/lib/reline/unicode.rb +218 -445
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +143 -224
- metadata +13 -11
- data/lib/reline/ansi.rb +0 -363
- data/lib/reline/general_io.rb +0 -116
- data/lib/reline/terminfo.rb +0 -160
@@ -1,19 +1,52 @@
|
|
1
1
|
require 'fiddle/import'
|
2
2
|
|
3
|
-
class Reline::Windows
|
4
|
-
|
3
|
+
class Reline::Windows < Reline::IO
|
4
|
+
|
5
|
+
attr_writer :output
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@input_buf = []
|
9
|
+
@output_buf = []
|
10
|
+
|
11
|
+
@output = STDOUT
|
12
|
+
@hsg = nil
|
13
|
+
@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
14
|
+
@kbhit = Win32API.new('msvcrt', '_kbhit', [], 'I')
|
15
|
+
@GetKeyState = Win32API.new('user32', 'GetKeyState', ['L'], 'L')
|
16
|
+
@GetConsoleScreenBufferInfo = Win32API.new('kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L')
|
17
|
+
@SetConsoleCursorPosition = Win32API.new('kernel32', 'SetConsoleCursorPosition', ['L', 'L'], 'L')
|
18
|
+
@GetStdHandle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L')
|
19
|
+
@FillConsoleOutputCharacter = Win32API.new('kernel32', 'FillConsoleOutputCharacter', ['L', 'L', 'L', 'L', 'P'], 'L')
|
20
|
+
@ScrollConsoleScreenBuffer = Win32API.new('kernel32', 'ScrollConsoleScreenBuffer', ['L', 'P', 'P', 'L', 'P'], 'L')
|
21
|
+
@hConsoleHandle = @GetStdHandle.call(STD_OUTPUT_HANDLE)
|
22
|
+
@hConsoleInputHandle = @GetStdHandle.call(STD_INPUT_HANDLE)
|
23
|
+
@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
24
|
+
@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
|
25
|
+
@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
26
|
+
@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
27
|
+
@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
28
|
+
@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L')
|
29
|
+
|
30
|
+
@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
31
|
+
@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
32
|
+
@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
|
33
|
+
|
34
|
+
@legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def encoding
|
5
38
|
Encoding::UTF_8
|
6
39
|
end
|
7
40
|
|
8
|
-
def
|
41
|
+
def win?
|
9
42
|
true
|
10
43
|
end
|
11
44
|
|
12
|
-
def
|
13
|
-
|
45
|
+
def win_legacy_console?
|
46
|
+
@legacy_console
|
14
47
|
end
|
15
48
|
|
16
|
-
def
|
49
|
+
def set_default_key_bindings(config)
|
17
50
|
{
|
18
51
|
[224, 72] => :ed_prev_history, # ↑
|
19
52
|
[224, 80] => :ed_next_history, # ↓
|
@@ -22,7 +55,6 @@ class Reline::Windows
|
|
22
55
|
[224, 83] => :key_delete, # Del
|
23
56
|
[224, 71] => :ed_move_to_beg, # Home
|
24
57
|
[224, 79] => :ed_move_to_end, # End
|
25
|
-
[ 0, 41] => :ed_unassigned, # input method on/off
|
26
58
|
[ 0, 72] => :ed_prev_history, # ↑
|
27
59
|
[ 0, 80] => :ed_next_history, # ↓
|
28
60
|
[ 0, 77] => :ed_next_char, # →
|
@@ -85,7 +117,7 @@ class Reline::Windows
|
|
85
117
|
def call(*args)
|
86
118
|
import = @proto.split("")
|
87
119
|
args.each_with_index do |x, i|
|
88
|
-
args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
|
120
|
+
args[i], = [x == 0 ? nil : +x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
|
89
121
|
args[i], = [x].pack("I").unpack("i") if import[i] == "I"
|
90
122
|
end
|
91
123
|
ret, = @func.call(*args)
|
@@ -127,58 +159,43 @@ class Reline::Windows
|
|
127
159
|
STD_OUTPUT_HANDLE = -11
|
128
160
|
FILE_TYPE_PIPE = 0x0003
|
129
161
|
FILE_NAME_INFO = 2
|
130
|
-
|
131
|
-
@@kbhit = Win32API.new('msvcrt', '_kbhit', [], 'I')
|
132
|
-
@@GetKeyState = Win32API.new('user32', 'GetKeyState', ['L'], 'L')
|
133
|
-
@@GetConsoleScreenBufferInfo = Win32API.new('kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L')
|
134
|
-
@@SetConsoleCursorPosition = Win32API.new('kernel32', 'SetConsoleCursorPosition', ['L', 'L'], 'L')
|
135
|
-
@@GetStdHandle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L')
|
136
|
-
@@FillConsoleOutputCharacter = Win32API.new('kernel32', 'FillConsoleOutputCharacter', ['L', 'L', 'L', 'L', 'P'], 'L')
|
137
|
-
@@ScrollConsoleScreenBuffer = Win32API.new('kernel32', 'ScrollConsoleScreenBuffer', ['L', 'P', 'P', 'L', 'P'], 'L')
|
138
|
-
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
139
|
-
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
140
|
-
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
141
|
-
@@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
|
142
|
-
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
143
|
-
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
144
|
-
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
145
|
-
@@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L')
|
146
|
-
|
147
|
-
@@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
148
|
-
@@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
149
|
-
@@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
|
162
|
+
ENABLE_WRAP_AT_EOL_OUTPUT = 2
|
150
163
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
151
164
|
|
152
|
-
|
153
|
-
|
154
|
-
|
165
|
+
# Calling Win32API with console handle is reported to fail after executing some external command.
|
166
|
+
# We need to refresh console handle and retry the call again.
|
167
|
+
private def call_with_console_handle(win32func, *args)
|
168
|
+
val = win32func.call(@hConsoleHandle, *args)
|
169
|
+
return val if val != 0
|
170
|
+
|
171
|
+
@hConsoleHandle = @GetStdHandle.call(STD_OUTPUT_HANDLE)
|
172
|
+
win32func.call(@hConsoleHandle, *args)
|
173
|
+
end
|
174
|
+
|
175
|
+
private def getconsolemode
|
176
|
+
mode = +"\0\0\0\0"
|
177
|
+
call_with_console_handle(@GetConsoleMode, mode)
|
155
178
|
mode.unpack1('L')
|
156
179
|
end
|
157
180
|
|
158
|
-
|
159
|
-
|
181
|
+
private def setconsolemode(mode)
|
182
|
+
call_with_console_handle(@SetConsoleMode, mode)
|
160
183
|
end
|
161
184
|
|
162
|
-
|
163
|
-
#if @@legacy_console
|
185
|
+
#if @legacy_console
|
164
186
|
# setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
165
|
-
#
|
187
|
+
# @legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
166
188
|
#end
|
167
189
|
|
168
|
-
|
169
|
-
@@output_buf = []
|
170
|
-
|
171
|
-
@@output = STDOUT
|
172
|
-
|
173
|
-
def self.msys_tty?(io = @@hConsoleInputHandle)
|
190
|
+
def msys_tty?(io = @hConsoleInputHandle)
|
174
191
|
# check if fd is a pipe
|
175
|
-
if
|
192
|
+
if @GetFileType.call(io) != FILE_TYPE_PIPE
|
176
193
|
return false
|
177
194
|
end
|
178
195
|
|
179
196
|
bufsize = 1024
|
180
197
|
p_buffer = "\0" * bufsize
|
181
|
-
res =
|
198
|
+
res = @GetFileInformationByHandleEx.call(io, FILE_NAME_INFO, p_buffer, bufsize - 2)
|
182
199
|
return false if res == 0
|
183
200
|
|
184
201
|
# get pipe name: p_buffer layout is:
|
@@ -215,65 +232,63 @@ class Reline::Windows
|
|
215
232
|
[ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ],
|
216
233
|
]
|
217
234
|
|
218
|
-
|
219
|
-
|
220
|
-
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
235
|
+
def process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
221
236
|
|
222
237
|
# high-surrogate
|
223
238
|
if 0xD800 <= char_code and char_code <= 0xDBFF
|
224
|
-
|
239
|
+
@hsg = char_code
|
225
240
|
return
|
226
241
|
end
|
227
242
|
# low-surrogate
|
228
243
|
if 0xDC00 <= char_code and char_code <= 0xDFFF
|
229
|
-
if
|
230
|
-
char_code = 0x10000 + (
|
231
|
-
|
244
|
+
if @hsg
|
245
|
+
char_code = 0x10000 + (@hsg - 0xD800) * 0x400 + char_code - 0xDC00
|
246
|
+
@hsg = nil
|
232
247
|
else
|
233
248
|
# no high-surrogate. ignored.
|
234
249
|
return
|
235
250
|
end
|
236
251
|
else
|
237
252
|
# ignore high-surrogate without low-surrogate if there
|
238
|
-
|
253
|
+
@hsg = nil
|
239
254
|
end
|
240
255
|
|
241
256
|
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
242
257
|
|
243
|
-
match = KEY_MAP.find { |args,| key.
|
258
|
+
match = KEY_MAP.find { |args,| key.match?(**args) }
|
244
259
|
unless match.nil?
|
245
|
-
|
260
|
+
@output_buf.concat(match.last)
|
246
261
|
return
|
247
262
|
end
|
248
263
|
|
249
264
|
# no char, only control keys
|
250
265
|
return if key.char_code == 0 and key.control_keys.any?
|
251
266
|
|
252
|
-
|
267
|
+
@output_buf.push("\e".ord) if key.control_keys.include?(:ALT) and !key.control_keys.include?(:CTRL)
|
253
268
|
|
254
|
-
|
269
|
+
@output_buf.concat(key.char.bytes)
|
255
270
|
end
|
256
271
|
|
257
|
-
def
|
272
|
+
def check_input_event
|
258
273
|
num_of_events = 0.chr * 8
|
259
|
-
while
|
260
|
-
Reline.core.line_editor.
|
261
|
-
if
|
274
|
+
while @output_buf.empty?
|
275
|
+
Reline.core.line_editor.handle_signal
|
276
|
+
if @WaitForSingleObject.(@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
262
277
|
# prevent for background consolemode change
|
263
|
-
|
278
|
+
@legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0
|
264
279
|
next
|
265
280
|
end
|
266
|
-
next if
|
281
|
+
next if @GetNumberOfConsoleInputEvents.(@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack1('L') == 0
|
267
282
|
input_records = 0.chr * 20 * 80
|
268
283
|
read_event = 0.chr * 4
|
269
|
-
if
|
284
|
+
if @ReadConsoleInputW.(@hConsoleInputHandle, input_records, 80, read_event) != 0
|
270
285
|
read_events = read_event.unpack1('L')
|
271
286
|
0.upto(read_events) do |idx|
|
272
287
|
input_record = input_records[idx * 20, 20]
|
273
288
|
event = input_record[0, 2].unpack1('s*')
|
274
289
|
case event
|
275
290
|
when WINDOW_BUFFER_SIZE_EVENT
|
276
|
-
|
291
|
+
@winch_handler.()
|
277
292
|
when KEY_EVENT
|
278
293
|
key_down = input_record[4, 4].unpack1('l*')
|
279
294
|
repeat_count = input_record[8, 2].unpack1('s*')
|
@@ -291,34 +306,42 @@ class Reline::Windows
|
|
291
306
|
end
|
292
307
|
end
|
293
308
|
|
294
|
-
def
|
309
|
+
def with_raw_input
|
295
310
|
yield
|
296
311
|
end
|
297
312
|
|
298
|
-
def
|
313
|
+
def write(string)
|
314
|
+
@output.write(string)
|
315
|
+
end
|
316
|
+
|
317
|
+
def buffered_output
|
318
|
+
yield
|
319
|
+
end
|
320
|
+
|
321
|
+
def getc(_timeout_second)
|
299
322
|
check_input_event
|
300
|
-
|
323
|
+
@output_buf.shift
|
301
324
|
end
|
302
325
|
|
303
|
-
def
|
304
|
-
|
326
|
+
def ungetc(c)
|
327
|
+
@output_buf.unshift(c)
|
305
328
|
end
|
306
329
|
|
307
|
-
def
|
308
|
-
not
|
330
|
+
def in_pasting?
|
331
|
+
not empty_buffer?
|
309
332
|
end
|
310
333
|
|
311
|
-
def
|
312
|
-
if not
|
334
|
+
def empty_buffer?
|
335
|
+
if not @output_buf.empty?
|
313
336
|
false
|
314
|
-
elsif
|
337
|
+
elsif @kbhit.call == 0
|
315
338
|
true
|
316
339
|
else
|
317
340
|
false
|
318
341
|
end
|
319
342
|
end
|
320
343
|
|
321
|
-
def
|
344
|
+
def get_console_screen_buffer_info
|
322
345
|
# CONSOLE_SCREEN_BUFFER_INFO
|
323
346
|
# [ 0,2] dwSize.X
|
324
347
|
# [ 2,2] dwSize.Y
|
@@ -332,134 +355,140 @@ class Reline::Windows
|
|
332
355
|
# [18,2] dwMaximumWindowSize.X
|
333
356
|
# [20,2] dwMaximumWindowSize.Y
|
334
357
|
csbi = 0.chr * 22
|
335
|
-
|
336
|
-
|
358
|
+
if call_with_console_handle(@GetConsoleScreenBufferInfo, csbi) != 0
|
359
|
+
# returns [width, height, x, y, attributes, left, top, right, bottom]
|
360
|
+
csbi.unpack("s9")
|
361
|
+
else
|
362
|
+
return nil
|
363
|
+
end
|
337
364
|
end
|
338
365
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
366
|
+
ALTERNATIVE_CSBI = [80, 24, 0, 0, 7, 0, 0, 79, 23].freeze
|
367
|
+
|
368
|
+
def get_screen_size
|
369
|
+
width, _, _, _, _, _, top, _, bottom = get_console_screen_buffer_info || ALTERNATIVE_CSBI
|
370
|
+
[bottom - top + 1, width]
|
344
371
|
end
|
345
372
|
|
346
|
-
def
|
347
|
-
|
348
|
-
|
349
|
-
end
|
350
|
-
x = csbi[4, 2].unpack1('s')
|
351
|
-
y = csbi[6, 2].unpack1('s')
|
352
|
-
Reline::CursorPos.new(x, y)
|
373
|
+
def cursor_pos
|
374
|
+
_, _, x, y, _, _, top, = get_console_screen_buffer_info || ALTERNATIVE_CSBI
|
375
|
+
Reline::CursorPos.new(x, y - top)
|
353
376
|
end
|
354
377
|
|
355
|
-
def
|
356
|
-
|
378
|
+
def move_cursor_column(val)
|
379
|
+
_, _, _, y, = get_console_screen_buffer_info
|
380
|
+
call_with_console_handle(@SetConsoleCursorPosition, y * 65536 + val) if y
|
357
381
|
end
|
358
382
|
|
359
|
-
def
|
383
|
+
def move_cursor_up(val)
|
360
384
|
if val > 0
|
361
|
-
|
385
|
+
_, _, x, y, _, _, top, = get_console_screen_buffer_info
|
386
|
+
return unless y
|
387
|
+
y = (y - top) - val
|
362
388
|
y = 0 if y < 0
|
363
|
-
|
389
|
+
call_with_console_handle(@SetConsoleCursorPosition, (y + top) * 65536 + x)
|
364
390
|
elsif val < 0
|
365
391
|
move_cursor_down(-val)
|
366
392
|
end
|
367
393
|
end
|
368
394
|
|
369
|
-
def
|
395
|
+
def move_cursor_down(val)
|
370
396
|
if val > 0
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
y =
|
375
|
-
|
397
|
+
_, _, x, y, _, _, top, _, bottom = get_console_screen_buffer_info
|
398
|
+
return unless y
|
399
|
+
screen_height = bottom - top
|
400
|
+
y = (y - top) + val
|
401
|
+
y = screen_height if y > screen_height
|
402
|
+
call_with_console_handle(@SetConsoleCursorPosition, (y + top) * 65536 + x)
|
376
403
|
elsif val < 0
|
377
404
|
move_cursor_up(-val)
|
378
405
|
end
|
379
406
|
end
|
380
407
|
|
381
|
-
def
|
382
|
-
|
383
|
-
|
384
|
-
cursor = csbi[4, 4].unpack1('L')
|
408
|
+
def erase_after_cursor
|
409
|
+
width, _, x, y, attributes, = get_console_screen_buffer_info
|
410
|
+
return unless x
|
385
411
|
written = 0.chr * 4
|
386
|
-
|
387
|
-
|
388
|
-
end
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
if @@legacy_console || window_left != 0
|
398
|
-
# unless ENABLE_VIRTUAL_TERMINAL,
|
399
|
-
# if srWindow.Left != 0 then it's conhost.exe hosted console
|
400
|
-
# and puts "\n" causes horizontal scroll. its glitch.
|
401
|
-
# FYI irb write from culumn 1, so this gives no gain.
|
402
|
-
scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4')
|
403
|
-
destination_origin = 0 # y * 65536 + x
|
404
|
-
fill = [' '.ord, attributes].pack('SS')
|
405
|
-
@@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
|
406
|
-
else
|
407
|
-
origin_x = x + 1
|
408
|
-
origin_y = y - window_top + 1
|
409
|
-
@@output.write [
|
410
|
-
(origin_y != screen_height) ? "\e[#{screen_height};H" : nil,
|
411
|
-
"\n" * val,
|
412
|
-
(origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil
|
413
|
-
].join
|
414
|
-
end
|
412
|
+
call_with_console_handle(@FillConsoleOutputCharacter, 0x20, width - x, y * 65536 + x, written)
|
413
|
+
call_with_console_handle(@FillConsoleOutputAttribute, attributes, width - x, y * 65536 + x, written)
|
414
|
+
end
|
415
|
+
|
416
|
+
# This only works when the cursor is at the bottom of the scroll range
|
417
|
+
# For more details, see https://github.com/ruby/reline/pull/577#issuecomment-1646679623
|
418
|
+
def scroll_down(x)
|
419
|
+
return if x.zero?
|
420
|
+
# We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
|
421
|
+
@output.write "\n" * x
|
415
422
|
end
|
416
423
|
|
417
|
-
def
|
418
|
-
if
|
419
|
-
|
420
|
-
|
421
|
-
fill_length =
|
422
|
-
screen_topleft =
|
424
|
+
def clear_screen
|
425
|
+
if @legacy_console
|
426
|
+
width, _, _, _, attributes, _, top, _, bottom = get_console_screen_buffer_info
|
427
|
+
return unless width
|
428
|
+
fill_length = width * (bottom - top + 1)
|
429
|
+
screen_topleft = top * 65536
|
423
430
|
written = 0.chr * 4
|
424
|
-
|
425
|
-
|
426
|
-
|
431
|
+
call_with_console_handle(@FillConsoleOutputCharacter, 0x20, fill_length, screen_topleft, written)
|
432
|
+
call_with_console_handle(@FillConsoleOutputAttribute, attributes, fill_length, screen_topleft, written)
|
433
|
+
call_with_console_handle(@SetConsoleCursorPosition, screen_topleft)
|
427
434
|
else
|
428
|
-
|
435
|
+
@output.write "\e[2J" "\e[H"
|
429
436
|
end
|
430
437
|
end
|
431
438
|
|
432
|
-
def
|
439
|
+
def set_screen_size(rows, columns)
|
433
440
|
raise NotImplementedError
|
434
441
|
end
|
435
442
|
|
436
|
-
def
|
443
|
+
def hide_cursor
|
437
444
|
size = 100
|
438
445
|
visible = 0 # 0 means false
|
439
446
|
cursor_info = [size, visible].pack('Li')
|
440
|
-
|
447
|
+
call_with_console_handle(@SetConsoleCursorInfo, cursor_info)
|
441
448
|
end
|
442
449
|
|
443
|
-
def
|
450
|
+
def show_cursor
|
444
451
|
size = 100
|
445
452
|
visible = 1 # 1 means true
|
446
453
|
cursor_info = [size, visible].pack('Li')
|
447
|
-
|
454
|
+
call_with_console_handle(@SetConsoleCursorInfo, cursor_info)
|
448
455
|
end
|
449
456
|
|
450
|
-
def
|
451
|
-
|
457
|
+
def set_winch_handler(&handler)
|
458
|
+
@winch_handler = handler
|
452
459
|
end
|
453
460
|
|
454
|
-
def
|
461
|
+
def prep
|
455
462
|
# do nothing
|
456
463
|
nil
|
457
464
|
end
|
458
465
|
|
459
|
-
def
|
466
|
+
def deprep(otio)
|
460
467
|
# do nothing
|
461
468
|
end
|
462
469
|
|
470
|
+
def disable_auto_linewrap(setting = true, &block)
|
471
|
+
mode = getconsolemode
|
472
|
+
if 0 == (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
473
|
+
if block
|
474
|
+
begin
|
475
|
+
setconsolemode(mode & ~ENABLE_WRAP_AT_EOL_OUTPUT)
|
476
|
+
block.call
|
477
|
+
ensure
|
478
|
+
setconsolemode(mode | ENABLE_WRAP_AT_EOL_OUTPUT)
|
479
|
+
end
|
480
|
+
else
|
481
|
+
if setting
|
482
|
+
setconsolemode(mode & ~ENABLE_WRAP_AT_EOL_OUTPUT)
|
483
|
+
else
|
484
|
+
setconsolemode(mode | ENABLE_WRAP_AT_EOL_OUTPUT)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
else
|
488
|
+
block.call if block
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
463
492
|
class KeyEventRecord
|
464
493
|
|
465
494
|
attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys
|
@@ -489,7 +518,7 @@ class Reline::Windows
|
|
489
518
|
# Verifies if the arguments match with this key event.
|
490
519
|
# Nil arguments are ignored, but at least one must be passed as non-nil.
|
491
520
|
# To verify that no control keys were pressed, pass an empty array: `control_keys: []`.
|
492
|
-
def
|
521
|
+
def match?(control_keys: nil, virtual_key_code: nil, char_code: nil)
|
493
522
|
raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil?
|
494
523
|
|
495
524
|
(control_keys.nil? || [*control_keys].sort == @control_keys) &&
|
data/lib/reline/io.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
module Reline
|
3
|
+
class IO
|
4
|
+
RESET_COLOR = "\e[0m"
|
5
|
+
|
6
|
+
def self.decide_io_gate
|
7
|
+
if ENV['TERM'] == 'dumb'
|
8
|
+
Reline::Dumb.new
|
9
|
+
else
|
10
|
+
require 'reline/io/ansi'
|
11
|
+
|
12
|
+
case RbConfig::CONFIG['host_os']
|
13
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
14
|
+
require 'reline/io/windows'
|
15
|
+
io = Reline::Windows.new
|
16
|
+
if io.msys_tty?
|
17
|
+
Reline::ANSI.new
|
18
|
+
else
|
19
|
+
io
|
20
|
+
end
|
21
|
+
else
|
22
|
+
Reline::ANSI.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def dumb?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def win?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset_color_sequence
|
36
|
+
self.class::RESET_COLOR
|
37
|
+
end
|
38
|
+
|
39
|
+
# Read a single encoding valid character from the input.
|
40
|
+
def read_single_char(keyseq_timeout)
|
41
|
+
buffer = String.new(encoding: Encoding::ASCII_8BIT)
|
42
|
+
loop do
|
43
|
+
timeout = buffer.empty? ? Float::INFINITY : keyseq_timeout
|
44
|
+
c = getc(timeout)
|
45
|
+
return unless c
|
46
|
+
|
47
|
+
buffer << c
|
48
|
+
encoded = buffer.dup.force_encoding(encoding)
|
49
|
+
return encoded if encoded.valid_encoding?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
require 'reline/io/dumb'
|
@@ -1,19 +1,37 @@
|
|
1
1
|
class Reline::KeyActor::Base
|
2
|
-
|
2
|
+
def initialize(mappings = nil)
|
3
|
+
@matching_bytes = {}
|
4
|
+
@key_bindings = {}
|
5
|
+
add_mappings(mappings) if mappings
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_mappings(mappings)
|
9
|
+
add([27], :ed_ignore)
|
10
|
+
128.times do |key|
|
11
|
+
func = mappings[key]
|
12
|
+
meta_func = mappings[key | 0b10000000]
|
13
|
+
add([key], func) if func
|
14
|
+
add([27, key], meta_func) if meta_func
|
15
|
+
end
|
16
|
+
end
|
3
17
|
|
4
|
-
def
|
5
|
-
|
18
|
+
def add(key, func)
|
19
|
+
(1...key.size).each do |size|
|
20
|
+
@matching_bytes[key.take(size)] = true
|
21
|
+
end
|
22
|
+
@key_bindings[key] = func
|
6
23
|
end
|
7
24
|
|
8
|
-
def
|
9
|
-
@
|
25
|
+
def matching?(key)
|
26
|
+
@matching_bytes[key]
|
10
27
|
end
|
11
28
|
|
12
|
-
def
|
13
|
-
@
|
29
|
+
def get(key)
|
30
|
+
@key_bindings[key]
|
14
31
|
end
|
15
32
|
|
16
|
-
def
|
17
|
-
@
|
33
|
+
def clear
|
34
|
+
@matching_bytes.clear
|
35
|
+
@key_bindings.clear
|
18
36
|
end
|
19
37
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Reline::KeyActor::Composite
|
2
|
+
def initialize(key_actors)
|
3
|
+
@key_actors = key_actors
|
4
|
+
end
|
5
|
+
|
6
|
+
def matching?(key)
|
7
|
+
@key_actors.any? { |key_actor| key_actor.matching?(key) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(key)
|
11
|
+
@key_actors.each do |key_actor|
|
12
|
+
func = key_actor.get(key)
|
13
|
+
return func if func
|
14
|
+
end
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|