"Fossies" - the Fresh Open Source Software Archive

Member "vscode-1.49.1/src/vs/platform/keybinding/common/abstractKeybindingService.ts" (16 Sep 2020, 9054 Bytes) of package /linux/misc/vscode-1.49.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "abstractKeybindingService.ts": 1.48.2_vs_1.49.0.

    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 * as nls from 'vs/nls';
    7 import * as arrays from 'vs/base/common/arrays';
    8 import { IntervalTimer } from 'vs/base/common/async';
    9 import { Emitter, Event } from 'vs/base/common/event';
   10 import { KeyCode, Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes';
   11 import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
   12 import { ICommandService } from 'vs/platform/commands/common/commands';
   13 import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey';
   14 import { IKeybindingEvent, IKeybindingService, IKeyboardEvent, KeybindingsSchemaContribution } from 'vs/platform/keybinding/common/keybinding';
   15 import { IResolveResult, KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
   16 import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
   17 import { INotificationService } from 'vs/platform/notification/common/notification';
   18 import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
   19 import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
   20 import { ILogService } from 'vs/platform/log/common/log';
   21 
   22 interface CurrentChord {
   23     keypress: string;
   24     label: string | null;
   25 }
   26 
   27 export abstract class AbstractKeybindingService extends Disposable implements IKeybindingService {
   28     public _serviceBrand: undefined;
   29 
   30     protected readonly _onDidUpdateKeybindings: Emitter<IKeybindingEvent> = this._register(new Emitter<IKeybindingEvent>());
   31     get onDidUpdateKeybindings(): Event<IKeybindingEvent> {
   32         return this._onDidUpdateKeybindings ? this._onDidUpdateKeybindings.event : Event.None; // Sinon stubbing walks properties on prototype
   33     }
   34 
   35     private _currentChord: CurrentChord | null;
   36     private _currentChordChecker: IntervalTimer;
   37     private _currentChordStatusMessage: IDisposable | null;
   38     protected _logging: boolean;
   39 
   40     public get inChordMode(): boolean {
   41         return !!this._currentChord;
   42     }
   43 
   44     constructor(
   45         private _contextKeyService: IContextKeyService,
   46         protected _commandService: ICommandService,
   47         protected _telemetryService: ITelemetryService,
   48         private _notificationService: INotificationService,
   49         protected _logService: ILogService,
   50     ) {
   51         super();
   52 
   53         this._currentChord = null;
   54         this._currentChordChecker = new IntervalTimer();
   55         this._currentChordStatusMessage = null;
   56         this._logging = false;
   57     }
   58 
   59     public dispose(): void {
   60         super.dispose();
   61     }
   62 
   63     protected abstract _getResolver(): KeybindingResolver;
   64     protected abstract _documentHasFocus(): boolean;
   65     public abstract resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
   66     public abstract resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
   67     public abstract resolveUserBinding(userBinding: string): ResolvedKeybinding[];
   68     public abstract registerSchemaContribution(contribution: KeybindingsSchemaContribution): void;
   69     public abstract _dumpDebugInfo(): string;
   70     public abstract _dumpDebugInfoJSON(): string;
   71 
   72     public getDefaultKeybindingsContent(): string {
   73         return '';
   74     }
   75 
   76     public toggleLogging(): boolean {
   77         this._logging = !this._logging;
   78         return this._logging;
   79     }
   80 
   81     protected _log(str: string): void {
   82         if (this._logging) {
   83             this._logService.info(`[KeybindingService]: ${str}`);
   84         }
   85     }
   86 
   87     public getDefaultKeybindings(): readonly ResolvedKeybindingItem[] {
   88         return this._getResolver().getDefaultKeybindings();
   89     }
   90 
   91     public getKeybindings(): readonly ResolvedKeybindingItem[] {
   92         return this._getResolver().getKeybindings();
   93     }
   94 
   95     public customKeybindingsCount(): number {
   96         return 0;
   97     }
   98 
   99     public lookupKeybindings(commandId: string): ResolvedKeybinding[] {
  100         return arrays.coalesce(
  101             this._getResolver().lookupKeybindings(commandId).map(item => item.resolvedKeybinding)
  102         );
  103     }
  104 
  105     public lookupKeybinding(commandId: string): ResolvedKeybinding | undefined {
  106         const result = this._getResolver().lookupPrimaryKeybinding(commandId);
  107         if (!result) {
  108             return undefined;
  109         }
  110         return result.resolvedKeybinding;
  111     }
  112 
  113     public dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean {
  114         return this._dispatch(e, target);
  115     }
  116 
  117     public softDispatch(e: IKeyboardEvent, target: IContextKeyServiceTarget): IResolveResult | null {
  118         const keybinding = this.resolveKeyboardEvent(e);
  119         if (keybinding.isChord()) {
  120             console.warn('Unexpected keyboard event mapped to a chord');
  121             return null;
  122         }
  123         const [firstPart,] = keybinding.getDispatchParts();
  124         if (firstPart === null) {
  125             // cannot be dispatched, probably only modifier keys
  126             return null;
  127         }
  128 
  129         const contextValue = this._contextKeyService.getContext(target);
  130         const currentChord = this._currentChord ? this._currentChord.keypress : null;
  131         return this._getResolver().resolve(contextValue, currentChord, firstPart);
  132     }
  133 
  134     private _enterChordMode(firstPart: string, keypressLabel: string | null): void {
  135         this._currentChord = {
  136             keypress: firstPart,
  137             label: keypressLabel
  138         };
  139         this._currentChordStatusMessage = this._notificationService.status(nls.localize('first.chord', "({0}) was pressed. Waiting for second key of chord...", keypressLabel));
  140         const chordEnterTime = Date.now();
  141         this._currentChordChecker.cancelAndSet(() => {
  142 
  143             if (!this._documentHasFocus()) {
  144                 // Focus has been lost => leave chord mode
  145                 this._leaveChordMode();
  146                 return;
  147             }
  148 
  149             if (Date.now() - chordEnterTime > 5000) {
  150                 // 5 seconds elapsed => leave chord mode
  151                 this._leaveChordMode();
  152             }
  153 
  154         }, 500);
  155     }
  156 
  157     private _leaveChordMode(): void {
  158         if (this._currentChordStatusMessage) {
  159             this._currentChordStatusMessage.dispose();
  160             this._currentChordStatusMessage = null;
  161         }
  162         this._currentChordChecker.cancel();
  163         this._currentChord = null;
  164     }
  165 
  166     public dispatchByUserSettingsLabel(userSettingsLabel: string, target: IContextKeyServiceTarget): void {
  167         const keybindings = this.resolveUserBinding(userSettingsLabel);
  168         if (keybindings.length >= 1) {
  169             this._doDispatch(keybindings[0], target);
  170         }
  171     }
  172 
  173     protected _dispatch(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean {
  174         return this._doDispatch(this.resolveKeyboardEvent(e), target);
  175     }
  176 
  177     private _doDispatch(keybinding: ResolvedKeybinding, target: IContextKeyServiceTarget): boolean {
  178         let shouldPreventDefault = false;
  179 
  180         if (keybinding.isChord()) {
  181             console.warn('Unexpected keyboard event mapped to a chord');
  182             return false;
  183         }
  184         const [firstPart,] = keybinding.getDispatchParts();
  185         if (firstPart === null) {
  186             this._log(`\\ Keyboard event cannot be dispatched.`);
  187             // cannot be dispatched, probably only modifier keys
  188             return shouldPreventDefault;
  189         }
  190 
  191         const contextValue = this._contextKeyService.getContext(target);
  192         const currentChord = this._currentChord ? this._currentChord.keypress : null;
  193         const keypressLabel = keybinding.getLabel();
  194         const resolveResult = this._getResolver().resolve(contextValue, currentChord, firstPart);
  195 
  196         this._logService.trace('KeybindingService#dispatch', keypressLabel, resolveResult?.commandId);
  197 
  198         if (resolveResult && resolveResult.enterChord) {
  199             shouldPreventDefault = true;
  200             this._enterChordMode(firstPart, keypressLabel);
  201             return shouldPreventDefault;
  202         }
  203 
  204         if (this._currentChord) {
  205             if (!resolveResult || !resolveResult.commandId) {
  206                 this._notificationService.status(nls.localize('missing.chord', "The key combination ({0}, {1}) is not a command.", this._currentChord.label, keypressLabel), { hideAfter: 10 * 1000 /* 10s */ });
  207                 shouldPreventDefault = true;
  208             }
  209         }
  210 
  211         this._leaveChordMode();
  212 
  213         if (resolveResult && resolveResult.commandId) {
  214             if (!resolveResult.bubble) {
  215                 shouldPreventDefault = true;
  216             }
  217             if (typeof resolveResult.commandArgs === 'undefined') {
  218                 this._commandService.executeCommand(resolveResult.commandId).then(undefined, err => this._notificationService.warn(err));
  219             } else {
  220                 this._commandService.executeCommand(resolveResult.commandId, resolveResult.commandArgs).then(undefined, err => this._notificationService.warn(err));
  221             }
  222             this._telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: resolveResult.commandId, from: 'keybinding' });
  223         }
  224 
  225         return shouldPreventDefault;
  226     }
  227 
  228     mightProducePrintableCharacter(event: IKeyboardEvent): boolean {
  229         if (event.ctrlKey || event.metaKey) {
  230             // ignore ctrl/cmd-combination but not shift/alt-combinatios
  231             return false;
  232         }
  233         // weak check for certain ranges. this is properly implemented in a subclass
  234         // with access to the KeyboardMapperFactory.
  235         if ((event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z)
  236             || (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9)) {
  237             return true;
  238         }
  239         return false;
  240     }
  241 }