reline 0.1.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 +50 -0
- data/lib/reline/ansi.rb +206 -58
- data/lib/reline/config.rb +71 -21
- data/lib/reline/general_io.rb +31 -3
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/key_actor/emacs.rb +2 -2
- data/lib/reline/key_actor/vi_command.rb +2 -2
- data/lib/reline/key_stroke.rb +64 -14
- data/lib/reline/kill_ring.rb +12 -0
- data/lib/reline/line_editor.rb +1332 -291
- data/lib/reline/sibori.rb +170 -0
- data/lib/reline/terminfo.rb +171 -0
- data/lib/reline/unicode/east_asian_width.rb +1149 -1130
- data/lib/reline/unicode.rb +103 -33
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +324 -109
- data/lib/reline.rb +184 -39
- data/license_of_rb-readline +25 -0
- metadata +6 -45
data/lib/reline/windows.rb
CHANGED
@@ -9,23 +9,48 @@ class Reline::Windows
|
|
9
9
|
true
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
12
|
+
def self.win_legacy_console?
|
13
|
+
@@legacy_console
|
14
|
+
end
|
15
|
+
|
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
|
29
54
|
|
30
55
|
if defined? JRUBY_VERSION
|
31
56
|
require 'win32api'
|
@@ -69,13 +94,37 @@ class Reline::Windows
|
|
69
94
|
end
|
70
95
|
end
|
71
96
|
|
97
|
+
VK_RETURN = 0x0D
|
72
98
|
VK_MENU = 0x12
|
73
99
|
VK_LMENU = 0xA4
|
74
100
|
VK_CONTROL = 0x11
|
75
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
|
+
|
76
126
|
STD_INPUT_HANDLE = -10
|
77
127
|
STD_OUTPUT_HANDLE = -11
|
78
|
-
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
79
128
|
FILE_TYPE_PIPE = 0x0003
|
80
129
|
FILE_NAME_INFO = 2
|
81
130
|
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
@@ -89,15 +138,39 @@ class Reline::Windows
|
|
89
138
|
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
90
139
|
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
91
140
|
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
92
|
-
@@
|
141
|
+
@@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
|
93
142
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
94
143
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
95
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')
|
150
|
+
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
151
|
+
|
152
|
+
private_class_method def self.getconsolemode
|
153
|
+
mode = "\000\000\000\000"
|
154
|
+
@@GetConsoleMode.call(@@hConsoleHandle, mode)
|
155
|
+
mode.unpack1('L')
|
156
|
+
end
|
157
|
+
|
158
|
+
private_class_method def self.setconsolemode(mode)
|
159
|
+
@@SetConsoleMode.call(@@hConsoleHandle, mode)
|
160
|
+
end
|
161
|
+
|
162
|
+
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
163
|
+
#if @@legacy_console
|
164
|
+
# setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
165
|
+
# @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
166
|
+
#end
|
96
167
|
|
97
168
|
@@input_buf = []
|
98
169
|
@@output_buf = []
|
99
170
|
|
100
|
-
|
171
|
+
@@output = STDOUT
|
172
|
+
|
173
|
+
def self.msys_tty?(io = @@hConsoleInputHandle)
|
101
174
|
# check if fd is a pipe
|
102
175
|
if @@GetFileType.call(io) != FILE_TYPE_PIPE
|
103
176
|
return false
|
@@ -113,7 +186,7 @@ class Reline::Windows
|
|
113
186
|
# DWORD FileNameLength;
|
114
187
|
# WCHAR FileName[1];
|
115
188
|
# } FILE_NAME_INFO
|
116
|
-
len = p_buffer[0, 4].
|
189
|
+
len = p_buffer[0, 4].unpack1("L")
|
117
190
|
name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
|
118
191
|
|
119
192
|
# Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX')
|
@@ -121,95 +194,157 @@ class Reline::Windows
|
|
121
194
|
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
122
195
|
end
|
123
196
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
130
226
|
end
|
131
|
-
|
132
|
-
|
133
|
-
if
|
134
|
-
@@
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
begin
|
140
|
-
bytes = ret.chr(Encoding::UTF_8).bytes
|
141
|
-
@@input_buf.push(*bytes)
|
142
|
-
rescue Encoding::UndefinedConversionError
|
143
|
-
@@input_buf << ret
|
144
|
-
@@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
|
145
235
|
end
|
236
|
+
else
|
237
|
+
# ignore high-surrogate without low-surrogate if there
|
238
|
+
@@hsg = nil
|
239
|
+
end
|
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
|
146
247
|
end
|
147
|
-
|
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)
|
148
255
|
end
|
149
256
|
|
150
|
-
def self.
|
257
|
+
def self.check_input_event
|
151
258
|
num_of_events = 0.chr * 8
|
152
|
-
while @@
|
153
|
-
|
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
|
154
268
|
read_event = 0.chr * 4
|
155
|
-
if @@
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
159
289
|
end
|
160
290
|
end
|
161
291
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
control = (@@GetKeyState.call(VK_CONTROL) & 0x80) != 0
|
168
|
-
shift = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0
|
169
|
-
force_enter = !input.instance_of?(Array) && (control or shift) && input == 0x0D
|
170
|
-
if force_enter
|
171
|
-
# It's treated as Meta+Enter on Windows
|
172
|
-
@@output_buf.push("\e".ord)
|
173
|
-
@@output_buf.push(input)
|
174
|
-
else
|
175
|
-
case input
|
176
|
-
when 0x00
|
177
|
-
meta = false
|
178
|
-
@@output_buf.push(input)
|
179
|
-
input = getwch
|
180
|
-
@@output_buf.push(*input)
|
181
|
-
when 0xE0
|
182
|
-
@@output_buf.push(input)
|
183
|
-
input = getwch
|
184
|
-
@@output_buf.push(*input)
|
185
|
-
when 0x03
|
186
|
-
@@output_buf.push(input)
|
187
|
-
else
|
188
|
-
@@output_buf.push(input)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
if meta
|
192
|
-
"\e".ord
|
193
|
-
else
|
194
|
-
@@output_buf.shift
|
195
|
-
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def self.getc
|
295
|
+
check_input_event
|
296
|
+
@@output_buf.shift
|
196
297
|
end
|
197
298
|
|
198
299
|
def self.ungetc(c)
|
199
300
|
@@output_buf.unshift(c)
|
200
301
|
end
|
201
302
|
|
202
|
-
def self.
|
303
|
+
def self.in_pasting?
|
304
|
+
not self.empty_buffer?
|
305
|
+
end
|
306
|
+
|
307
|
+
def self.empty_buffer?
|
308
|
+
if not @@output_buf.empty?
|
309
|
+
false
|
310
|
+
elsif @@kbhit.call == 0
|
311
|
+
true
|
312
|
+
else
|
313
|
+
false
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
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
|
203
330
|
csbi = 0.chr * 22
|
204
|
-
@@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
|
205
339
|
csbi[0, 4].unpack('SS').reverse
|
206
340
|
end
|
207
341
|
|
208
342
|
def self.cursor_pos
|
209
|
-
csbi =
|
210
|
-
|
211
|
-
|
212
|
-
|
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')
|
213
348
|
Reline::CursorPos.new(x, y)
|
214
349
|
end
|
215
350
|
|
@@ -219,7 +354,9 @@ class Reline::Windows
|
|
219
354
|
|
220
355
|
def self.move_cursor_up(val)
|
221
356
|
if val > 0
|
222
|
-
|
357
|
+
y = cursor_pos.y - val
|
358
|
+
y = 0 if y < 0
|
359
|
+
@@SetConsoleCursorPosition.call(@@hConsoleHandle, y * 65536 + cursor_pos.x)
|
223
360
|
elsif val < 0
|
224
361
|
move_cursor_down(-val)
|
225
362
|
end
|
@@ -227,6 +364,10 @@ class Reline::Windows
|
|
227
364
|
|
228
365
|
def self.move_cursor_down(val)
|
229
366
|
if val > 0
|
367
|
+
return unless csbi = get_console_screen_buffer_info
|
368
|
+
screen_height = get_screen_size.first
|
369
|
+
y = cursor_pos.y + val
|
370
|
+
y = screen_height - 1 if y > (screen_height - 1)
|
230
371
|
@@SetConsoleCursorPosition.call(@@hConsoleHandle, (cursor_pos.y + val) * 65536 + cursor_pos.x)
|
231
372
|
elsif val < 0
|
232
373
|
move_cursor_up(-val)
|
@@ -234,39 +375,74 @@ class Reline::Windows
|
|
234
375
|
end
|
235
376
|
|
236
377
|
def self.erase_after_cursor
|
237
|
-
csbi =
|
238
|
-
|
239
|
-
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')
|
240
381
|
written = 0.chr * 4
|
241
382
|
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written)
|
383
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, get_screen_size.last - cursor_pos.x, cursor, written)
|
242
384
|
end
|
243
385
|
|
244
386
|
def self.scroll_down(val)
|
245
|
-
return if val
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
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
|
250
411
|
end
|
251
412
|
|
252
413
|
def self.clear_screen
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
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
|
264
426
|
end
|
265
427
|
|
266
428
|
def self.set_screen_size(rows, columns)
|
267
429
|
raise NotImplementedError
|
268
430
|
end
|
269
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
|
+
|
270
446
|
def self.set_winch_handler(&handler)
|
271
447
|
@@winch_handler = handler
|
272
448
|
end
|
@@ -279,4 +455,43 @@ class Reline::Windows
|
|
279
455
|
def self.deprep(otio)
|
280
456
|
# do nothing
|
281
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
|
282
497
|
end
|