reline 0.2.5 → 0.2.8.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -0
- data/lib/reline/ansi.rb +120 -56
- data/lib/reline/config.rb +39 -13
- data/lib/reline/general_io.rb +11 -3
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/line_editor.rb +347 -18
- data/lib/reline/terminfo.rb +126 -0
- data/lib/reline/unicode.rb +30 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +176 -80
- data/lib/reline.rb +83 -13
- metadata +6 -61
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'fiddle'
|
2
|
+
require 'fiddle/import'
|
3
|
+
|
4
|
+
module Reline::Terminfo
|
5
|
+
extend Fiddle::Importer
|
6
|
+
|
7
|
+
class TerminfoError < StandardError; end
|
8
|
+
|
9
|
+
def self.curses_dl_files
|
10
|
+
case RUBY_PLATFORM
|
11
|
+
when /mingw/, /mswin/
|
12
|
+
# aren't supported
|
13
|
+
[]
|
14
|
+
when /cygwin/
|
15
|
+
%w[cygncursesw-10.dll cygncurses-10.dll]
|
16
|
+
when /darwin/
|
17
|
+
%w[libncursesw.dylib libcursesw.dylib libncurses.dylib libcurses.dylib]
|
18
|
+
else
|
19
|
+
%w[libncursesw.so libcursesw.so libncurses.so libcurses.so]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
@curses_dl = false
|
24
|
+
def self.curses_dl
|
25
|
+
return @curses_dl unless @curses_dl == false
|
26
|
+
if RUBY_VERSION >= '3.0.0'
|
27
|
+
# Gem module isn't defined in test-all of the Ruby repository, and
|
28
|
+
# Fiddle in Ruby 3.0.0 or later supports Fiddle::TYPE_VARIADIC.
|
29
|
+
fiddle_supports_variadic = true
|
30
|
+
elsif Fiddle.const_defined?(:VERSION) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
|
31
|
+
# Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1.
|
32
|
+
fiddle_supports_variadic = true
|
33
|
+
else
|
34
|
+
fiddle_supports_variadic = false
|
35
|
+
end
|
36
|
+
if fiddle_supports_variadic and not Fiddle.const_defined?(:TYPE_VARIADIC)
|
37
|
+
# If the libffi version is not 3.0.5 or higher, there isn't TYPE_VARIADIC.
|
38
|
+
fiddle_supports_variadic = false
|
39
|
+
end
|
40
|
+
if fiddle_supports_variadic
|
41
|
+
curses_dl_files.each do |curses_name|
|
42
|
+
result = Fiddle::Handle.new(curses_name)
|
43
|
+
rescue Fiddle::DLError
|
44
|
+
next
|
45
|
+
else
|
46
|
+
@curses_dl = result
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@curses_dl = nil if @curses_dl == false
|
51
|
+
@curses_dl
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module Reline::Terminfo
|
56
|
+
dlload curses_dl
|
57
|
+
#extern 'int setupterm(char *term, int fildes, int *errret)'
|
58
|
+
@setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
59
|
+
#extern 'char *tigetstr(char *capname)'
|
60
|
+
@tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP)
|
61
|
+
begin
|
62
|
+
#extern 'char *tiparm(const char *str, ...)'
|
63
|
+
@tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
|
64
|
+
rescue Fiddle::DLError
|
65
|
+
# OpenBSD lacks tiparm
|
66
|
+
#extern 'char *tparm(const char *str, ...)'
|
67
|
+
@tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
|
68
|
+
end
|
69
|
+
# TODO: add int tigetflag(char *capname) and int tigetnum(char *capname)
|
70
|
+
|
71
|
+
def self.setupterm(term, fildes)
|
72
|
+
errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
|
73
|
+
ret = @setupterm.(term, fildes, errret_int)
|
74
|
+
errret = errret_int.unpack('i')[0]
|
75
|
+
case ret
|
76
|
+
when 0 # OK
|
77
|
+
0
|
78
|
+
when -1 # ERR
|
79
|
+
case errret
|
80
|
+
when 1
|
81
|
+
raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
|
82
|
+
when 0
|
83
|
+
raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.')
|
84
|
+
when -1
|
85
|
+
raise TerminfoError.new('The terminfo database could not be found.')
|
86
|
+
else # unknown
|
87
|
+
-1
|
88
|
+
end
|
89
|
+
else # unknown
|
90
|
+
-2
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class StringWithTiparm < String
|
95
|
+
def tiparm(*args) # for method chain
|
96
|
+
Reline::Terminfo.tiparm(self, *args)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.tigetstr(capname)
|
101
|
+
capability = @tigetstr.(capname)
|
102
|
+
case capability.to_i
|
103
|
+
when 0, -1
|
104
|
+
raise TerminfoError, "can't find capability: #{capname}"
|
105
|
+
end
|
106
|
+
StringWithTiparm.new(capability.to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.tiparm(str, *args)
|
110
|
+
new_args = []
|
111
|
+
args.each do |a|
|
112
|
+
new_args << Fiddle::TYPE_INT << a
|
113
|
+
end
|
114
|
+
@tiparm.(str, *new_args).to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.enabled?
|
118
|
+
true
|
119
|
+
end
|
120
|
+
end if Reline::Terminfo.curses_dl
|
121
|
+
|
122
|
+
module Reline::Terminfo
|
123
|
+
def self.enabled?
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end unless Reline::Terminfo.curses_dl
|
data/lib/reline/unicode.rb
CHANGED
@@ -185,6 +185,36 @@ class Reline::Unicode
|
|
185
185
|
[lines, height]
|
186
186
|
end
|
187
187
|
|
188
|
+
# Take a chunk of a String with escape sequences.
|
189
|
+
def self.take_range(str, col, length, encoding = str.encoding)
|
190
|
+
chunk = String.new(encoding: encoding)
|
191
|
+
width = 0
|
192
|
+
rest = str.encode(Encoding::UTF_8)
|
193
|
+
in_zero_width = false
|
194
|
+
rest.scan(WIDTH_SCANNER) do |gc|
|
195
|
+
case
|
196
|
+
when gc[NON_PRINTING_START_INDEX]
|
197
|
+
in_zero_width = true
|
198
|
+
when gc[NON_PRINTING_END_INDEX]
|
199
|
+
in_zero_width = false
|
200
|
+
when gc[CSI_REGEXP_INDEX]
|
201
|
+
chunk << gc[CSI_REGEXP_INDEX]
|
202
|
+
when gc[OSC_REGEXP_INDEX]
|
203
|
+
chunk << gc[OSC_REGEXP_INDEX]
|
204
|
+
when gc[GRAPHEME_CLUSTER_INDEX]
|
205
|
+
gc = gc[GRAPHEME_CLUSTER_INDEX]
|
206
|
+
if in_zero_width
|
207
|
+
chunk << gc
|
208
|
+
else
|
209
|
+
width = get_mbchar_width(gc)
|
210
|
+
break if (width + length) <= col
|
211
|
+
chunk << gc if col <= width
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
chunk
|
216
|
+
end
|
217
|
+
|
188
218
|
def self.get_next_mbchar_size(line, byte_pointer)
|
189
219
|
grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
|
190
220
|
grapheme ? grapheme.bytesize : 0
|
data/lib/reline/version.rb
CHANGED
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
|
@@ -145,78 +192,74 @@ class Reline::Windows
|
|
145
192
|
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
146
193
|
end
|
147
194
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
195
|
+
KEY_MAP = [
|
196
|
+
# It's treated as Meta+Enter on Windows.
|
197
|
+
[ { control_keys: :CTRL, virtual_key_code: 0x0D }, "\e\r".bytes ],
|
198
|
+
[ { control_keys: :SHIFT, virtual_key_code: 0x0D }, "\e\r".bytes ],
|
199
|
+
|
200
|
+
# It's treated as Meta+Space on Windows.
|
201
|
+
[ { control_keys: :CTRL, char_code: 0x20 }, "\e ".bytes ],
|
202
|
+
|
203
|
+
# Emulate getwch() key sequences.
|
204
|
+
[ { control_keys: [], virtual_key_code: VK_UP }, [0, 72] ],
|
205
|
+
[ { control_keys: [], virtual_key_code: VK_DOWN }, [0, 80] ],
|
206
|
+
[ { control_keys: [], virtual_key_code: VK_RIGHT }, [0, 77] ],
|
207
|
+
[ { control_keys: [], virtual_key_code: VK_LEFT }, [0, 75] ],
|
208
|
+
[ { control_keys: [], virtual_key_code: VK_DELETE }, [0, 83] ],
|
209
|
+
[ { control_keys: [], virtual_key_code: VK_HOME }, [0, 71] ],
|
210
|
+
[ { control_keys: [], virtual_key_code: VK_END }, [0, 79] ],
|
211
|
+
|
212
|
+
# Emulate ANSI key sequence.
|
213
|
+
[ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ],
|
214
|
+
]
|
215
|
+
|
216
|
+
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
217
|
+
|
218
|
+
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
219
|
+
|
220
|
+
match = KEY_MAP.find { |args,| key.matches?(**args) }
|
221
|
+
unless match.nil?
|
222
|
+
@@output_buf.concat(match.last)
|
223
|
+
return
|
170
224
|
end
|
171
|
-
|
225
|
+
|
226
|
+
# no char, only control keys
|
227
|
+
return if key.char_code == 0 and key.control_keys.any?
|
228
|
+
|
229
|
+
@@output_buf.concat(key.char.bytes)
|
172
230
|
end
|
173
231
|
|
174
|
-
def self.
|
232
|
+
def self.check_input_event
|
175
233
|
num_of_events = 0.chr * 8
|
176
|
-
while @@
|
234
|
+
while @@output_buf.empty? #or true
|
235
|
+
next if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
236
|
+
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
|
177
237
|
input_record = 0.chr * 18
|
178
238
|
read_event = 0.chr * 4
|
179
|
-
if @@
|
239
|
+
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
180
240
|
event = input_record[0, 2].unpack('s*').first
|
181
|
-
|
241
|
+
case event
|
242
|
+
when WINDOW_BUFFER_SIZE_EVENT
|
182
243
|
@@winch_handler.()
|
244
|
+
when KEY_EVENT
|
245
|
+
key_down = input_record[4, 4].unpack('l*').first
|
246
|
+
repeat_count = input_record[8, 2].unpack('s*').first
|
247
|
+
virtual_key_code = input_record[10, 2].unpack('s*').first
|
248
|
+
virtual_scan_code = input_record[12, 2].unpack('s*').first
|
249
|
+
char_code = input_record[14, 2].unpack('S*').first
|
250
|
+
control_key_state = input_record[16, 2].unpack('S*').first
|
251
|
+
is_key_down = key_down.zero? ? false : true
|
252
|
+
if is_key_down
|
253
|
+
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
254
|
+
end
|
183
255
|
end
|
184
256
|
end
|
185
257
|
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
|
258
|
+
end
|
259
|
+
|
260
|
+
def self.getc
|
261
|
+
check_input_event
|
262
|
+
@@output_buf.shift
|
220
263
|
end
|
221
264
|
|
222
265
|
def self.ungetc(c)
|
@@ -313,6 +356,20 @@ class Reline::Windows
|
|
313
356
|
raise NotImplementedError
|
314
357
|
end
|
315
358
|
|
359
|
+
def self.hide_cursor
|
360
|
+
size = 100
|
361
|
+
visible = 0 # 0 means false
|
362
|
+
cursor_info = [size, visible].pack('Li')
|
363
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
364
|
+
end
|
365
|
+
|
366
|
+
def self.show_cursor
|
367
|
+
size = 100
|
368
|
+
visible = 1 # 1 means true
|
369
|
+
cursor_info = [size, visible].pack('Li')
|
370
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
371
|
+
end
|
372
|
+
|
316
373
|
def self.set_winch_handler(&handler)
|
317
374
|
@@winch_handler = handler
|
318
375
|
end
|
@@ -325,4 +382,43 @@ class Reline::Windows
|
|
325
382
|
def self.deprep(otio)
|
326
383
|
# do nothing
|
327
384
|
end
|
385
|
+
|
386
|
+
class KeyEventRecord
|
387
|
+
|
388
|
+
attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys
|
389
|
+
|
390
|
+
def initialize(virtual_key_code, char_code, control_key_state)
|
391
|
+
@virtual_key_code = virtual_key_code
|
392
|
+
@char_code = char_code
|
393
|
+
@control_key_state = control_key_state
|
394
|
+
@enhanced = control_key_state & ENHANCED_KEY != 0
|
395
|
+
|
396
|
+
(@control_keys = []).tap do |control_keys|
|
397
|
+
# symbols must be sorted to make comparison is easier later on
|
398
|
+
control_keys << :ALT if control_key_state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0
|
399
|
+
control_keys << :CTRL if control_key_state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0
|
400
|
+
control_keys << :SHIFT if control_key_state & SHIFT_PRESSED != 0
|
401
|
+
end.freeze
|
402
|
+
end
|
403
|
+
|
404
|
+
def char
|
405
|
+
@char_code.chr(Encoding::UTF_8)
|
406
|
+
end
|
407
|
+
|
408
|
+
def enhanced?
|
409
|
+
@enhanced
|
410
|
+
end
|
411
|
+
|
412
|
+
# Verifies if the arguments match with this key event.
|
413
|
+
# Nil arguments are ignored, but at least one must be passed as non-nil.
|
414
|
+
# To verify that no control keys were pressed, pass an empty array: `control_keys: []`.
|
415
|
+
def matches?(control_keys: nil, virtual_key_code: nil, char_code: nil)
|
416
|
+
raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil?
|
417
|
+
|
418
|
+
(control_keys.nil? || [*control_keys].sort == @control_keys) &&
|
419
|
+
(virtual_key_code.nil? || @virtual_key_code == virtual_key_code) &&
|
420
|
+
(char_code.nil? || char_code == @char_code)
|
421
|
+
end
|
422
|
+
|
423
|
+
end
|
328
424
|
end
|