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 { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../base/common/actions.js';
7
>
import * as arrays from '../../../base/common/arrays.js';
8
>
import { IntervalTimer, TimeoutTimer } from '../../../base/common/async.js';
9
>
import { illegalState } from '../../../base/common/errors.js';
10
>
import { Emitter, Event } from '../../../base/common/event.js';
11
>
import { IME } from '../../../base/common/ime.js';
12
>
import { KeyCode } from '../../../base/common/keyCodes.js';
13
>
import { Keybinding, ResolvedChord, ResolvedKeybinding, SingleModifierChord } from '../../../base/common/keybindings.js';
14
>
import { Disposable, IDisposable } from '../../../base/common/lifecycle.js';
15
>
import * as nls from '../../../nls.js';
16
>
17
>
import { ICommandService } from '../../commands/common/commands.js';
18
>
import { IContextKeyService, IContextKeyServiceTarget } from '../../contextkey/common/contextkey.js';
19
>
import { IKeybindingService, IKeyboardEvent, KeybindingsSchemaContribution } from './keybinding.js';
20
>
import { ResolutionResult, KeybindingResolver, ResultKind, NoMatchingKb } from './keybindingResolver.js';
21
>
import { ResolvedKeybindingItem } from './resolvedKeybindingItem.js';
22
>
import { ILogService } from '../../log/common/log.js';
23
>
import { INotificationService, IStatusHandle } from '../../notification/common/notification.js';
24
>
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
25
>
26
>
interface CurrentChord {
27
>
keypress: string;
28
>
label: string | null;
29
>
}
30
>
31
>
const HIGH_FREQ_COMMANDS = /^(cursor|delete|undo|redo|tab|editor\.action\.clipboard)/;
32
>
33
>
export abstract class AbstractKeybindingService extends Disposable implements IKeybindingService {
34
>
35
>
public _serviceBrand: undefined;
36
>
37
>
protected readonly _onDidUpdateKeybindings: Emitter<void> = this._register(new Emitter<void>());
38
>
get onDidUpdateKeybindings(): Event<void> {
39
>
return this._onDidUpdateKeybindings ? this._onDidUpdateKeybindings.event : Event.None; // Sinon stubbing walks properties on prototype
40
>
}
41
>
42
>
/** recently recorded keypresses that can trigger a keybinding;
43
>
*
44
>
* example: say, there's "cmd+k cmd+i" keybinding;
45
>
* the user pressed "cmd+k" (before they press "cmd+i")
46
>
* "cmd+k" would be stored in this array, when on pressing "cmd+i", the service
47
>
* would invoke the command bound by the keybinding
48
>
*/
49
>
private _currentChords: CurrentChord[];
50
>
51
>
private _currentChordChecker: IntervalTimer;
52
>
private _currentChordStatusMessage: IStatusHandle | null;
53
>
private _ignoreSingleModifiers: KeybindingModifierSet;
54
>
private _currentSingleModifier: SingleModifierChord | null;
55
>
private _currentSingleModifierClearTimeout: TimeoutTimer;
56
>
protected _currentlyDispatchingCommandId: string | null;
57
>
58
>
protected _logging: boolean;
59
>
60
>
public get inChordMode(): boolean {
61
return this._currentChords.length > 0;
62
}
64
>
constructor(
65
>
private _contextKeyService: IContextKeyService,
66
>
protected _commandService: ICommandService,
67
>
protected _telemetryService: ITelemetryService,
68
>
private _notificationService: INotificationService,
69
>
protected _logService: ILogService,
70
>
) {
71
>
super();
72
>
73
>
this._currentChords = [];
74
>
this._currentChordChecker = new IntervalTimer();
75
>
this._currentChordStatusMessage = null;
76
>
this._ignoreSingleModifiers = KeybindingModifierSet.EMPTY;
77
>
this._currentSingleModifier = null;
78
>
this._currentSingleModifierClearTimeout = new TimeoutTimer();
79
>
this._currentlyDispatchingCommandId = null;
80
>
this._logging = false;
81
>
}
82
>
83
>
84
>
protected abstract _getResolver(): KeybindingResolver;
85
>
protected abstract _documentHasFocus(): boolean;
86
>
public abstract resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
87
>
public abstract resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
88
>
public abstract resolveUserBinding(userBinding: string): ResolvedKeybinding[];
89
>
public abstract registerSchemaContribution(contribution: KeybindingsSchemaContribution): IDisposable;
90
>
public abstract _dumpDebugInfo(): string;
91
>
public abstract _dumpDebugInfoJSON(): string;
92
>
93
>
public getDefaultKeybindingsContent(): string {
94
return '';
95
}
97
>
public toggleLogging(): boolean {
98
this._logging = !this._logging;
99
return this._logging;
100
}
102
>
protected _log(str: string): void {
103
if (this._logging) {
104
this._logService.info(`[KeybindingService]: ${str}`);
105
}
106
}
108
>
public getDefaultKeybindings(): readonly ResolvedKeybindingItem[] {
109
return this._getResolver().getDefaultKeybindings();
110
}
112
>
public getKeybindings(): readonly ResolvedKeybindingItem[] {
113
return this._getResolver().getKeybindings();
114
}
116
>
public customKeybindingsCount(): number {
117
return 0;
118
}
120
>
public lookupKeybindings(commandId: string): ResolvedKeybinding[] {
121
return arrays.coalesce(
122
this._getResolver().lookupKeybindings(commandId).map(item => item.resolvedKeybinding)
123
);
124
}
126
>
public lookupKeybinding(commandId: string, context?: IContextKeyService, enforceContextCheck = false): ResolvedKeybinding | undefined {
127
const result = this._getResolver().lookupPrimaryKeybinding(commandId, context || this._contextKeyService, enforceContextCheck);
128
if (!result) {