Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/keybinding/common/keybindingsRegistry.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { decodeKeybinding, Keybinding } from '../../../base/common/keybindings.js';
7
import { OperatingSystem, OS } from '../../../base/common/platform.js';
8
import { CommandsRegistry, ICommandHandler, ICommandMetadata } from '../../commands/common/commands.js';
9
import { ContextKeyExpression } from '../../contextkey/common/contextkey.js';
10
import { Registry } from '../../registry/common/platform.js';
11
import { combinedDisposable, DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
12
import { LinkedList } from '../../../base/common/linkedList.js';
13
14
export interface IKeybindingItem {
15
keybinding: Keybinding | null;
16
command: string | null;
17
commandArgs?: any;
18
when: ContextKeyExpression | null | undefined;
19
weight1: number;
20
weight2: number;
21
extensionId: string | null;
22
isBuiltinExtension: boolean;
23
}
24
25
export interface IKeybindings {
26
primary?: number;
27
secondary?: number[];
28
win?: {
29
primary: number;
30
secondary?: number[];
31
};
32
linux?: {
33
primary: number;
34
secondary?: number[];
35
};
36
mac?: {
37
primary: number;
38
secondary?: number[];
39
};
40
}
41
42
export interface IKeybindingRule extends IKeybindings {
43
id: string;
44
weight: number;
45
args?: any;
46
/**
47
* Keybinding is disabled if expression returns false.
48
*/
49
when?: ContextKeyExpression | null | undefined;
50
}
51
52
export interface IExtensionKeybindingRule {
53
keybinding: Keybinding | null;
54
id: string;
55
args?: any;
56
weight: number;
57
when: ContextKeyExpression | undefined;
58
extensionId?: string;
59
isBuiltinExtension?: boolean;
60
}
61
62
export const enum KeybindingWeight {
63
EditorCore = 0,
64
EditorContrib = 100,
65
WorkbenchContrib = 200,
66
BuiltinExtension = 300,
67
ExternalExtension = 400
68
}
69
70
export interface ICommandAndKeybindingRule extends IKeybindingRule {
71
handler: ICommandHandler;
72
metadata?: ICommandMetadata | null;
73
}
74
75
export interface IKeybindingsRegistry {
76
registerKeybindingRule(rule: IKeybindingRule): IDisposable;
77
setExtensionKeybindings(rules: IExtensionKeybindingRule[]): void;
78
registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): IDisposable;
79
getDefaultKeybindings(): IKeybindingItem[];
80
}
81
82
/**
83
* Stores all built-in and extension-provided keybindings (but not ones that user defines themselves)
84
*/
85
class KeybindingsRegistryImpl implements IKeybindingsRegistry {
86
87
private _coreKeybindings: LinkedList<IKeybindingItem>;
88
private _extensionKeybindings: IKeybindingItem[];
89
private _cachedMergedKeybindings: IKeybindingItem[] | null;
90
91
constructor() {
92
this._coreKeybindings = new LinkedList();
93
this._extensionKeybindings = [];
94
this._cachedMergedKeybindings = null;
95
}
96
97
/**
98
* Take current platform into account and reduce to primary & secondary.
99
*/
100
private static bindToCurrentPlatform(kb: IKeybindings): { primary?: number; secondary?: number[] } {
101
if (OS === OperatingSystem.Windows) {
102
if (kb && kb.win) {
103
return kb.win;
104
}
105
} else if (OS === OperatingSystem.Macintosh) {
106
if (kb && kb.mac) {
107
return kb.mac;
108
}
109
} else {
110
if (kb && kb.linux) {
111
return kb.linux;
112
}
113
}
114
115
return kb;
116
}
117
118
public registerKeybindingRule(rule: IKeybindingRule): IDisposable {
119
const actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform(rule);
120
const result = new DisposableStore();
121
122
if (actualKb && actualKb.primary) {
123
const kk = decodeKeybinding(actualKb.primary, OS);
124
if (kk) {
125
result.add(this._registerDefaultKeybinding(kk, rule.id, rule.args, rule.weight, 0, rule.when));
126
}
127
}
128
129
if (actualKb && Array.isArray(actualKb.secondary)) {
130
for (let i = 0, len = actualKb.secondary.length; i < len; i++) {
131
const k = actualKb.secondary[i];
132
const kk = decodeKeybinding(k, OS);
133
if (kk) {
134
result.add(this._registerDefaultKeybinding(kk, rule.id, rule.args, rule.weight, -i - 1, rule.when));
135
}
136
}
137
}
138
return result;
139
}
140
141
public setExtensionKeybindings(rules: IExtensionKeybindingRule[]): void {
142
const result: IKeybindingItem[] = [];
143
let keybindingsLen = 0;
144
for (const rule of rules) {
145
if (rule.keybinding) {
146
result[keybindingsLen++] = {
147
keybinding: rule.keybinding,
148
command: rule.id,
149
commandArgs: rule.args,
150
when: rule.when,
151
weight1: rule.weight,
152
weight2: 0,
153
extensionId: rule.extensionId || null,
154
isBuiltinExtension: rule.isBuiltinExtension || false
155
};
156
}
157
}
158
159
this._extensionKeybindings = result;
160
this._cachedMergedKeybindings = null;
161
}
162
163
public registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): IDisposable {
164
return combinedDisposable(
165
this.registerKeybindingRule(desc),
166
CommandsRegistry.registerCommand(desc)
167
);
168
}
169
170
private _registerDefaultKeybinding(keybinding: Keybinding, commandId: string, commandArgs: any, weight1: number, weight2: number, when: ContextKeyExpression | null | undefined): IDisposable {
171
const remove = this._coreKeybindings.push({
172
keybinding: keybinding,
173
command: commandId,
174
commandArgs: commandArgs,
175
when: when,
176
weight1: weight1,
177
weight2: weight2,
178
extensionId: null,
179
isBuiltinExtension: false
180
});
181
this._cachedMergedKeybindings = null;
182
183
return toDisposable(() => {
184
remove();
185
this._cachedMergedKeybindings = null;
186
});
187
}
188
189
public getDefaultKeybindings(): IKeybindingItem[] {
190
if (!this._cachedMergedKeybindings) {
191
this._cachedMergedKeybindings = Array.from(this._coreKeybindings).concat(this._extensionKeybindings);
192
this._cachedMergedKeybindings.sort(sorter);
193
}
194
return this._cachedMergedKeybindings.slice(0);
195
}
196
}
197
export const KeybindingsRegistry: IKeybindingsRegistry = new KeybindingsRegistryImpl();
198
199
// Define extension point ids
200
export const Extensions = {
201
EditorModes: 'platform.keybindingsRegistry'
202
};
203
Registry.add(Extensions.EditorModes, KeybindingsRegistry);
204
205
function sorter(a: IKeybindingItem, b: IKeybindingItem): number {
206
if (a.weight1 !== b.weight1) {
207
return a.weight1 - b.weight1;
208
}
209
if (a.command && b.command) {
210
if (a.command < b.command) {
211
return -1;
212
}
213
if (a.command > b.command) {
214
return 1;
215
}
216
}
217
return a.weight2 - b.weight2;
218
}
219
220