This repository was archived by the owner on Sep 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 48
/
Copy pathhterm_keyboard_bindings.js
244 lines (220 loc) · 6.28 KB
/
hterm_keyboard_bindings.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// Copyright (c) 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* @typedef {{
* keyCode: number,
* shift: (boolean|undefined),
* ctrl: (boolean|undefined),
* alt: (boolean|undefined),
* meta: (boolean|undefined),
* }}
*/
hterm.Keyboard.KeyDown;
/**
* @typedef {function(!hterm.Terminal, !hterm.Keyboard.KeyDown):
* !hterm.Keyboard.KeyAction}
*/
hterm.Keyboard.KeyBindingFunction;
/** @typedef {!hterm.Keyboard.KeyAction|!hterm.Keyboard.KeyBindingFunction} */
hterm.Keyboard.KeyBindingAction;
/**
* @typedef {{
* keyPattern: !hterm.Keyboard.KeyPattern,
* action: !hterm.Keyboard.KeyBindingAction,
* }}
*/
hterm.Keyboard.KeyBinding;
/**
* A mapping from hterm.Keyboard.KeyPattern to an action.
*
* TODO(rginda): For now this bindings code is only used for user overrides.
* hterm.Keyboard.KeyMap still handles all of the built-in key mappings.
* It'd be nice if we migrated that over to be hterm.Keyboard.Bindings based.
*
* @constructor
*/
hterm.Keyboard.Bindings = function() {
/** @private {!Object<number, !Array<!hterm.Keyboard.KeyBinding>>} */
this.bindings_ = {};
};
/**
* Default bindings for each OS.
*
* @type {!Object<string, !Object<string, string>>}
*/
hterm.Keyboard.Bindings.OsDefaults = {
'android': {
},
'cros': {
// Submit feedback.
'Alt+Shift+I': 'PASS',
// Toggle chromevox.
'Ctrl+Alt+Z': 'PASS',
// Switch input method.
'Ctrl+Space': 'PASS',
},
'linux': {
},
'mac': {
// Home.
'Meta+Left': '"\u001b[H"',
// End.
'Meta+Right': '"\u001b[F"',
},
'windows': {
},
};
/**
* Remove all bindings.
*/
hterm.Keyboard.Bindings.prototype.clear = function() {
this.bindings_ = {};
};
/**
* Add a new binding.
*
* Internal API that assumes parsed objects as inputs.
* See the public addBinding for more details.
*
* @param {!hterm.Keyboard.KeyPattern} keyPattern
* @param {!hterm.Keyboard.KeyBindingAction} action
*/
hterm.Keyboard.Bindings.prototype.addBinding_ = function(keyPattern, action) {
let binding = null;
const list = this.bindings_[keyPattern.keyCode];
if (list) {
for (let i = 0; i < list.length; i++) {
if (list[i].keyPattern.matchKeyPattern(keyPattern)) {
binding = list[i];
break;
}
}
}
if (binding) {
binding.action = action;
} else {
binding = {keyPattern: keyPattern, action: action};
if (!list) {
this.bindings_[keyPattern.keyCode] = [binding];
} else {
this.bindings_[keyPattern.keyCode].push(binding);
list.sort(function(a, b) {
return hterm.Keyboard.KeyPattern.sortCompare(
a.keyPattern, b.keyPattern);
});
}
}
};
/**
* Add a new binding.
*
* If a binding for the keyPattern already exists it will be overridden.
*
* More specific keyPatterns take precedence over those with wildcards. Given
* bindings for "Ctrl+A" and "Ctrl+*+A", and a "Ctrl+A" keydown, the "Ctrl+A"
* binding will match even if "Ctrl+*+A" was created last.
*
* If action is a string, it will be passed through hterm.Parser.parseKeyAction.
*
* For example:
* // Will replace Ctrl+P keystrokes with the string "hiya!".
* addBinding('Ctrl+P', "'hiya!'");
* // Will cancel the keystroke entirely (make it do nothing).
* addBinding('Alt+D', hterm.Keyboard.KeyActions.CANCEL);
* // Will execute the code and return the action.
* addBinding('Ctrl+T', function() {
* console.log('Got a T!');
* return hterm.Keyboard.KeyActions.PASS;
* });
*
* @param {string|!hterm.Keyboard.KeyPattern} key
* @param {!hterm.Keyboard.KeyBindingAction} action
*/
hterm.Keyboard.Bindings.prototype.addBinding = function(key, action) {
// If we're given a hterm.Keyboard.KeyPattern object, pass it down.
if (typeof key != 'string') {
this.addBinding_(key, action);
return;
}
// Here we treat key as a string.
const p = new hterm.Parser();
p.reset(key);
let sequence;
try {
sequence = p.parseKeySequence();
} catch (ex) {
console.error(ex);
return;
}
if (!p.isComplete()) {
console.error(p.error('Expected end of sequence: ' + sequence));
return;
}
// If action is a string, parse it. Otherwise assume it's callable.
if (typeof action == 'string') {
p.reset(action);
try {
action = p.parseKeyAction();
} catch (ex) {
console.error(ex);
return;
}
}
if (!p.isComplete()) {
console.error(p.error('Expected end of sequence: ' + sequence));
return;
}
this.addBinding_(new hterm.Keyboard.KeyPattern(sequence), action);
};
/**
* Add multiple bindings at a time using a map of {string: string, ...}
*
* This uses hterm.Parser to parse the maps key into KeyPatterns, and the
* map values into {!hterm.Keyboard.KeyBindingAction}.
*
* For example:
* {
* // Will replace Ctrl+P keystrokes with the string "hiya!".
* 'Ctrl+P': "'hiya!'",
* // Will cancel the keystroke entirely (make it do nothing).
* 'Alt+D': hterm.Keyboard.KeyActions.CANCEL,
* }
*
* @param {!Object<string, !hterm.Keyboard.KeyBindingAction>} map
* @param {boolean=} addOsDefaults If true, OS defaults are added first to
* ensure user defined mappings take precedence.
*/
hterm.Keyboard.Bindings.prototype.addBindings = function(
map, addOsDefaults = false) {
if (addOsDefaults) {
this.addBindings(hterm.Keyboard.Bindings.OsDefaults[hterm.os] || {});
}
for (const key in map) {
this.addBinding(key, map[key]);
}
};
/**
* Return the binding that is the best match for the given keyDown record,
* or null if there is no match.
*
* @param {!hterm.Keyboard.KeyDown} keyDown An object with a keyCode property
* and zero or more boolean properties representing key modifiers. These
* property names must match those defined in
* hterm.Keyboard.KeyPattern.modifiers.
* @return {?hterm.Keyboard.KeyBinding} The keyboard binding for this key.
*/
hterm.Keyboard.Bindings.prototype.getBinding = function(keyDown) {
const list = this.bindings_[keyDown.keyCode];
if (!list) {
return null;
}
for (let i = 0; i < list.length; i++) {
const binding = list[i];
if (binding.keyPattern.matchKeyDown(keyDown)) {
return binding;
}
}
return null;
};