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
data/lib/reline/windows.rb
CHANGED
@@ -13,23 +13,44 @@ class Reline::Windows
|
|
13
13
|
@@legacy_console
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
16
|
+
def self.set_default_key_bindings(config)
|
17
|
+
{
|
18
|
+
[224, 72] => :ed_prev_history, # ↑
|
19
|
+
[224, 80] => :ed_next_history, # ↓
|
20
|
+
[224, 77] => :ed_next_char, # →
|
21
|
+
[224, 75] => :ed_prev_char, # ←
|
22
|
+
[224, 83] => :key_delete, # Del
|
23
|
+
[224, 71] => :ed_move_to_beg, # Home
|
24
|
+
[224, 79] => :ed_move_to_end, # End
|
25
|
+
[ 0, 41] => :ed_unassigned, # input method on/off
|
26
|
+
[ 0, 72] => :ed_prev_history, # ↑
|
27
|
+
[ 0, 80] => :ed_next_history, # ↓
|
28
|
+
[ 0, 77] => :ed_next_char, # →
|
29
|
+
[ 0, 75] => :ed_prev_char, # ←
|
30
|
+
[ 0, 83] => :key_delete, # Del
|
31
|
+
[ 0, 71] => :ed_move_to_beg, # Home
|
32
|
+
[ 0, 79] => :ed_move_to_end # End
|
33
|
+
}.each_pair do |key, func|
|
34
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
35
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
36
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
37
|
+
end
|
38
|
+
|
39
|
+
{
|
40
|
+
[27, 32] => :em_set_mark, # M-<space>
|
41
|
+
[24, 24] => :em_exchange_mark, # C-x C-x
|
42
|
+
}.each_pair do |key, func|
|
43
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Emulate ANSI key sequence.
|
47
|
+
{
|
48
|
+
[27, 91, 90] => :completion_journey_up, # S-Tab
|
49
|
+
}.each_pair do |key, func|
|
50
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
51
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
52
|
+
end
|
53
|
+
end
|
33
54
|
|
34
55
|
if defined? JRUBY_VERSION
|
35
56
|
require 'win32api'
|
@@ -73,13 +94,37 @@ class Reline::Windows
|
|
73
94
|
end
|
74
95
|
end
|
75
96
|
|
97
|
+
VK_RETURN = 0x0D
|
76
98
|
VK_MENU = 0x12
|
77
99
|
VK_LMENU = 0xA4
|
78
100
|
VK_CONTROL = 0x11
|
79
101
|
VK_SHIFT = 0x10
|
102
|
+
VK_DIVIDE = 0x6F
|
103
|
+
|
104
|
+
KEY_EVENT = 0x01
|
105
|
+
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
106
|
+
|
107
|
+
CAPSLOCK_ON = 0x0080
|
108
|
+
ENHANCED_KEY = 0x0100
|
109
|
+
LEFT_ALT_PRESSED = 0x0002
|
110
|
+
LEFT_CTRL_PRESSED = 0x0008
|
111
|
+
NUMLOCK_ON = 0x0020
|
112
|
+
RIGHT_ALT_PRESSED = 0x0001
|
113
|
+
RIGHT_CTRL_PRESSED = 0x0004
|
114
|
+
SCROLLLOCK_ON = 0x0040
|
115
|
+
SHIFT_PRESSED = 0x0010
|
116
|
+
|
117
|
+
VK_TAB = 0x09
|
118
|
+
VK_END = 0x23
|
119
|
+
VK_HOME = 0x24
|
120
|
+
VK_LEFT = 0x25
|
121
|
+
VK_UP = 0x26
|
122
|
+
VK_RIGHT = 0x27
|
123
|
+
VK_DOWN = 0x28
|
124
|
+
VK_DELETE = 0x2E
|
125
|
+
|
80
126
|
STD_INPUT_HANDLE = -10
|
81
127
|
STD_OUTPUT_HANDLE = -11
|
82
|
-
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
83
128
|
FILE_TYPE_PIPE = 0x0003
|
84
129
|
FILE_NAME_INFO = 2
|
85
130
|
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
@@ -93,13 +138,15 @@ class Reline::Windows
|
|
93
138
|
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
94
139
|
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
95
140
|
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
96
|
-
@@
|
141
|
+
@@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
|
97
142
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
98
143
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
99
144
|
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
145
|
+
@@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L')
|
100
146
|
|
101
147
|
@@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
102
148
|
@@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
149
|
+
@@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
|
103
150
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
104
151
|
|
105
152
|
private_class_method def self.getconsolemode
|
@@ -121,7 +168,9 @@ class Reline::Windows
|
|
121
168
|
@@input_buf = []
|
122
169
|
@@output_buf = []
|
123
170
|
|
124
|
-
|
171
|
+
@@output = STDOUT
|
172
|
+
|
173
|
+
def self.msys_tty?(io = @@hConsoleInputHandle)
|
125
174
|
# check if fd is a pipe
|
126
175
|
if @@GetFileType.call(io) != FILE_TYPE_PIPE
|
127
176
|
return false
|
@@ -137,7 +186,7 @@ class Reline::Windows
|
|
137
186
|
# DWORD FileNameLength;
|
138
187
|
# WCHAR FileName[1];
|
139
188
|
# } FILE_NAME_INFO
|
140
|
-
len = p_buffer[0, 4].
|
189
|
+
len = p_buffer[0, 4].unpack1("L")
|
141
190
|
name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
|
142
191
|
|
143
192
|
# Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX')
|
@@ -145,78 +194,106 @@ class Reline::Windows
|
|
145
194
|
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
146
195
|
end
|
147
196
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
197
|
+
KEY_MAP = [
|
198
|
+
# It's treated as Meta+Enter on Windows.
|
199
|
+
[ { control_keys: :CTRL, virtual_key_code: 0x0D }, "\e\r".bytes ],
|
200
|
+
[ { control_keys: :SHIFT, virtual_key_code: 0x0D }, "\e\r".bytes ],
|
201
|
+
|
202
|
+
# It's treated as Meta+Space on Windows.
|
203
|
+
[ { control_keys: :CTRL, char_code: 0x20 }, "\e ".bytes ],
|
204
|
+
|
205
|
+
# Emulate getwch() key sequences.
|
206
|
+
[ { control_keys: [], virtual_key_code: VK_UP }, [0, 72] ],
|
207
|
+
[ { control_keys: [], virtual_key_code: VK_DOWN }, [0, 80] ],
|
208
|
+
[ { control_keys: [], virtual_key_code: VK_RIGHT }, [0, 77] ],
|
209
|
+
[ { control_keys: [], virtual_key_code: VK_LEFT }, [0, 75] ],
|
210
|
+
[ { control_keys: [], virtual_key_code: VK_DELETE }, [0, 83] ],
|
211
|
+
[ { control_keys: [], virtual_key_code: VK_HOME }, [0, 71] ],
|
212
|
+
[ { control_keys: [], virtual_key_code: VK_END }, [0, 79] ],
|
213
|
+
|
214
|
+
# Emulate ANSI key sequence.
|
215
|
+
[ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ],
|
216
|
+
]
|
217
|
+
|
218
|
+
@@hsg = nil
|
219
|
+
|
220
|
+
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
221
|
+
|
222
|
+
# high-surrogate
|
223
|
+
if 0xD800 <= char_code and char_code <= 0xDBFF
|
224
|
+
@@hsg = char_code
|
225
|
+
return
|
154
226
|
end
|
155
|
-
|
156
|
-
|
157
|
-
if
|
158
|
-
@@
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
begin
|
164
|
-
bytes = ret.chr(Encoding::UTF_8).bytes
|
165
|
-
@@input_buf.push(*bytes)
|
166
|
-
rescue Encoding::UndefinedConversionError
|
167
|
-
@@input_buf << ret
|
168
|
-
@@input_buf << @@getwch.call if ret == 224
|
227
|
+
# low-surrogate
|
228
|
+
if 0xDC00 <= char_code and char_code <= 0xDFFF
|
229
|
+
if @@hsg
|
230
|
+
char_code = 0x10000 + (@@hsg - 0xD800) * 0x400 + char_code - 0xDC00
|
231
|
+
@@hsg = nil
|
232
|
+
else
|
233
|
+
# no high-surrogate. ignored.
|
234
|
+
return
|
169
235
|
end
|
236
|
+
else
|
237
|
+
# ignore high-surrogate without low-surrogate if there
|
238
|
+
@@hsg = nil
|
170
239
|
end
|
171
|
-
|
240
|
+
|
241
|
+
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
242
|
+
|
243
|
+
match = KEY_MAP.find { |args,| key.matches?(**args) }
|
244
|
+
unless match.nil?
|
245
|
+
@@output_buf.concat(match.last)
|
246
|
+
return
|
247
|
+
end
|
248
|
+
|
249
|
+
# no char, only control keys
|
250
|
+
return if key.char_code == 0 and key.control_keys.any?
|
251
|
+
|
252
|
+
@@output_buf.push("\e".ord) if key.control_keys.include?(:ALT)
|
253
|
+
|
254
|
+
@@output_buf.concat(key.char.bytes)
|
172
255
|
end
|
173
256
|
|
174
|
-
def self.
|
257
|
+
def self.check_input_event
|
175
258
|
num_of_events = 0.chr * 8
|
176
|
-
while @@
|
177
|
-
|
259
|
+
while @@output_buf.empty?
|
260
|
+
Reline.core.line_editor.resize
|
261
|
+
if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
262
|
+
# prevent for background consolemode change
|
263
|
+
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
264
|
+
next
|
265
|
+
end
|
266
|
+
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack1('L') == 0
|
267
|
+
input_records = 0.chr * 20 * 80
|
178
268
|
read_event = 0.chr * 4
|
179
|
-
if @@
|
180
|
-
|
181
|
-
|
182
|
-
|
269
|
+
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_records, 80, read_event) != 0
|
270
|
+
read_events = read_event.unpack1('L')
|
271
|
+
0.upto(read_events) do |idx|
|
272
|
+
input_record = input_records[idx * 20, 20]
|
273
|
+
event = input_record[0, 2].unpack1('s*')
|
274
|
+
case event
|
275
|
+
when WINDOW_BUFFER_SIZE_EVENT
|
276
|
+
@@winch_handler.()
|
277
|
+
when KEY_EVENT
|
278
|
+
key_down = input_record[4, 4].unpack1('l*')
|
279
|
+
repeat_count = input_record[8, 2].unpack1('s*')
|
280
|
+
virtual_key_code = input_record[10, 2].unpack1('s*')
|
281
|
+
virtual_scan_code = input_record[12, 2].unpack1('s*')
|
282
|
+
char_code = input_record[14, 2].unpack1('S*')
|
283
|
+
control_key_state = input_record[16, 2].unpack1('S*')
|
284
|
+
is_key_down = key_down.zero? ? false : true
|
285
|
+
if is_key_down
|
286
|
+
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
287
|
+
end
|
288
|
+
end
|
183
289
|
end
|
184
290
|
end
|
185
291
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
control = (@@GetKeyState.call(VK_CONTROL) & 0x80) != 0
|
192
|
-
shift = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0
|
193
|
-
force_enter = !input.instance_of?(Array) && (control or shift) && input == 0x0D
|
194
|
-
if force_enter
|
195
|
-
# It's treated as Meta+Enter on Windows
|
196
|
-
@@output_buf.push("\e".ord)
|
197
|
-
@@output_buf.push(input)
|
198
|
-
else
|
199
|
-
case input
|
200
|
-
when 0x00
|
201
|
-
meta = false
|
202
|
-
@@output_buf.push(input)
|
203
|
-
input = getwch
|
204
|
-
@@output_buf.push(*input)
|
205
|
-
when 0xE0
|
206
|
-
@@output_buf.push(input)
|
207
|
-
input = getwch
|
208
|
-
@@output_buf.push(*input)
|
209
|
-
when 0x03
|
210
|
-
@@output_buf.push(input)
|
211
|
-
else
|
212
|
-
@@output_buf.push(input)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
if meta
|
216
|
-
"\e".ord
|
217
|
-
else
|
218
|
-
@@output_buf.shift
|
219
|
-
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def self.getc
|
295
|
+
check_input_event
|
296
|
+
@@output_buf.shift
|
220
297
|
end
|
221
298
|
|
222
299
|
def self.ungetc(c)
|
@@ -228,7 +305,7 @@ class Reline::Windows
|
|
228
305
|
end
|
229
306
|
|
230
307
|
def self.empty_buffer?
|
231
|
-
if not @@
|
308
|
+
if not @@output_buf.empty?
|
232
309
|
false
|
233
310
|
elsif @@kbhit.call == 0
|
234
311
|
true
|
@@ -237,17 +314,37 @@ class Reline::Windows
|
|
237
314
|
end
|
238
315
|
end
|
239
316
|
|
240
|
-
def self.
|
317
|
+
def self.get_console_screen_buffer_info
|
318
|
+
# CONSOLE_SCREEN_BUFFER_INFO
|
319
|
+
# [ 0,2] dwSize.X
|
320
|
+
# [ 2,2] dwSize.Y
|
321
|
+
# [ 4,2] dwCursorPositions.X
|
322
|
+
# [ 6,2] dwCursorPositions.Y
|
323
|
+
# [ 8,2] wAttributes
|
324
|
+
# [10,2] srWindow.Left
|
325
|
+
# [12,2] srWindow.Top
|
326
|
+
# [14,2] srWindow.Right
|
327
|
+
# [16,2] srWindow.Bottom
|
328
|
+
# [18,2] dwMaximumWindowSize.X
|
329
|
+
# [20,2] dwMaximumWindowSize.Y
|
241
330
|
csbi = 0.chr * 22
|
242
|
-
@@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
|
331
|
+
return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
|
332
|
+
csbi
|
333
|
+
end
|
334
|
+
|
335
|
+
def self.get_screen_size
|
336
|
+
unless csbi = get_console_screen_buffer_info
|
337
|
+
return [1, 1]
|
338
|
+
end
|
243
339
|
csbi[0, 4].unpack('SS').reverse
|
244
340
|
end
|
245
341
|
|
246
342
|
def self.cursor_pos
|
247
|
-
csbi =
|
248
|
-
|
249
|
-
|
250
|
-
|
343
|
+
unless csbi = get_console_screen_buffer_info
|
344
|
+
return Reline::CursorPos.new(0, 0)
|
345
|
+
end
|
346
|
+
x = csbi[4, 2].unpack1('s')
|
347
|
+
y = csbi[6, 2].unpack1('s')
|
251
348
|
Reline::CursorPos.new(x, y)
|
252
349
|
end
|
253
350
|
|
@@ -267,6 +364,7 @@ class Reline::Windows
|
|
267
364
|
|
268
365
|
def self.move_cursor_down(val)
|
269
366
|
if val > 0
|
367
|
+
return unless csbi = get_console_screen_buffer_info
|
270
368
|
screen_height = get_screen_size.first
|
271
369
|
y = cursor_pos.y + val
|
272
370
|
y = screen_height - 1 if y > (screen_height - 1)
|
@@ -277,42 +375,74 @@ class Reline::Windows
|
|
277
375
|
end
|
278
376
|
|
279
377
|
def self.erase_after_cursor
|
280
|
-
csbi =
|
281
|
-
|
282
|
-
cursor = csbi[4, 4].
|
378
|
+
return unless csbi = get_console_screen_buffer_info
|
379
|
+
attributes = csbi[8, 2].unpack1('S')
|
380
|
+
cursor = csbi[4, 4].unpack1('L')
|
283
381
|
written = 0.chr * 4
|
284
382
|
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written)
|
285
|
-
@@FillConsoleOutputAttribute.call(@@hConsoleHandle,
|
383
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, get_screen_size.last - cursor_pos.x, cursor, written)
|
286
384
|
end
|
287
385
|
|
288
386
|
def self.scroll_down(val)
|
289
|
-
return if val
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
@@
|
387
|
+
return if val < 0
|
388
|
+
return unless csbi = get_console_screen_buffer_info
|
389
|
+
buffer_width, buffer_lines, x, y, attributes, window_left, window_top, window_bottom = csbi.unpack('ssssSssx2s')
|
390
|
+
screen_height = window_bottom - window_top + 1
|
391
|
+
val = screen_height if val > screen_height
|
392
|
+
|
393
|
+
if @@legacy_console || window_left != 0
|
394
|
+
# unless ENABLE_VIRTUAL_TERMINAL,
|
395
|
+
# if srWindow.Left != 0 then it's conhost.exe hosted console
|
396
|
+
# and puts "\n" causes horizontal scroll. its glitch.
|
397
|
+
# FYI irb write from culumn 1, so this gives no gain.
|
398
|
+
scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4')
|
399
|
+
destination_origin = 0 # y * 65536 + x
|
400
|
+
fill = [' '.ord, attributes].pack('SS')
|
401
|
+
@@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
|
402
|
+
else
|
403
|
+
origin_x = x + 1
|
404
|
+
origin_y = y - window_top + 1
|
405
|
+
@@output.write [
|
406
|
+
(origin_y != screen_height) ? "\e[#{screen_height};H" : nil,
|
407
|
+
"\n" * val,
|
408
|
+
(origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil
|
409
|
+
].join
|
410
|
+
end
|
296
411
|
end
|
297
412
|
|
298
413
|
def self.clear_screen
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
414
|
+
if @@legacy_console
|
415
|
+
return unless csbi = get_console_screen_buffer_info
|
416
|
+
buffer_width, _buffer_lines, attributes, window_top, window_bottom = csbi.unpack('ss@8S@12sx2s')
|
417
|
+
fill_length = buffer_width * (window_bottom - window_top + 1)
|
418
|
+
screen_topleft = window_top * 65536
|
419
|
+
written = 0.chr * 4
|
420
|
+
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
|
421
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written)
|
422
|
+
@@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft)
|
423
|
+
else
|
424
|
+
@@output.write "\e[2J" "\e[H"
|
425
|
+
end
|
310
426
|
end
|
311
427
|
|
312
428
|
def self.set_screen_size(rows, columns)
|
313
429
|
raise NotImplementedError
|
314
430
|
end
|
315
431
|
|
432
|
+
def self.hide_cursor
|
433
|
+
size = 100
|
434
|
+
visible = 0 # 0 means false
|
435
|
+
cursor_info = [size, visible].pack('Li')
|
436
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
437
|
+
end
|
438
|
+
|
439
|
+
def self.show_cursor
|
440
|
+
size = 100
|
441
|
+
visible = 1 # 1 means true
|
442
|
+
cursor_info = [size, visible].pack('Li')
|
443
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
444
|
+
end
|
445
|
+
|
316
446
|
def self.set_winch_handler(&handler)
|
317
447
|
@@winch_handler = handler
|
318
448
|
end
|
@@ -325,4 +455,43 @@ class Reline::Windows
|
|
325
455
|
def self.deprep(otio)
|
326
456
|
# do nothing
|
327
457
|
end
|
458
|
+
|
459
|
+
class KeyEventRecord
|
460
|
+
|
461
|
+
attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys
|
462
|
+
|
463
|
+
def initialize(virtual_key_code, char_code, control_key_state)
|
464
|
+
@virtual_key_code = virtual_key_code
|
465
|
+
@char_code = char_code
|
466
|
+
@control_key_state = control_key_state
|
467
|
+
@enhanced = control_key_state & ENHANCED_KEY != 0
|
468
|
+
|
469
|
+
(@control_keys = []).tap do |control_keys|
|
470
|
+
# symbols must be sorted to make comparison is easier later on
|
471
|
+
control_keys << :ALT if control_key_state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0
|
472
|
+
control_keys << :CTRL if control_key_state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0
|
473
|
+
control_keys << :SHIFT if control_key_state & SHIFT_PRESSED != 0
|
474
|
+
end.freeze
|
475
|
+
end
|
476
|
+
|
477
|
+
def char
|
478
|
+
@char_code.chr(Encoding::UTF_8)
|
479
|
+
end
|
480
|
+
|
481
|
+
def enhanced?
|
482
|
+
@enhanced
|
483
|
+
end
|
484
|
+
|
485
|
+
# Verifies if the arguments match with this key event.
|
486
|
+
# Nil arguments are ignored, but at least one must be passed as non-nil.
|
487
|
+
# To verify that no control keys were pressed, pass an empty array: `control_keys: []`.
|
488
|
+
def matches?(control_keys: nil, virtual_key_code: nil, char_code: nil)
|
489
|
+
raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil?
|
490
|
+
|
491
|
+
(control_keys.nil? || [*control_keys].sort == @control_keys) &&
|
492
|
+
(virtual_key_code.nil? || @virtual_key_code == virtual_key_code) &&
|
493
|
+
(char_code.nil? || char_code == @char_code)
|
494
|
+
end
|
495
|
+
|
496
|
+
end
|
328
497
|
end
|