reline 0.2.4 → 0.2.8.pre.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 +119 -56
- data/lib/reline/config.rb +30 -13
- data/lib/reline/general_io.rb +11 -3
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/line_editor.rb +395 -23
- 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 +164 -80
- data/lib/reline.rb +78 -13
- metadata +6 -62
- data/lib/reline/line_editor.rb.orig +0 -2696
@@ -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,36 @@ 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
|
+
end
|
33
46
|
|
34
47
|
if defined? JRUBY_VERSION
|
35
48
|
require 'win32api'
|
@@ -73,13 +86,36 @@ class Reline::Windows
|
|
73
86
|
end
|
74
87
|
end
|
75
88
|
|
89
|
+
VK_RETURN = 0x0D
|
76
90
|
VK_MENU = 0x12
|
77
91
|
VK_LMENU = 0xA4
|
78
92
|
VK_CONTROL = 0x11
|
79
93
|
VK_SHIFT = 0x10
|
94
|
+
VK_DIVIDE = 0x6F
|
95
|
+
|
96
|
+
KEY_EVENT = 0x01
|
97
|
+
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
98
|
+
|
99
|
+
CAPSLOCK_ON = 0x0080
|
100
|
+
ENHANCED_KEY = 0x0100
|
101
|
+
LEFT_ALT_PRESSED = 0x0002
|
102
|
+
LEFT_CTRL_PRESSED = 0x0008
|
103
|
+
NUMLOCK_ON = 0x0020
|
104
|
+
RIGHT_ALT_PRESSED = 0x0001
|
105
|
+
RIGHT_CTRL_PRESSED = 0x0004
|
106
|
+
SCROLLLOCK_ON = 0x0040
|
107
|
+
SHIFT_PRESSED = 0x0010
|
108
|
+
|
109
|
+
VK_END = 0x23
|
110
|
+
VK_HOME = 0x24
|
111
|
+
VK_LEFT = 0x25
|
112
|
+
VK_UP = 0x26
|
113
|
+
VK_RIGHT = 0x27
|
114
|
+
VK_DOWN = 0x28
|
115
|
+
VK_DELETE = 0x2E
|
116
|
+
|
80
117
|
STD_INPUT_HANDLE = -10
|
81
118
|
STD_OUTPUT_HANDLE = -11
|
82
|
-
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
83
119
|
FILE_TYPE_PIPE = 0x0003
|
84
120
|
FILE_NAME_INFO = 2
|
85
121
|
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
@@ -93,13 +129,15 @@ class Reline::Windows
|
|
93
129
|
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
94
130
|
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
95
131
|
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
96
|
-
@@
|
132
|
+
@@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
|
97
133
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
98
134
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
99
135
|
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
136
|
+
@@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L')
|
100
137
|
|
101
138
|
@@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
102
139
|
@@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
140
|
+
@@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
|
103
141
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
104
142
|
|
105
143
|
private_class_method def self.getconsolemode
|
@@ -145,78 +183,71 @@ class Reline::Windows
|
|
145
183
|
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
146
184
|
end
|
147
185
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
186
|
+
KEY_MAP = [
|
187
|
+
# It's treated as Meta+Enter on Windows.
|
188
|
+
[ { control_keys: :CTRL, virtual_key_code: 0x0D }, "\e\r".bytes ],
|
189
|
+
[ { control_keys: :SHIFT, virtual_key_code: 0x0D }, "\e\r".bytes ],
|
190
|
+
|
191
|
+
# It's treated as Meta+Space on Windows.
|
192
|
+
[ { control_keys: :CTRL, char_code: 0x20 }, "\e ".bytes ],
|
193
|
+
|
194
|
+
# Emulate getwch() key sequences.
|
195
|
+
[ { control_keys: [], virtual_key_code: VK_UP }, [0, 72] ],
|
196
|
+
[ { control_keys: [], virtual_key_code: VK_DOWN }, [0, 80] ],
|
197
|
+
[ { control_keys: [], virtual_key_code: VK_RIGHT }, [0, 77] ],
|
198
|
+
[ { control_keys: [], virtual_key_code: VK_LEFT }, [0, 75] ],
|
199
|
+
[ { control_keys: [], virtual_key_code: VK_DELETE }, [0, 83] ],
|
200
|
+
[ { control_keys: [], virtual_key_code: VK_HOME }, [0, 71] ],
|
201
|
+
[ { control_keys: [], virtual_key_code: VK_END }, [0, 79] ],
|
202
|
+
]
|
203
|
+
|
204
|
+
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
205
|
+
|
206
|
+
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
207
|
+
|
208
|
+
match = KEY_MAP.find { |args,| key.matches?(**args) }
|
209
|
+
unless match.nil?
|
210
|
+
@@output_buf.concat(match.last)
|
211
|
+
return
|
170
212
|
end
|
171
|
-
|
213
|
+
|
214
|
+
# no char, only control keys
|
215
|
+
return if key.char_code == 0 and key.control_keys.any?
|
216
|
+
|
217
|
+
@@output_buf.concat(key.char.bytes)
|
172
218
|
end
|
173
219
|
|
174
|
-
def self.
|
220
|
+
def self.check_input_event
|
175
221
|
num_of_events = 0.chr * 8
|
176
|
-
while @@
|
222
|
+
while @@output_buf.empty? #or true
|
223
|
+
next if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
224
|
+
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
|
177
225
|
input_record = 0.chr * 18
|
178
226
|
read_event = 0.chr * 4
|
179
|
-
if @@
|
227
|
+
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
180
228
|
event = input_record[0, 2].unpack('s*').first
|
181
|
-
|
229
|
+
case event
|
230
|
+
when WINDOW_BUFFER_SIZE_EVENT
|
182
231
|
@@winch_handler.()
|
232
|
+
when KEY_EVENT
|
233
|
+
key_down = input_record[4, 4].unpack('l*').first
|
234
|
+
repeat_count = input_record[8, 2].unpack('s*').first
|
235
|
+
virtual_key_code = input_record[10, 2].unpack('s*').first
|
236
|
+
virtual_scan_code = input_record[12, 2].unpack('s*').first
|
237
|
+
char_code = input_record[14, 2].unpack('S*').first
|
238
|
+
control_key_state = input_record[16, 2].unpack('S*').first
|
239
|
+
is_key_down = key_down.zero? ? false : true
|
240
|
+
if is_key_down
|
241
|
+
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
242
|
+
end
|
183
243
|
end
|
184
244
|
end
|
185
245
|
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
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.getc
|
249
|
+
check_input_event
|
250
|
+
@@output_buf.shift
|
220
251
|
end
|
221
252
|
|
222
253
|
def self.ungetc(c)
|
@@ -313,6 +344,20 @@ class Reline::Windows
|
|
313
344
|
raise NotImplementedError
|
314
345
|
end
|
315
346
|
|
347
|
+
def self.hide_cursor
|
348
|
+
size = 100
|
349
|
+
visible = 0 # 0 means false
|
350
|
+
cursor_info = [size, visible].pack('Li')
|
351
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.show_cursor
|
355
|
+
size = 100
|
356
|
+
visible = 1 # 1 means true
|
357
|
+
cursor_info = [size, visible].pack('Li')
|
358
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
359
|
+
end
|
360
|
+
|
316
361
|
def self.set_winch_handler(&handler)
|
317
362
|
@@winch_handler = handler
|
318
363
|
end
|
@@ -325,4 +370,43 @@ class Reline::Windows
|
|
325
370
|
def self.deprep(otio)
|
326
371
|
# do nothing
|
327
372
|
end
|
373
|
+
|
374
|
+
class KeyEventRecord
|
375
|
+
|
376
|
+
attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys
|
377
|
+
|
378
|
+
def initialize(virtual_key_code, char_code, control_key_state)
|
379
|
+
@virtual_key_code = virtual_key_code
|
380
|
+
@char_code = char_code
|
381
|
+
@control_key_state = control_key_state
|
382
|
+
@enhanced = control_key_state & ENHANCED_KEY != 0
|
383
|
+
|
384
|
+
(@control_keys = []).tap do |control_keys|
|
385
|
+
# symbols must be sorted to make comparison is easier later on
|
386
|
+
control_keys << :ALT if control_key_state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0
|
387
|
+
control_keys << :CTRL if control_key_state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0
|
388
|
+
control_keys << :SHIFT if control_key_state & SHIFT_PRESSED != 0
|
389
|
+
end.freeze
|
390
|
+
end
|
391
|
+
|
392
|
+
def char
|
393
|
+
@char_code.chr(Encoding::UTF_8)
|
394
|
+
end
|
395
|
+
|
396
|
+
def enhanced?
|
397
|
+
@enhanced
|
398
|
+
end
|
399
|
+
|
400
|
+
# Verifies if the arguments match with this key event.
|
401
|
+
# Nil arguments are ignored, but at least one must be passed as non-nil.
|
402
|
+
# To verify that no control keys were pressed, pass an empty array: `control_keys: []`.
|
403
|
+
def matches?(control_keys: nil, virtual_key_code: nil, char_code: nil)
|
404
|
+
raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil?
|
405
|
+
|
406
|
+
(control_keys.nil? || [*control_keys].sort == @control_keys) &&
|
407
|
+
(virtual_key_code.nil? || @virtual_key_code == virtual_key_code) &&
|
408
|
+
(char_code.nil? || char_code == @char_code)
|
409
|
+
end
|
410
|
+
|
411
|
+
end
|
328
412
|
end
|