src/vs/platform/keybinding/common/keybindingsRegistry.ts

274 LOC · 127 covered · 147 uncovered · 9 ranges · 200 concepts · 1 introducers · 184 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- actions.ts ×20
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 > SessionsContrib = 250,
67 > BuiltinExtension = 300,
68 > ExternalExtension = 400
69 > }
70 >
71 > export interface ICommandAndKeybindingRule<Args extends unknown[] = unknown[]> extends IKeybindingRule {
72 > handler: ICommandHandler<Args>;
73 > metadata?: ICommandMetadata | null;
74 > }
75 >
76 > export interface IKeybindingsRegistry {
77 > registerKeybindingRule(rule: IKeybindingRule): IDisposable;
78 > setExtensionKeybindings(rules: IExtensionKeybindingRule[]): void;
79 > registerCommandAndKeybindingRule<Args extends unknown[] = unknown[]>(desc: ICommandAndKeybindingRule<Args>): IDisposable;
80 > getDefaultKeybindings(): IKeybindingItem[];
81 > getDefaultKeybindingsForOS(os: OperatingSystem): IKeybindingItem[];
82 > }
83 >
84 > /**
85 > * Stores all built-in and extension-provided keybindings (but not ones that user defines themselves)
86 > */
87 > class KeybindingsRegistryImpl implements IKeybindingsRegistry {
88 >
89 > private _coreKeybindings: LinkedList<IKeybindingItem>;
90 > private _coreKeybindingRules: LinkedList<IKeybindingRule>;
91 > private _extensionKeybindings: IKeybindingItem[];
92 > private _cachedMergedKeybindings: IKeybindingItem[] | null;
93 >
94 > constructor() {
95 > this._coreKeybindings = new LinkedList();
96 > this._coreKeybindingRules = new LinkedList();
97 > this._extensionKeybindings = [];
98 > this._cachedMergedKeybindings = null;
99 > }
100 >
101 > private static bindToPlatform(kb: IKeybindings, os: OperatingSystem): { primary?: number; secondary?: number[] } {
102 if (os === OperatingSystem.Windows) {
103 if (kb && kb.win) {
104 return kb.win;
105 }
106 } else if (os === OperatingSystem.Macintosh) {
107 if (kb && kb.mac) {
108 return kb.mac;
109 }
110 } else {
111 if (kb && kb.linux) {
112 return kb.linux;
113 }
114 }
115 return kb;
116 }
118 > /**
119 > * Take current platform into account and reduce to primary & secondary.
120 > */
121 > private static bindToCurrentPlatform(kb: IKeybindings): { primary?: number; secondary?: number[] } {
122 return KeybindingsRegistryImpl.bindToPlatform(kb, OS);
123 }
125 > public registerKeybindingRule(rule: IKeybindingRule): IDisposable {
126 const actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform(rule);
127 const result = new DisposableStore();
128
129 if (actualKb && actualKb.primary) {
130 const kk = decodeKeybinding(actualKb.primary, OS);
131 if (kk) {
132 result.add(this._registerDefaultKeybinding(kk, rule.id, rule.args, rule.weight, 0, rule.when));
133 }
134 }
135
136 if (actualKb && Array.isArray(actualKb.secondary)) {
137 for (let i = 0, len = actualKb.secondary.length; i < len; i++) {
138 const k = actualKb.secondary[i];
139 const kk = decodeKeybinding(k, OS);
140 if (kk) {
141 result.add(this._registerDefaultKeybinding(kk, rule.id, rule.args, rule.weight, -i - 1, rule.when));
142 }
143 }
144 }
145
146 const removeRule = this._coreKeybindingRules.push(rule);
147 result.add(toDisposable(() => { removeRule(); }));
148
149 return result;
150 }
152 > public setExtensionKeybindings(rules: IExtensionKeybindingRule[]): void {
153 const result: IKeybindingItem[] = [];
154 let keybindingsLen = 0;
155 for (const rule of rules) {
156 if (rule.keybinding) {
157 result[keybindingsLen++] = {
158 keybinding: rule.keybinding,
159 command: rule.id,
160 commandArgs: rule.args,
161 when: rule.when,
162 weight1: rule.weight,
163 weight2: 0,
164 extensionId: rule.extensionId || null,
165 isBuiltinExtension: rule.isBuiltinExtension || false
166 };
167 }
168 }
169
170 this._extensionKeybindings = result;
171 this._cachedMergedKeybindings = null;
172 }
174 > public registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): IDisposable {
175 return combinedDisposable(
176 this.registerKeybindingRule(desc),
177 CommandsRegistry.registerCommand(desc)
178 );
179 }
181 > private _registerDefaultKeybinding(keybinding: Keybinding, commandId: string, commandArgs: any, weight1: number, weight2: number, when: ContextKeyExpression | null | undefined): IDisposable {
182 const remove = this._coreKeybindings.push({
183 keybinding: keybinding,
184 command: commandId,
185 commandArgs: commandArgs,
186 when: when,
187 weight1: weight1,
188 weight2: weight2,
189 extensionId: null,
190 isBuiltinExtension: false
191 });
192 this._cachedMergedKeybindings = null;
193
194 return toDisposable(() => {
195 remove();
196 this._cachedMergedKeybindings = null;
197 });
198 }
200 > public getDefaultKeybindings(): IKeybindingItem[] {
201 if (!this._cachedMergedKeybindings) {
202 this._cachedMergedKeybindings = Array.from(this._coreKeybindings).concat(this._extensionKeybindings);
203 this._cachedMergedKeybindings.sort(sorter);
204 }
205 return this._cachedMergedKeybindings.slice(0);
206 }
208 > public getDefaultKeybindingsForOS(os: OperatingSystem): IKeybindingItem[] {
209 const result: IKeybindingItem[] = [];
210 for (const rule of this._coreKeybindingRules) {
211 const actualKb = KeybindingsRegistryImpl.bindToPlatform(rule, os);
212
213 if (actualKb && actualKb.primary) {
214 const kk = decodeKeybinding(actualKb.primary, os);
215 if (kk) {
216 result.push({
217 keybinding: kk,
218 command: rule.id,
219 commandArgs: rule.args,
220 when: rule.when,
221 weight1: rule.weight,
222 weight2: 0,
223 extensionId: null,
224 isBuiltinExtension: false
225 });
226 }
227 }
228
229 if (actualKb && Array.isArray(actualKb.secondary)) {
230 for (let i = 0, len = actualKb.secondary.length; i < len; i++) {
231 const k = actualKb.secondary[i];
232 const kk = decodeKeybinding(k, os);
233 if (kk) {
234 result.push({
235 keybinding: kk,
236 command: rule.id,
237 commandArgs: rule.args,
238 when: rule.when,
239 weight1: rule.weight,
240 weight2: -i - 1,
241 extensionId: null,
242 isBuiltinExtension: false
243 });
244 }
245 }
246 }
247 }
248
249 result.sort(sorter);
250 return result;
251 }
252 > } actions.ts ×20
253 > export const KeybindingsRegistry: IKeybindingsRegistry = new KeybindingsRegistryImpl();
254 >
255 > // Define extension point ids
256 > export const Extensions = {
257 > EditorModes: 'platform.keybindingsRegistry'
258 > };
259 > Registry.add(Extensions.EditorModes, KeybindingsRegistry);
260 >
261 function sorter(a: IKeybindingItem, b: IKeybindingItem): number {
262 if (a.weight1 !== b.weight1) {
263 return a.weight1 - b.weight1;
264 }
265 if (a.command && b.command) {
266 if (a.command < b.command) {
267 return -1;
268 }
269 if (a.command > b.command) {
270 return 1;
271 }
272 }
273 return a.weight2 - b.weight2;
274 }