Monaco Editor 中的 Keybinding 机制( 二 )


3. 注册 keybindings回到例子中的代码:
const action = {id: 'test',label: 'test',precondition: 'isChrome == true',keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyL],run: () => {window.alert('chrome: cmd + k');},};setActionDispose(editorIns.addAction(action));注册过程如下:

Monaco Editor 中的 Keybinding 机制

文章插图
当通过 editorIns.addAction 来注册 keybinding 时,会调用 StandaloneKeybindingServices 实例的 addDynamicKeybinding 方法来注册 keybinding 。
public addDynamicKeybinding(commandId: string,_keybinding: number,handler: ICommandHandler,when: ContextKeyExpression | undefined): IDisposable {const keybinding = createKeybinding(_keybinding, OS);const toDispose = new DisposableStore();if (keybinding) {this._dynamicKeybindings.push({keybinding: keybinding.parts,command: commandId,when: when,weight1: 1000,weight2: 0,extensionId: null,isBuiltinExtension: false,});toDispose.add(toDisposable(() => {for (let i = 0; i < this._dynamicKeybindings.length; i++) {let kb = this._dynamicKeybindings[i];if (kb.command === commandId) {this._dynamicKeybindings.splice(i, 1);this.updateResolver({source: KeybindingSource.Default,});return;}}}));}toDispose.add(CommandsRegistry.registerCommand(commandId, handler));this.updateResolver({ source: KeybindingSource.Default });return toDispose;}会先根据传入的 _keybinding 创建 keybinding 实例,然后连同 command、when 等其他信息存入_dynamicKeybindings 数组中,同时会注册对应的 command,当后面触发 keybinding 时便执行对应的 command 。返回的 toDispose 实例则用于取消对应的 keybinding 和 command 。
回到上面代码中创建 keybinding 实例的地方,createKeybinding 方法会根据传入的 _keybinding 数字和 OS 类型得到实例,大致结构如下(已省略部分属性):
{parts: [{ctrlKey: boolean,shiftKey: boolean,altKey: boolean,metaKey: boolean,keyCode: KeyCode,}],}那么,是怎么通过一个 number 得到所有按键信息的呢?往下看↓↓↓
4. key的转换先看看一开始传入的 keybinding 是什么:
const action = {id: 'test',label: 'test',precondition: 'isChrome == true',keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyL],run: () => {window.alert('chrome: cmd + k');},};传入的 keybinding 就是上面代码中的 keybindings 数组中的元素,monaco.KeyMod.CtrlCmd = 2048,monaco.KeyCode.KeyL = 42,对应的数字是 monaco-editor 中定义的枚举值,与真实的 keyCode 存在对应关系 。所以注册时传入的 keybinding 参数为: 2048 | 42 = 2090
先简单了解下 JS 中的位运算(操作的是32位带符号的二进制整数,下面例子中只用8位简单表示):
按位与(AND)&
对应的位都为1则返回1,否则返回0
例如:
00001010// 10
00000110// 6
------
00000010// 2
按位或(OR)|
对应的位,只要有一个为1则返回1,否则返回0
00001010// 10
00000110// 6
-------
00001110// 14
左移(Left shift)<<
将二进制数每一位向左移动指定位数,左侧移出的位舍弃,右侧补0
00001010// 10
-------// 10 << 2
00101000// 40
右移 >>
将二进制数每位向右移动指定位数,右侧移出的位舍弃,左侧用原来最左边的数补齐
00001010// 10
-------// 10 >> 2
00000010// 2
无符号右移 >>>
将二进制数每位向右移动指定位数,右侧移出的位舍弃,左侧补0
00001010// 10
-------// 10 >> 2
00000010// 2
接下来看下是怎么根据一个数字,创建出对应的 keybinding 实例:
export function createKeybinding(keybinding: number, OS: OperatingSystem): Keybinding | null {if (keybinding === 0) {return null;}const firstPart = (keybinding & 0x0000FFFF) >>> 0;// 处理分两步的keybinding,例如:shift shift,若无第二部分,则chordPart = 0const chordPart = (keybinding & 0xFFFF0000) >>> 16;if (chordPart !== 0) {return new ChordKeybinding([createSimpleKeybinding(firstPart, OS),createSimpleKeybinding(chordPart, OS)]);}return new ChordKeybinding([createSimpleKeybinding(firstPart, OS)]);}

经验总结扩展阅读