mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 10:38:31 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
265
src/vs/workbench/services/keybinding/common/keybindingEditing.ts
Normal file
265
src/vs/workbench/services/keybinding/common/keybindingEditing.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isArray } from 'vs/base/common/types';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { IReference, Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as json from 'vs/base/common/json';
|
||||
import { Edit } from 'vs/base/common/jsonFormatter';
|
||||
import { setProperty } from 'vs/base/common/jsonEdit';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
|
||||
|
||||
export const IKeybindingEditingService = createDecorator<IKeybindingEditingService>('keybindingEditingService');
|
||||
|
||||
export interface IKeybindingEditingService {
|
||||
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise<void>;
|
||||
|
||||
removeKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise<void>;
|
||||
|
||||
resetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise<void>;
|
||||
}
|
||||
|
||||
export class KeybindingsEditingService extends Disposable implements IKeybindingEditingService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
private queue: Queue<void>;
|
||||
|
||||
private resource: URI = URI.file(this.environmentService.appKeybindingsPath);
|
||||
|
||||
constructor(
|
||||
@ITextModelService private textModelResolverService: ITextModelService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
) {
|
||||
super();
|
||||
this.queue = new Queue<void>();
|
||||
}
|
||||
|
||||
editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise<void> {
|
||||
return this.queue.queue(() => this.doEditKeybinding(key, keybindingItem)); // queue up writes to prevent race conditions
|
||||
}
|
||||
|
||||
resetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise<void> {
|
||||
return this.queue.queue(() => this.doResetKeybinding(keybindingItem)); // queue up writes to prevent race conditions
|
||||
}
|
||||
|
||||
removeKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise<void> {
|
||||
return this.queue.queue(() => this.doRemoveKeybinding(keybindingItem)); // queue up writes to prevent race conditions
|
||||
}
|
||||
|
||||
private doEditKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise<void> {
|
||||
return this.resolveAndValidate()
|
||||
.then(reference => {
|
||||
const model = reference.object.textEditorModel;
|
||||
if (keybindingItem.isDefault) {
|
||||
this.updateDefaultKeybinding(key, keybindingItem, model);
|
||||
} else {
|
||||
this.updateUserKeybinding(key, keybindingItem, model);
|
||||
}
|
||||
return this.save().then(() => reference.dispose());
|
||||
});
|
||||
}
|
||||
|
||||
private doRemoveKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise<void> {
|
||||
return this.resolveAndValidate()
|
||||
.then(reference => {
|
||||
const model = reference.object.textEditorModel;
|
||||
if (keybindingItem.isDefault) {
|
||||
this.removeDefaultKeybinding(keybindingItem, model);
|
||||
} else {
|
||||
this.removeUserKeybinding(keybindingItem, model);
|
||||
}
|
||||
return this.save().then(() => reference.dispose());
|
||||
});
|
||||
}
|
||||
|
||||
private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise<void> {
|
||||
return this.resolveAndValidate()
|
||||
.then(reference => {
|
||||
const model = reference.object.textEditorModel;
|
||||
if (!keybindingItem.isDefault) {
|
||||
this.removeUserKeybinding(keybindingItem, model);
|
||||
this.removeUnassignedDefaultKeybinding(keybindingItem, model);
|
||||
}
|
||||
return this.save().then(() => reference.dispose());
|
||||
});
|
||||
}
|
||||
|
||||
private save(): TPromise<any> {
|
||||
return this.textFileService.save(this.resource);
|
||||
}
|
||||
|
||||
private updateUserKeybinding(newKey: string, keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
|
||||
const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries);
|
||||
if (userKeybindingEntryIndex !== -1) {
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex, 'key'], newKey, { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
}
|
||||
|
||||
private updateDefaultKeybinding(newKey: string, keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
|
||||
const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries);
|
||||
if (userKeybindingEntryIndex !== -1) {
|
||||
// Update the keybinding with new key
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex, 'key'], newKey, { tabSize, insertSpaces, eol })[0], model);
|
||||
} else {
|
||||
// Add the new keybinidng with new key
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(newKey, keybindingItem.command, keybindingItem.when, false), { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
if (keybindingItem.resolvedKeybinding) {
|
||||
// Unassign the default keybinding
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(keybindingItem.resolvedKeybinding.getUserSettingsLabel(), keybindingItem.command, keybindingItem.when, true), { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
}
|
||||
|
||||
private removeUserKeybinding(keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
|
||||
const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries);
|
||||
if (userKeybindingEntryIndex !== -1) {
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex], void 0, { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
}
|
||||
|
||||
private removeDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(keybindingItem.resolvedKeybinding.getUserSettingsLabel(), keybindingItem.command, keybindingItem.when, true), { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
|
||||
private removeUnassignedDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
|
||||
const index = this.findUnassignedDefaultKeybindingEntryIndex(keybindingItem, userKeybindingEntries);
|
||||
if (index !== -1) {
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [index], void 0, { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
}
|
||||
|
||||
private findUserKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number {
|
||||
for (let index = 0; index < userKeybindingEntries.length; index++) {
|
||||
const keybinding = userKeybindingEntries[index];
|
||||
if (keybinding.command === keybindingItem.command) {
|
||||
if (!keybinding.when && !keybindingItem.when) {
|
||||
return index;
|
||||
}
|
||||
if (keybinding.when && keybindingItem.when) {
|
||||
if (ContextKeyExpr.deserialize(keybinding.when).serialize() === keybindingItem.when.serialize()) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private findUnassignedDefaultKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number {
|
||||
for (let index = 0; index < userKeybindingEntries.length; index++) {
|
||||
if (userKeybindingEntries[index].command === `-${keybindingItem.command}`) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private asObject(key: string, command: string, when: ContextKeyExpr, negate: boolean): any {
|
||||
const object = { key };
|
||||
object['command'] = negate ? `-${command}` : command;
|
||||
if (when) {
|
||||
object['when'] = when.serialize();
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
private applyEditsToBuffer(edit: Edit, model: editorCommon.IModel): void {
|
||||
const startPosition = model.getPositionAt(edit.offset);
|
||||
const endPosition = model.getPositionAt(edit.offset + edit.length);
|
||||
const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
|
||||
let currentText = model.getValueInRange(range);
|
||||
const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content);
|
||||
model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []);
|
||||
}
|
||||
|
||||
|
||||
private resolveModelReference(): TPromise<IReference<ITextEditorModel>> {
|
||||
return this.fileService.existsFile(this.resource)
|
||||
.then(exists => {
|
||||
const EOL = this.configurationService.getConfiguration('files', { overrideIdentifier: 'json' })['eol'];
|
||||
const result = exists ? TPromise.as(null) : this.fileService.updateContent(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' });
|
||||
return result.then(() => this.textModelResolverService.createModelReference(this.resource));
|
||||
});
|
||||
}
|
||||
|
||||
private resolveAndValidate(): TPromise<IReference<ITextEditorModel>> {
|
||||
|
||||
// Target cannot be dirty if not writing into buffer
|
||||
if (this.textFileService.isDirty(this.resource)) {
|
||||
return TPromise.wrapError<IReference<ITextEditorModel>>(new Error(localize('errorKeybindingsFileDirty', "Unable to write because the file is dirty. Please save the **Keybindings** file and try again.")));
|
||||
}
|
||||
|
||||
return this.resolveModelReference()
|
||||
.then(reference => {
|
||||
const model = reference.object.textEditorModel;
|
||||
const EOL = model.getEOL();
|
||||
if (model.getValue()) {
|
||||
const parsed = this.parse(model);
|
||||
if (parsed.parseErrors.length) {
|
||||
return TPromise.wrapError<IReference<ITextEditorModel>>(new Error(localize('parseErrors', "Unable to write keybindings. Please open **Keybindings file** to correct errors/warnings in the file and try again.")));
|
||||
}
|
||||
if (parsed.result) {
|
||||
if (!isArray(parsed.result)) {
|
||||
return TPromise.wrapError<IReference<ITextEditorModel>>(new Error(localize('errorInvalidConfiguration', "Unable to write keybindings. **Keybindings file** has an object which is not of type Array. Please open the file to clean up and try again.")));
|
||||
}
|
||||
} else {
|
||||
const content = EOL + '[]';
|
||||
this.applyEditsToBuffer({ content, length: content.length, offset: model.getValue().length }, model);
|
||||
}
|
||||
} else {
|
||||
const content = this.getEmptyContent(EOL);
|
||||
this.applyEditsToBuffer({ content, length: content.length, offset: 0 }, model);
|
||||
}
|
||||
return reference;
|
||||
});
|
||||
}
|
||||
|
||||
private parse(model: editorCommon.IModel): { result: IUserFriendlyKeybinding[], parseErrors: json.ParseError[] } {
|
||||
const parseErrors: json.ParseError[] = [];
|
||||
const result = json.parse(model.getValue(), parseErrors, { allowTrailingComma: true });
|
||||
return { result, parseErrors };
|
||||
}
|
||||
|
||||
private getEmptyContent(EOL: string): string {
|
||||
return '// ' + localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + EOL + '[]';
|
||||
}
|
||||
}
|
||||
194
src/vs/workbench/services/keybinding/common/keybindingIO.ts
Normal file
194
src/vs/workbench/services/keybinding/common/keybindingIO.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Keybinding, SimpleKeybinding, ChordKeybinding, KeyCodeUtils } from 'vs/base/common/keyCodes';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
import { ScanCodeBinding, ScanCodeUtils } from 'vs/workbench/services/keybinding/common/scanCode';
|
||||
|
||||
export interface IUserKeybindingItem {
|
||||
firstPart: SimpleKeybinding | ScanCodeBinding;
|
||||
chordPart: SimpleKeybinding | ScanCodeBinding;
|
||||
command: string;
|
||||
commandArgs?: any;
|
||||
when: ContextKeyExpr;
|
||||
}
|
||||
|
||||
export class KeybindingIO {
|
||||
|
||||
public static writeKeybindingItem(out: OutputBuilder, item: ResolvedKeybindingItem, OS: OperatingSystem): void {
|
||||
let quotedSerializedKeybinding = JSON.stringify(item.resolvedKeybinding.getUserSettingsLabel());
|
||||
out.write(`{ "key": ${rightPaddedString(quotedSerializedKeybinding + ',', 25)} "command": `);
|
||||
|
||||
let serializedWhen = item.when ? item.when.serialize() : '';
|
||||
let quotedSerializeCommand = JSON.stringify(item.command);
|
||||
if (serializedWhen.length > 0) {
|
||||
out.write(`${quotedSerializeCommand},`);
|
||||
out.writeLine();
|
||||
out.write(` "when": "${serializedWhen}" `);
|
||||
} else {
|
||||
out.write(`${quotedSerializeCommand} `);
|
||||
}
|
||||
// out.write(String(item.weight1 + '-' + item.weight2));
|
||||
out.write('}');
|
||||
}
|
||||
|
||||
public static readUserKeybindingItem(input: IUserFriendlyKeybinding, OS: OperatingSystem): IUserKeybindingItem {
|
||||
const [firstPart, chordPart] = (typeof input.key === 'string' ? this._readUserBinding(input.key) : [null, null]);
|
||||
const when = (typeof input.when === 'string' ? ContextKeyExpr.deserialize(input.when) : null);
|
||||
const command = (typeof input.command === 'string' ? input.command : null);
|
||||
const commandArgs = (typeof input.args !== 'undefined' ? input.args : null);
|
||||
return {
|
||||
firstPart: firstPart,
|
||||
chordPart: chordPart,
|
||||
command: command,
|
||||
commandArgs: commandArgs,
|
||||
when: when
|
||||
};
|
||||
}
|
||||
|
||||
private static _readModifiers(input: string) {
|
||||
input = input.toLowerCase().trim();
|
||||
|
||||
let ctrl = false;
|
||||
let shift = false;
|
||||
let alt = false;
|
||||
let meta = false;
|
||||
|
||||
let matchedModifier: boolean;
|
||||
|
||||
do {
|
||||
matchedModifier = false;
|
||||
if (/^ctrl(\+|\-)/.test(input)) {
|
||||
ctrl = true;
|
||||
input = input.substr('ctrl-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^shift(\+|\-)/.test(input)) {
|
||||
shift = true;
|
||||
input = input.substr('shift-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^alt(\+|\-)/.test(input)) {
|
||||
alt = true;
|
||||
input = input.substr('alt-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^meta(\+|\-)/.test(input)) {
|
||||
meta = true;
|
||||
input = input.substr('meta-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^win(\+|\-)/.test(input)) {
|
||||
meta = true;
|
||||
input = input.substr('win-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^cmd(\+|\-)/.test(input)) {
|
||||
meta = true;
|
||||
input = input.substr('cmd-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
} while (matchedModifier);
|
||||
|
||||
let key: string;
|
||||
|
||||
const firstSpaceIdx = input.indexOf(' ');
|
||||
if (firstSpaceIdx > 0) {
|
||||
key = input.substring(0, firstSpaceIdx);
|
||||
input = input.substring(firstSpaceIdx);
|
||||
} else {
|
||||
key = input;
|
||||
input = '';
|
||||
}
|
||||
|
||||
return {
|
||||
remains: input,
|
||||
ctrl,
|
||||
shift,
|
||||
alt,
|
||||
meta,
|
||||
key
|
||||
};
|
||||
}
|
||||
|
||||
private static _readSimpleKeybinding(input: string): [SimpleKeybinding, string] {
|
||||
const mods = this._readModifiers(input);
|
||||
const keyCode = KeyCodeUtils.fromUserSettings(mods.key);
|
||||
return [new SimpleKeybinding(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains];
|
||||
}
|
||||
|
||||
public static readKeybinding(input: string, OS: OperatingSystem): Keybinding {
|
||||
if (!input) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let [firstPart, remains] = this._readSimpleKeybinding(input);
|
||||
let chordPart: SimpleKeybinding = null;
|
||||
if (remains.length > 0) {
|
||||
[chordPart] = this._readSimpleKeybinding(remains);
|
||||
}
|
||||
|
||||
if (chordPart) {
|
||||
return new ChordKeybinding(firstPart, chordPart);
|
||||
}
|
||||
return firstPart;
|
||||
}
|
||||
|
||||
private static _readSimpleUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding, string] {
|
||||
const mods = this._readModifiers(input);
|
||||
const scanCodeMatch = mods.key.match(/^\[([^\]]+)\]$/);
|
||||
if (scanCodeMatch) {
|
||||
const strScanCode = scanCodeMatch[1];
|
||||
const scanCode = ScanCodeUtils.lowerCaseToEnum(strScanCode);
|
||||
return [new ScanCodeBinding(mods.ctrl, mods.shift, mods.alt, mods.meta, scanCode), mods.remains];
|
||||
}
|
||||
const keyCode = KeyCodeUtils.fromUserSettings(mods.key);
|
||||
return [new SimpleKeybinding(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains];
|
||||
}
|
||||
|
||||
static _readUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding, SimpleKeybinding | ScanCodeBinding] {
|
||||
if (!input) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
let [firstPart, remains] = this._readSimpleUserBinding(input);
|
||||
let chordPart: SimpleKeybinding | ScanCodeBinding = null;
|
||||
if (remains.length > 0) {
|
||||
[chordPart] = this._readSimpleUserBinding(remains);
|
||||
}
|
||||
return [firstPart, chordPart];
|
||||
}
|
||||
}
|
||||
|
||||
function rightPaddedString(str: string, minChars: number): string {
|
||||
if (str.length < minChars) {
|
||||
return str + (new Array(minChars - str.length).join(' '));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export class OutputBuilder {
|
||||
|
||||
private _lines: string[] = [];
|
||||
private _currentLine: string = '';
|
||||
|
||||
write(str: string): void {
|
||||
this._currentLine += str;
|
||||
}
|
||||
|
||||
writeLine(str: string = ''): void {
|
||||
this._lines.push(this._currentLine + str);
|
||||
this._currentLine = '';
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
this.writeLine();
|
||||
return this._lines.join('\n');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ScanCodeBinding } from 'vs/workbench/services/keybinding/common/scanCode';
|
||||
|
||||
export interface IKeyboardMapper {
|
||||
dumpDebugInfo(): string;
|
||||
resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
|
||||
resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
|
||||
resolveUserBinding(firstPart: SimpleKeybinding | ScanCodeBinding, chordPart: SimpleKeybinding | ScanCodeBinding): ResolvedKeybinding[];
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { ResolvedKeybinding, SimpleKeybinding, Keybinding, KeyCode, ChordKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
|
||||
import { ScanCodeBinding, ScanCode, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/workbench/services/keybinding/common/scanCode';
|
||||
|
||||
export interface IMacLinuxKeyMapping {
|
||||
value: string;
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
|
||||
valueIsDeadKey?: boolean;
|
||||
withShiftIsDeadKey?: boolean;
|
||||
withAltGrIsDeadKey?: boolean;
|
||||
withShiftAltGrIsDeadKey?: boolean;
|
||||
}
|
||||
|
||||
export interface IMacLinuxKeyboardMapping {
|
||||
[scanCode: string]: IMacLinuxKeyMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* A keyboard mapper to be used when reading the keymap from the OS fails.
|
||||
*/
|
||||
export class MacLinuxFallbackKeyboardMapper implements IKeyboardMapper {
|
||||
|
||||
/**
|
||||
* OS (can be Linux or Macintosh)
|
||||
*/
|
||||
private readonly _OS: OperatingSystem;
|
||||
|
||||
constructor(OS: OperatingSystem) {
|
||||
this._OS = OS;
|
||||
}
|
||||
|
||||
public dumpDebugInfo(): string {
|
||||
return 'FallbackKeyboardMapper dispatching on keyCode';
|
||||
}
|
||||
|
||||
public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] {
|
||||
return [new USLayoutResolvedKeybinding(keybinding, this._OS)];
|
||||
}
|
||||
|
||||
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
|
||||
let keybinding = new SimpleKeybinding(
|
||||
keyboardEvent.ctrlKey,
|
||||
keyboardEvent.shiftKey,
|
||||
keyboardEvent.altKey,
|
||||
keyboardEvent.metaKey,
|
||||
keyboardEvent.keyCode
|
||||
);
|
||||
return new USLayoutResolvedKeybinding(keybinding, this._OS);
|
||||
}
|
||||
|
||||
private _scanCodeToKeyCode(scanCode: ScanCode): KeyCode {
|
||||
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode];
|
||||
if (immutableKeyCode !== -1) {
|
||||
return immutableKeyCode;
|
||||
}
|
||||
|
||||
switch (scanCode) {
|
||||
case ScanCode.KeyA: return KeyCode.KEY_A;
|
||||
case ScanCode.KeyB: return KeyCode.KEY_B;
|
||||
case ScanCode.KeyC: return KeyCode.KEY_C;
|
||||
case ScanCode.KeyD: return KeyCode.KEY_D;
|
||||
case ScanCode.KeyE: return KeyCode.KEY_E;
|
||||
case ScanCode.KeyF: return KeyCode.KEY_F;
|
||||
case ScanCode.KeyG: return KeyCode.KEY_G;
|
||||
case ScanCode.KeyH: return KeyCode.KEY_H;
|
||||
case ScanCode.KeyI: return KeyCode.KEY_I;
|
||||
case ScanCode.KeyJ: return KeyCode.KEY_J;
|
||||
case ScanCode.KeyK: return KeyCode.KEY_K;
|
||||
case ScanCode.KeyL: return KeyCode.KEY_L;
|
||||
case ScanCode.KeyM: return KeyCode.KEY_M;
|
||||
case ScanCode.KeyN: return KeyCode.KEY_N;
|
||||
case ScanCode.KeyO: return KeyCode.KEY_O;
|
||||
case ScanCode.KeyP: return KeyCode.KEY_P;
|
||||
case ScanCode.KeyQ: return KeyCode.KEY_Q;
|
||||
case ScanCode.KeyR: return KeyCode.KEY_R;
|
||||
case ScanCode.KeyS: return KeyCode.KEY_S;
|
||||
case ScanCode.KeyT: return KeyCode.KEY_T;
|
||||
case ScanCode.KeyU: return KeyCode.KEY_U;
|
||||
case ScanCode.KeyV: return KeyCode.KEY_V;
|
||||
case ScanCode.KeyW: return KeyCode.KEY_W;
|
||||
case ScanCode.KeyX: return KeyCode.KEY_X;
|
||||
case ScanCode.KeyY: return KeyCode.KEY_Y;
|
||||
case ScanCode.KeyZ: return KeyCode.KEY_Z;
|
||||
case ScanCode.Digit1: return KeyCode.KEY_1;
|
||||
case ScanCode.Digit2: return KeyCode.KEY_2;
|
||||
case ScanCode.Digit3: return KeyCode.KEY_3;
|
||||
case ScanCode.Digit4: return KeyCode.KEY_4;
|
||||
case ScanCode.Digit5: return KeyCode.KEY_5;
|
||||
case ScanCode.Digit6: return KeyCode.KEY_6;
|
||||
case ScanCode.Digit7: return KeyCode.KEY_7;
|
||||
case ScanCode.Digit8: return KeyCode.KEY_8;
|
||||
case ScanCode.Digit9: return KeyCode.KEY_9;
|
||||
case ScanCode.Digit0: return KeyCode.KEY_0;
|
||||
case ScanCode.Minus: return KeyCode.US_MINUS;
|
||||
case ScanCode.Equal: return KeyCode.US_EQUAL;
|
||||
case ScanCode.BracketLeft: return KeyCode.US_OPEN_SQUARE_BRACKET;
|
||||
case ScanCode.BracketRight: return KeyCode.US_CLOSE_SQUARE_BRACKET;
|
||||
case ScanCode.Backslash: return KeyCode.US_BACKSLASH;
|
||||
case ScanCode.IntlHash: return KeyCode.Unknown; // missing
|
||||
case ScanCode.Semicolon: return KeyCode.US_SEMICOLON;
|
||||
case ScanCode.Quote: return KeyCode.US_QUOTE;
|
||||
case ScanCode.Backquote: return KeyCode.US_BACKTICK;
|
||||
case ScanCode.Comma: return KeyCode.US_COMMA;
|
||||
case ScanCode.Period: return KeyCode.US_DOT;
|
||||
case ScanCode.Slash: return KeyCode.US_SLASH;
|
||||
case ScanCode.IntlBackslash: return KeyCode.OEM_102;
|
||||
}
|
||||
return KeyCode.Unknown;
|
||||
}
|
||||
|
||||
private _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding): SimpleKeybinding {
|
||||
if (!binding) {
|
||||
return null;
|
||||
}
|
||||
if (binding instanceof SimpleKeybinding) {
|
||||
return binding;
|
||||
}
|
||||
const keyCode = this._scanCodeToKeyCode(binding.scanCode);
|
||||
if (keyCode === KeyCode.Unknown) {
|
||||
return null;
|
||||
}
|
||||
return new SimpleKeybinding(binding.ctrlKey, binding.shiftKey, binding.altKey, binding.metaKey, keyCode);
|
||||
}
|
||||
|
||||
public resolveUserBinding(firstPart: SimpleKeybinding | ScanCodeBinding, chordPart: SimpleKeybinding | ScanCodeBinding): ResolvedKeybinding[] {
|
||||
const _firstPart = this._resolveSimpleUserBinding(firstPart);
|
||||
const _chordPart = this._resolveSimpleUserBinding(chordPart);
|
||||
if (_firstPart && _chordPart) {
|
||||
return [new USLayoutResolvedKeybinding(new ChordKeybinding(_firstPart, _chordPart), this._OS)];
|
||||
}
|
||||
if (_firstPart) {
|
||||
return [new USLayoutResolvedKeybinding(_firstPart, this._OS)];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
691
src/vs/workbench/services/keybinding/common/scanCode.ts
Normal file
691
src/vs/workbench/services/keybinding/common/scanCode.ts
Normal file
@@ -0,0 +1,691 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
/**
|
||||
* keyboardEvent.code
|
||||
*/
|
||||
export const enum ScanCode {
|
||||
None,
|
||||
|
||||
Hyper,
|
||||
Super,
|
||||
Fn,
|
||||
FnLock,
|
||||
Suspend,
|
||||
Resume,
|
||||
Turbo,
|
||||
Sleep,
|
||||
WakeUp,
|
||||
KeyA,
|
||||
KeyB,
|
||||
KeyC,
|
||||
KeyD,
|
||||
KeyE,
|
||||
KeyF,
|
||||
KeyG,
|
||||
KeyH,
|
||||
KeyI,
|
||||
KeyJ,
|
||||
KeyK,
|
||||
KeyL,
|
||||
KeyM,
|
||||
KeyN,
|
||||
KeyO,
|
||||
KeyP,
|
||||
KeyQ,
|
||||
KeyR,
|
||||
KeyS,
|
||||
KeyT,
|
||||
KeyU,
|
||||
KeyV,
|
||||
KeyW,
|
||||
KeyX,
|
||||
KeyY,
|
||||
KeyZ,
|
||||
Digit1,
|
||||
Digit2,
|
||||
Digit3,
|
||||
Digit4,
|
||||
Digit5,
|
||||
Digit6,
|
||||
Digit7,
|
||||
Digit8,
|
||||
Digit9,
|
||||
Digit0,
|
||||
Enter,
|
||||
Escape,
|
||||
Backspace,
|
||||
Tab,
|
||||
Space,
|
||||
Minus,
|
||||
Equal,
|
||||
BracketLeft,
|
||||
BracketRight,
|
||||
Backslash,
|
||||
IntlHash,
|
||||
Semicolon,
|
||||
Quote,
|
||||
Backquote,
|
||||
Comma,
|
||||
Period,
|
||||
Slash,
|
||||
CapsLock,
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
PrintScreen,
|
||||
ScrollLock,
|
||||
Pause,
|
||||
Insert,
|
||||
Home,
|
||||
PageUp,
|
||||
Delete,
|
||||
End,
|
||||
PageDown,
|
||||
ArrowRight,
|
||||
ArrowLeft,
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
NumLock,
|
||||
NumpadDivide,
|
||||
NumpadMultiply,
|
||||
NumpadSubtract,
|
||||
NumpadAdd,
|
||||
NumpadEnter,
|
||||
Numpad1,
|
||||
Numpad2,
|
||||
Numpad3,
|
||||
Numpad4,
|
||||
Numpad5,
|
||||
Numpad6,
|
||||
Numpad7,
|
||||
Numpad8,
|
||||
Numpad9,
|
||||
Numpad0,
|
||||
NumpadDecimal,
|
||||
IntlBackslash,
|
||||
ContextMenu,
|
||||
Power,
|
||||
NumpadEqual,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
Open,
|
||||
Help,
|
||||
Select,
|
||||
Again,
|
||||
Undo,
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
Find,
|
||||
AudioVolumeMute,
|
||||
AudioVolumeUp,
|
||||
AudioVolumeDown,
|
||||
NumpadComma,
|
||||
IntlRo,
|
||||
KanaMode,
|
||||
IntlYen,
|
||||
Convert,
|
||||
NonConvert,
|
||||
Lang1,
|
||||
Lang2,
|
||||
Lang3,
|
||||
Lang4,
|
||||
Lang5,
|
||||
Abort,
|
||||
Props,
|
||||
NumpadParenLeft,
|
||||
NumpadParenRight,
|
||||
NumpadBackspace,
|
||||
NumpadMemoryStore,
|
||||
NumpadMemoryRecall,
|
||||
NumpadMemoryClear,
|
||||
NumpadMemoryAdd,
|
||||
NumpadMemorySubtract,
|
||||
NumpadClear,
|
||||
NumpadClearEntry,
|
||||
ControlLeft,
|
||||
ShiftLeft,
|
||||
AltLeft,
|
||||
MetaLeft,
|
||||
ControlRight,
|
||||
ShiftRight,
|
||||
AltRight,
|
||||
MetaRight,
|
||||
BrightnessUp,
|
||||
BrightnessDown,
|
||||
MediaPlay,
|
||||
MediaRecord,
|
||||
MediaFastForward,
|
||||
MediaRewind,
|
||||
MediaTrackNext,
|
||||
MediaTrackPrevious,
|
||||
MediaStop,
|
||||
Eject,
|
||||
MediaPlayPause,
|
||||
MediaSelect,
|
||||
LaunchMail,
|
||||
LaunchApp2,
|
||||
LaunchApp1,
|
||||
SelectTask,
|
||||
LaunchScreenSaver,
|
||||
BrowserSearch,
|
||||
BrowserHome,
|
||||
BrowserBack,
|
||||
BrowserForward,
|
||||
BrowserStop,
|
||||
BrowserRefresh,
|
||||
BrowserFavorites,
|
||||
ZoomToggle,
|
||||
MailReply,
|
||||
MailForward,
|
||||
MailSend,
|
||||
|
||||
MAX_VALUE
|
||||
}
|
||||
|
||||
const scanCodeIntToStr: string[] = [];
|
||||
const scanCodeStrToInt: { [code: string]: number; } = Object.create(null);
|
||||
const scanCodeLowerCaseStrToInt: { [code: string]: number; } = Object.create(null);
|
||||
|
||||
export const ScanCodeUtils = {
|
||||
lowerCaseToEnum: (scanCode: string) => scanCodeLowerCaseStrToInt[scanCode] || ScanCode.None,
|
||||
toEnum: (scanCode: string) => scanCodeStrToInt[scanCode] || ScanCode.None,
|
||||
toString: (scanCode: ScanCode) => scanCodeIntToStr[scanCode] || 'None'
|
||||
};
|
||||
|
||||
/**
|
||||
* -1 if a ScanCode => KeyCode mapping depends on kb layout.
|
||||
*/
|
||||
export const IMMUTABLE_CODE_TO_KEY_CODE: KeyCode[] = [];
|
||||
|
||||
/**
|
||||
* -1 if a KeyCode => ScanCode mapping depends on kb layout.
|
||||
*/
|
||||
export const IMMUTABLE_KEY_CODE_TO_CODE: ScanCode[] = [];
|
||||
|
||||
export class ScanCodeBinding {
|
||||
public readonly ctrlKey: boolean;
|
||||
public readonly shiftKey: boolean;
|
||||
public readonly altKey: boolean;
|
||||
public readonly metaKey: boolean;
|
||||
public readonly scanCode: ScanCode;
|
||||
|
||||
constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, scanCode: ScanCode) {
|
||||
this.ctrlKey = ctrlKey;
|
||||
this.shiftKey = shiftKey;
|
||||
this.altKey = altKey;
|
||||
this.metaKey = metaKey;
|
||||
this.scanCode = scanCode;
|
||||
}
|
||||
|
||||
public equals(other: ScanCodeBinding): boolean {
|
||||
return (
|
||||
this.ctrlKey === other.ctrlKey
|
||||
&& this.shiftKey === other.shiftKey
|
||||
&& this.altKey === other.altKey
|
||||
&& this.metaKey === other.metaKey
|
||||
&& this.scanCode === other.scanCode
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this keybinding refer to the key code of a modifier and it also has the modifier flag?
|
||||
*/
|
||||
public isDuplicateModifierCase(): boolean {
|
||||
return (
|
||||
(this.ctrlKey && (this.scanCode === ScanCode.ControlLeft || this.scanCode === ScanCode.ControlRight))
|
||||
|| (this.shiftKey && (this.scanCode === ScanCode.ShiftLeft || this.scanCode === ScanCode.ShiftRight))
|
||||
|| (this.altKey && (this.scanCode === ScanCode.AltLeft || this.scanCode === ScanCode.AltRight))
|
||||
|| (this.metaKey && (this.scanCode === ScanCode.MetaLeft || this.scanCode === ScanCode.MetaRight))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(function () {
|
||||
function d(intScanCode: ScanCode, strScanCode: string): void {
|
||||
scanCodeIntToStr[intScanCode] = strScanCode;
|
||||
scanCodeStrToInt[strScanCode] = intScanCode;
|
||||
scanCodeLowerCaseStrToInt[strScanCode.toLowerCase()] = intScanCode;
|
||||
}
|
||||
d(ScanCode.None, 'None');
|
||||
d(ScanCode.Hyper, 'Hyper');
|
||||
d(ScanCode.Super, 'Super');
|
||||
d(ScanCode.Fn, 'Fn');
|
||||
d(ScanCode.FnLock, 'FnLock');
|
||||
d(ScanCode.Suspend, 'Suspend');
|
||||
d(ScanCode.Resume, 'Resume');
|
||||
d(ScanCode.Turbo, 'Turbo');
|
||||
d(ScanCode.Sleep, 'Sleep');
|
||||
d(ScanCode.WakeUp, 'WakeUp');
|
||||
d(ScanCode.KeyA, 'KeyA');
|
||||
d(ScanCode.KeyB, 'KeyB');
|
||||
d(ScanCode.KeyC, 'KeyC');
|
||||
d(ScanCode.KeyD, 'KeyD');
|
||||
d(ScanCode.KeyE, 'KeyE');
|
||||
d(ScanCode.KeyF, 'KeyF');
|
||||
d(ScanCode.KeyG, 'KeyG');
|
||||
d(ScanCode.KeyH, 'KeyH');
|
||||
d(ScanCode.KeyI, 'KeyI');
|
||||
d(ScanCode.KeyJ, 'KeyJ');
|
||||
d(ScanCode.KeyK, 'KeyK');
|
||||
d(ScanCode.KeyL, 'KeyL');
|
||||
d(ScanCode.KeyM, 'KeyM');
|
||||
d(ScanCode.KeyN, 'KeyN');
|
||||
d(ScanCode.KeyO, 'KeyO');
|
||||
d(ScanCode.KeyP, 'KeyP');
|
||||
d(ScanCode.KeyQ, 'KeyQ');
|
||||
d(ScanCode.KeyR, 'KeyR');
|
||||
d(ScanCode.KeyS, 'KeyS');
|
||||
d(ScanCode.KeyT, 'KeyT');
|
||||
d(ScanCode.KeyU, 'KeyU');
|
||||
d(ScanCode.KeyV, 'KeyV');
|
||||
d(ScanCode.KeyW, 'KeyW');
|
||||
d(ScanCode.KeyX, 'KeyX');
|
||||
d(ScanCode.KeyY, 'KeyY');
|
||||
d(ScanCode.KeyZ, 'KeyZ');
|
||||
d(ScanCode.Digit1, 'Digit1');
|
||||
d(ScanCode.Digit2, 'Digit2');
|
||||
d(ScanCode.Digit3, 'Digit3');
|
||||
d(ScanCode.Digit4, 'Digit4');
|
||||
d(ScanCode.Digit5, 'Digit5');
|
||||
d(ScanCode.Digit6, 'Digit6');
|
||||
d(ScanCode.Digit7, 'Digit7');
|
||||
d(ScanCode.Digit8, 'Digit8');
|
||||
d(ScanCode.Digit9, 'Digit9');
|
||||
d(ScanCode.Digit0, 'Digit0');
|
||||
d(ScanCode.Enter, 'Enter');
|
||||
d(ScanCode.Escape, 'Escape');
|
||||
d(ScanCode.Backspace, 'Backspace');
|
||||
d(ScanCode.Tab, 'Tab');
|
||||
d(ScanCode.Space, 'Space');
|
||||
d(ScanCode.Minus, 'Minus');
|
||||
d(ScanCode.Equal, 'Equal');
|
||||
d(ScanCode.BracketLeft, 'BracketLeft');
|
||||
d(ScanCode.BracketRight, 'BracketRight');
|
||||
d(ScanCode.Backslash, 'Backslash');
|
||||
d(ScanCode.IntlHash, 'IntlHash');
|
||||
d(ScanCode.Semicolon, 'Semicolon');
|
||||
d(ScanCode.Quote, 'Quote');
|
||||
d(ScanCode.Backquote, 'Backquote');
|
||||
d(ScanCode.Comma, 'Comma');
|
||||
d(ScanCode.Period, 'Period');
|
||||
d(ScanCode.Slash, 'Slash');
|
||||
d(ScanCode.CapsLock, 'CapsLock');
|
||||
d(ScanCode.F1, 'F1');
|
||||
d(ScanCode.F2, 'F2');
|
||||
d(ScanCode.F3, 'F3');
|
||||
d(ScanCode.F4, 'F4');
|
||||
d(ScanCode.F5, 'F5');
|
||||
d(ScanCode.F6, 'F6');
|
||||
d(ScanCode.F7, 'F7');
|
||||
d(ScanCode.F8, 'F8');
|
||||
d(ScanCode.F9, 'F9');
|
||||
d(ScanCode.F10, 'F10');
|
||||
d(ScanCode.F11, 'F11');
|
||||
d(ScanCode.F12, 'F12');
|
||||
d(ScanCode.PrintScreen, 'PrintScreen');
|
||||
d(ScanCode.ScrollLock, 'ScrollLock');
|
||||
d(ScanCode.Pause, 'Pause');
|
||||
d(ScanCode.Insert, 'Insert');
|
||||
d(ScanCode.Home, 'Home');
|
||||
d(ScanCode.PageUp, 'PageUp');
|
||||
d(ScanCode.Delete, 'Delete');
|
||||
d(ScanCode.End, 'End');
|
||||
d(ScanCode.PageDown, 'PageDown');
|
||||
d(ScanCode.ArrowRight, 'ArrowRight');
|
||||
d(ScanCode.ArrowLeft, 'ArrowLeft');
|
||||
d(ScanCode.ArrowDown, 'ArrowDown');
|
||||
d(ScanCode.ArrowUp, 'ArrowUp');
|
||||
d(ScanCode.NumLock, 'NumLock');
|
||||
d(ScanCode.NumpadDivide, 'NumpadDivide');
|
||||
d(ScanCode.NumpadMultiply, 'NumpadMultiply');
|
||||
d(ScanCode.NumpadSubtract, 'NumpadSubtract');
|
||||
d(ScanCode.NumpadAdd, 'NumpadAdd');
|
||||
d(ScanCode.NumpadEnter, 'NumpadEnter');
|
||||
d(ScanCode.Numpad1, 'Numpad1');
|
||||
d(ScanCode.Numpad2, 'Numpad2');
|
||||
d(ScanCode.Numpad3, 'Numpad3');
|
||||
d(ScanCode.Numpad4, 'Numpad4');
|
||||
d(ScanCode.Numpad5, 'Numpad5');
|
||||
d(ScanCode.Numpad6, 'Numpad6');
|
||||
d(ScanCode.Numpad7, 'Numpad7');
|
||||
d(ScanCode.Numpad8, 'Numpad8');
|
||||
d(ScanCode.Numpad9, 'Numpad9');
|
||||
d(ScanCode.Numpad0, 'Numpad0');
|
||||
d(ScanCode.NumpadDecimal, 'NumpadDecimal');
|
||||
d(ScanCode.IntlBackslash, 'IntlBackslash');
|
||||
d(ScanCode.ContextMenu, 'ContextMenu');
|
||||
d(ScanCode.Power, 'Power');
|
||||
d(ScanCode.NumpadEqual, 'NumpadEqual');
|
||||
d(ScanCode.F13, 'F13');
|
||||
d(ScanCode.F14, 'F14');
|
||||
d(ScanCode.F15, 'F15');
|
||||
d(ScanCode.F16, 'F16');
|
||||
d(ScanCode.F17, 'F17');
|
||||
d(ScanCode.F18, 'F18');
|
||||
d(ScanCode.F19, 'F19');
|
||||
d(ScanCode.F20, 'F20');
|
||||
d(ScanCode.F21, 'F21');
|
||||
d(ScanCode.F22, 'F22');
|
||||
d(ScanCode.F23, 'F23');
|
||||
d(ScanCode.F24, 'F24');
|
||||
d(ScanCode.Open, 'Open');
|
||||
d(ScanCode.Help, 'Help');
|
||||
d(ScanCode.Select, 'Select');
|
||||
d(ScanCode.Again, 'Again');
|
||||
d(ScanCode.Undo, 'Undo');
|
||||
d(ScanCode.Cut, 'Cut');
|
||||
d(ScanCode.Copy, 'Copy');
|
||||
d(ScanCode.Paste, 'Paste');
|
||||
d(ScanCode.Find, 'Find');
|
||||
d(ScanCode.AudioVolumeMute, 'AudioVolumeMute');
|
||||
d(ScanCode.AudioVolumeUp, 'AudioVolumeUp');
|
||||
d(ScanCode.AudioVolumeDown, 'AudioVolumeDown');
|
||||
d(ScanCode.NumpadComma, 'NumpadComma');
|
||||
d(ScanCode.IntlRo, 'IntlRo');
|
||||
d(ScanCode.KanaMode, 'KanaMode');
|
||||
d(ScanCode.IntlYen, 'IntlYen');
|
||||
d(ScanCode.Convert, 'Convert');
|
||||
d(ScanCode.NonConvert, 'NonConvert');
|
||||
d(ScanCode.Lang1, 'Lang1');
|
||||
d(ScanCode.Lang2, 'Lang2');
|
||||
d(ScanCode.Lang3, 'Lang3');
|
||||
d(ScanCode.Lang4, 'Lang4');
|
||||
d(ScanCode.Lang5, 'Lang5');
|
||||
d(ScanCode.Abort, 'Abort');
|
||||
d(ScanCode.Props, 'Props');
|
||||
d(ScanCode.NumpadParenLeft, 'NumpadParenLeft');
|
||||
d(ScanCode.NumpadParenRight, 'NumpadParenRight');
|
||||
d(ScanCode.NumpadBackspace, 'NumpadBackspace');
|
||||
d(ScanCode.NumpadMemoryStore, 'NumpadMemoryStore');
|
||||
d(ScanCode.NumpadMemoryRecall, 'NumpadMemoryRecall');
|
||||
d(ScanCode.NumpadMemoryClear, 'NumpadMemoryClear');
|
||||
d(ScanCode.NumpadMemoryAdd, 'NumpadMemoryAdd');
|
||||
d(ScanCode.NumpadMemorySubtract, 'NumpadMemorySubtract');
|
||||
d(ScanCode.NumpadClear, 'NumpadClear');
|
||||
d(ScanCode.NumpadClearEntry, 'NumpadClearEntry');
|
||||
d(ScanCode.ControlLeft, 'ControlLeft');
|
||||
d(ScanCode.ShiftLeft, 'ShiftLeft');
|
||||
d(ScanCode.AltLeft, 'AltLeft');
|
||||
d(ScanCode.MetaLeft, 'MetaLeft');
|
||||
d(ScanCode.ControlRight, 'ControlRight');
|
||||
d(ScanCode.ShiftRight, 'ShiftRight');
|
||||
d(ScanCode.AltRight, 'AltRight');
|
||||
d(ScanCode.MetaRight, 'MetaRight');
|
||||
d(ScanCode.BrightnessUp, 'BrightnessUp');
|
||||
d(ScanCode.BrightnessDown, 'BrightnessDown');
|
||||
d(ScanCode.MediaPlay, 'MediaPlay');
|
||||
d(ScanCode.MediaRecord, 'MediaRecord');
|
||||
d(ScanCode.MediaFastForward, 'MediaFastForward');
|
||||
d(ScanCode.MediaRewind, 'MediaRewind');
|
||||
d(ScanCode.MediaTrackNext, 'MediaTrackNext');
|
||||
d(ScanCode.MediaTrackPrevious, 'MediaTrackPrevious');
|
||||
d(ScanCode.MediaStop, 'MediaStop');
|
||||
d(ScanCode.Eject, 'Eject');
|
||||
d(ScanCode.MediaPlayPause, 'MediaPlayPause');
|
||||
d(ScanCode.MediaSelect, 'MediaSelect');
|
||||
d(ScanCode.LaunchMail, 'LaunchMail');
|
||||
d(ScanCode.LaunchApp2, 'LaunchApp2');
|
||||
d(ScanCode.LaunchApp1, 'LaunchApp1');
|
||||
d(ScanCode.SelectTask, 'SelectTask');
|
||||
d(ScanCode.LaunchScreenSaver, 'LaunchScreenSaver');
|
||||
d(ScanCode.BrowserSearch, 'BrowserSearch');
|
||||
d(ScanCode.BrowserHome, 'BrowserHome');
|
||||
d(ScanCode.BrowserBack, 'BrowserBack');
|
||||
d(ScanCode.BrowserForward, 'BrowserForward');
|
||||
d(ScanCode.BrowserStop, 'BrowserStop');
|
||||
d(ScanCode.BrowserRefresh, 'BrowserRefresh');
|
||||
d(ScanCode.BrowserFavorites, 'BrowserFavorites');
|
||||
d(ScanCode.ZoomToggle, 'ZoomToggle');
|
||||
d(ScanCode.MailReply, 'MailReply');
|
||||
d(ScanCode.MailForward, 'MailForward');
|
||||
d(ScanCode.MailSend, 'MailSend');
|
||||
})();
|
||||
|
||||
(function () {
|
||||
for (let i = 0; i <= ScanCode.MAX_VALUE; i++) {
|
||||
IMMUTABLE_CODE_TO_KEY_CODE[i] = -1;
|
||||
}
|
||||
|
||||
for (let i = 0; i <= KeyCode.MAX_VALUE; i++) {
|
||||
IMMUTABLE_KEY_CODE_TO_CODE[i] = -1;
|
||||
}
|
||||
|
||||
function define(code: ScanCode, keyCode: KeyCode): void {
|
||||
IMMUTABLE_CODE_TO_KEY_CODE[code] = keyCode;
|
||||
|
||||
if (
|
||||
(keyCode !== KeyCode.Unknown)
|
||||
&& (keyCode !== KeyCode.Enter)
|
||||
&& (keyCode !== KeyCode.Ctrl)
|
||||
&& (keyCode !== KeyCode.Shift)
|
||||
&& (keyCode !== KeyCode.Alt)
|
||||
&& (keyCode !== KeyCode.Meta)
|
||||
) {
|
||||
IMMUTABLE_KEY_CODE_TO_CODE[keyCode] = code;
|
||||
}
|
||||
}
|
||||
|
||||
// Manually added due to the exclusion above (due to duplication with NumpadEnter)
|
||||
IMMUTABLE_KEY_CODE_TO_CODE[KeyCode.Enter] = ScanCode.Enter;
|
||||
|
||||
define(ScanCode.None, KeyCode.Unknown);
|
||||
define(ScanCode.Hyper, KeyCode.Unknown);
|
||||
define(ScanCode.Super, KeyCode.Unknown);
|
||||
define(ScanCode.Fn, KeyCode.Unknown);
|
||||
define(ScanCode.FnLock, KeyCode.Unknown);
|
||||
define(ScanCode.Suspend, KeyCode.Unknown);
|
||||
define(ScanCode.Resume, KeyCode.Unknown);
|
||||
define(ScanCode.Turbo, KeyCode.Unknown);
|
||||
define(ScanCode.Sleep, KeyCode.Unknown);
|
||||
define(ScanCode.WakeUp, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyA, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyB, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyC, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyD, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyE, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyF, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyG, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyH, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyI, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyJ, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyK, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyL, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyM, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyN, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyO, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyP, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyQ, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyR, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyS, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyT, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyU, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyV, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyW, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyX, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyY, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyZ, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit1, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit2, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit3, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit4, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit5, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit6, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit7, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit8, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit9, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit0, KeyCode.Unknown);
|
||||
define(ScanCode.Enter, KeyCode.Enter);
|
||||
define(ScanCode.Escape, KeyCode.Escape);
|
||||
define(ScanCode.Backspace, KeyCode.Backspace);
|
||||
define(ScanCode.Tab, KeyCode.Tab);
|
||||
define(ScanCode.Space, KeyCode.Space);
|
||||
// define(ScanCode.Minus, KeyCode.Unknown);
|
||||
// define(ScanCode.Equal, KeyCode.Unknown);
|
||||
// define(ScanCode.BracketLeft, KeyCode.Unknown);
|
||||
// define(ScanCode.BracketRight, KeyCode.Unknown);
|
||||
// define(ScanCode.Backslash, KeyCode.Unknown);
|
||||
// define(ScanCode.IntlHash, KeyCode.Unknown);
|
||||
// define(ScanCode.Semicolon, KeyCode.Unknown);
|
||||
// define(ScanCode.Quote, KeyCode.Unknown);
|
||||
// define(ScanCode.Backquote, KeyCode.Unknown);
|
||||
// define(ScanCode.Comma, KeyCode.Unknown);
|
||||
// define(ScanCode.Period, KeyCode.Unknown);
|
||||
// define(ScanCode.Slash, KeyCode.Unknown);
|
||||
define(ScanCode.CapsLock, KeyCode.CapsLock);
|
||||
define(ScanCode.F1, KeyCode.F1);
|
||||
define(ScanCode.F2, KeyCode.F2);
|
||||
define(ScanCode.F3, KeyCode.F3);
|
||||
define(ScanCode.F4, KeyCode.F4);
|
||||
define(ScanCode.F5, KeyCode.F5);
|
||||
define(ScanCode.F6, KeyCode.F6);
|
||||
define(ScanCode.F7, KeyCode.F7);
|
||||
define(ScanCode.F8, KeyCode.F8);
|
||||
define(ScanCode.F9, KeyCode.F9);
|
||||
define(ScanCode.F10, KeyCode.F10);
|
||||
define(ScanCode.F11, KeyCode.F11);
|
||||
define(ScanCode.F12, KeyCode.F12);
|
||||
define(ScanCode.PrintScreen, KeyCode.Unknown);
|
||||
define(ScanCode.ScrollLock, KeyCode.ScrollLock);
|
||||
define(ScanCode.Pause, KeyCode.PauseBreak);
|
||||
define(ScanCode.Insert, KeyCode.Insert);
|
||||
define(ScanCode.Home, KeyCode.Home);
|
||||
define(ScanCode.PageUp, KeyCode.PageUp);
|
||||
define(ScanCode.Delete, KeyCode.Delete);
|
||||
define(ScanCode.End, KeyCode.End);
|
||||
define(ScanCode.PageDown, KeyCode.PageDown);
|
||||
define(ScanCode.ArrowRight, KeyCode.RightArrow);
|
||||
define(ScanCode.ArrowLeft, KeyCode.LeftArrow);
|
||||
define(ScanCode.ArrowDown, KeyCode.DownArrow);
|
||||
define(ScanCode.ArrowUp, KeyCode.UpArrow);
|
||||
define(ScanCode.NumLock, KeyCode.NumLock);
|
||||
define(ScanCode.NumpadDivide, KeyCode.NUMPAD_DIVIDE);
|
||||
define(ScanCode.NumpadMultiply, KeyCode.NUMPAD_MULTIPLY);
|
||||
define(ScanCode.NumpadSubtract, KeyCode.NUMPAD_SUBTRACT);
|
||||
define(ScanCode.NumpadAdd, KeyCode.NUMPAD_ADD);
|
||||
define(ScanCode.NumpadEnter, KeyCode.Enter); // Duplicate
|
||||
define(ScanCode.Numpad1, KeyCode.NUMPAD_1);
|
||||
define(ScanCode.Numpad2, KeyCode.NUMPAD_2);
|
||||
define(ScanCode.Numpad3, KeyCode.NUMPAD_3);
|
||||
define(ScanCode.Numpad4, KeyCode.NUMPAD_4);
|
||||
define(ScanCode.Numpad5, KeyCode.NUMPAD_5);
|
||||
define(ScanCode.Numpad6, KeyCode.NUMPAD_6);
|
||||
define(ScanCode.Numpad7, KeyCode.NUMPAD_7);
|
||||
define(ScanCode.Numpad8, KeyCode.NUMPAD_8);
|
||||
define(ScanCode.Numpad9, KeyCode.NUMPAD_9);
|
||||
define(ScanCode.Numpad0, KeyCode.NUMPAD_0);
|
||||
define(ScanCode.NumpadDecimal, KeyCode.NUMPAD_DECIMAL);
|
||||
// define(ScanCode.IntlBackslash, KeyCode.Unknown);
|
||||
define(ScanCode.ContextMenu, KeyCode.ContextMenu);
|
||||
define(ScanCode.Power, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadEqual, KeyCode.Unknown);
|
||||
define(ScanCode.F13, KeyCode.F13);
|
||||
define(ScanCode.F14, KeyCode.F14);
|
||||
define(ScanCode.F15, KeyCode.F15);
|
||||
define(ScanCode.F16, KeyCode.F16);
|
||||
define(ScanCode.F17, KeyCode.F17);
|
||||
define(ScanCode.F18, KeyCode.F18);
|
||||
define(ScanCode.F19, KeyCode.F19);
|
||||
define(ScanCode.F20, KeyCode.Unknown);
|
||||
define(ScanCode.F21, KeyCode.Unknown);
|
||||
define(ScanCode.F22, KeyCode.Unknown);
|
||||
define(ScanCode.F23, KeyCode.Unknown);
|
||||
define(ScanCode.F24, KeyCode.Unknown);
|
||||
define(ScanCode.Open, KeyCode.Unknown);
|
||||
define(ScanCode.Help, KeyCode.Unknown);
|
||||
define(ScanCode.Select, KeyCode.Unknown);
|
||||
define(ScanCode.Again, KeyCode.Unknown);
|
||||
define(ScanCode.Undo, KeyCode.Unknown);
|
||||
define(ScanCode.Cut, KeyCode.Unknown);
|
||||
define(ScanCode.Copy, KeyCode.Unknown);
|
||||
define(ScanCode.Paste, KeyCode.Unknown);
|
||||
define(ScanCode.Find, KeyCode.Unknown);
|
||||
define(ScanCode.AudioVolumeMute, KeyCode.Unknown);
|
||||
define(ScanCode.AudioVolumeUp, KeyCode.Unknown);
|
||||
define(ScanCode.AudioVolumeDown, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadComma, KeyCode.NUMPAD_SEPARATOR);
|
||||
// define(ScanCode.IntlRo, KeyCode.Unknown);
|
||||
define(ScanCode.KanaMode, KeyCode.Unknown);
|
||||
// define(ScanCode.IntlYen, KeyCode.Unknown);
|
||||
define(ScanCode.Convert, KeyCode.Unknown);
|
||||
define(ScanCode.NonConvert, KeyCode.Unknown);
|
||||
define(ScanCode.Lang1, KeyCode.Unknown);
|
||||
define(ScanCode.Lang2, KeyCode.Unknown);
|
||||
define(ScanCode.Lang3, KeyCode.Unknown);
|
||||
define(ScanCode.Lang4, KeyCode.Unknown);
|
||||
define(ScanCode.Lang5, KeyCode.Unknown);
|
||||
define(ScanCode.Abort, KeyCode.Unknown);
|
||||
define(ScanCode.Props, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadParenLeft, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadParenRight, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadBackspace, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemoryStore, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemoryRecall, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemoryClear, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemoryAdd, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemorySubtract, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadClear, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadClearEntry, KeyCode.Unknown);
|
||||
define(ScanCode.ControlLeft, KeyCode.Ctrl); // Duplicate
|
||||
define(ScanCode.ShiftLeft, KeyCode.Shift); // Duplicate
|
||||
define(ScanCode.AltLeft, KeyCode.Alt); // Duplicate
|
||||
define(ScanCode.MetaLeft, KeyCode.Meta); // Duplicate
|
||||
define(ScanCode.ControlRight, KeyCode.Ctrl); // Duplicate
|
||||
define(ScanCode.ShiftRight, KeyCode.Shift); // Duplicate
|
||||
define(ScanCode.AltRight, KeyCode.Alt); // Duplicate
|
||||
define(ScanCode.MetaRight, KeyCode.Meta); // Duplicate
|
||||
define(ScanCode.BrightnessUp, KeyCode.Unknown);
|
||||
define(ScanCode.BrightnessDown, KeyCode.Unknown);
|
||||
define(ScanCode.MediaPlay, KeyCode.Unknown);
|
||||
define(ScanCode.MediaRecord, KeyCode.Unknown);
|
||||
define(ScanCode.MediaFastForward, KeyCode.Unknown);
|
||||
define(ScanCode.MediaRewind, KeyCode.Unknown);
|
||||
define(ScanCode.MediaTrackNext, KeyCode.Unknown);
|
||||
define(ScanCode.MediaTrackPrevious, KeyCode.Unknown);
|
||||
define(ScanCode.MediaStop, KeyCode.Unknown);
|
||||
define(ScanCode.Eject, KeyCode.Unknown);
|
||||
define(ScanCode.MediaPlayPause, KeyCode.Unknown);
|
||||
define(ScanCode.MediaSelect, KeyCode.Unknown);
|
||||
define(ScanCode.LaunchMail, KeyCode.Unknown);
|
||||
define(ScanCode.LaunchApp2, KeyCode.Unknown);
|
||||
define(ScanCode.LaunchApp1, KeyCode.Unknown);
|
||||
define(ScanCode.SelectTask, KeyCode.Unknown);
|
||||
define(ScanCode.LaunchScreenSaver, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserSearch, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserHome, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserBack, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserForward, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserStop, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserRefresh, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserFavorites, KeyCode.Unknown);
|
||||
define(ScanCode.ZoomToggle, KeyCode.Unknown);
|
||||
define(ScanCode.MailReply, KeyCode.Unknown);
|
||||
define(ScanCode.MailForward, KeyCode.Unknown);
|
||||
define(ScanCode.MailSend, KeyCode.Unknown);
|
||||
})();
|
||||
@@ -0,0 +1,754 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { KeyCode, KeyCodeUtils, ResolvedKeybinding, Keybinding, SimpleKeybinding, KeybindingType, ResolvedKeybindingPart } from 'vs/base/common/keyCodes';
|
||||
import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, ScanCodeBinding } from 'vs/workbench/services/keybinding/common/scanCode';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { UILabelProvider, AriaLabelProvider, ElectronAcceleratorLabelProvider, UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
export interface IWindowsKeyMapping {
|
||||
vkey: string;
|
||||
value: string;
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
}
|
||||
|
||||
function windowsKeyMappingEquals(a: IWindowsKeyMapping, b: IWindowsKeyMapping): boolean {
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
a.vkey === b.vkey
|
||||
&& a.value === b.value
|
||||
&& a.withShift === b.withShift
|
||||
&& a.withAltGr === b.withAltGr
|
||||
&& a.withShiftAltGr === b.withShiftAltGr
|
||||
);
|
||||
}
|
||||
|
||||
export interface IWindowsKeyboardMapping {
|
||||
[scanCode: string]: IWindowsKeyMapping;
|
||||
}
|
||||
|
||||
export function windowsKeyboardMappingEquals(a: IWindowsKeyboardMapping, b: IWindowsKeyboardMapping): boolean {
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
for (let scanCode = 0; scanCode < ScanCode.MAX_VALUE; scanCode++) {
|
||||
const strScanCode = ScanCodeUtils.toString(scanCode);
|
||||
const aEntry = a[strScanCode];
|
||||
const bEntry = b[strScanCode];
|
||||
if (!windowsKeyMappingEquals(aEntry, bEntry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const LOG = false;
|
||||
function log(str: string): void {
|
||||
if (LOG) {
|
||||
console.info(str);
|
||||
}
|
||||
}
|
||||
|
||||
const NATIVE_KEY_CODE_TO_KEY_CODE: { [nativeKeyCode: string]: KeyCode; } = _getNativeMap();
|
||||
|
||||
export interface IScanCodeMapping {
|
||||
scanCode: ScanCode;
|
||||
keyCode: KeyCode;
|
||||
value: string;
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
}
|
||||
|
||||
export class WindowsNativeResolvedKeybinding extends ResolvedKeybinding {
|
||||
|
||||
private readonly _mapper: WindowsKeyboardMapper;
|
||||
private readonly _firstPart: SimpleKeybinding;
|
||||
private readonly _chordPart: SimpleKeybinding;
|
||||
|
||||
constructor(mapper: WindowsKeyboardMapper, firstPart: SimpleKeybinding, chordPart: SimpleKeybinding) {
|
||||
super();
|
||||
this._mapper = mapper;
|
||||
this._firstPart = firstPart;
|
||||
this._chordPart = chordPart;
|
||||
}
|
||||
|
||||
private _getUILabelForKeybinding(keybinding: SimpleKeybinding): string {
|
||||
if (!keybinding) {
|
||||
return null;
|
||||
}
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return '';
|
||||
}
|
||||
return this._mapper.getUILabelForKeyCode(keybinding.keyCode);
|
||||
}
|
||||
|
||||
public getLabel(): string {
|
||||
let firstPart = this._getUILabelForKeybinding(this._firstPart);
|
||||
let chordPart = this._getUILabelForKeybinding(this._chordPart);
|
||||
return UILabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows);
|
||||
}
|
||||
|
||||
private _getUSLabelForKeybinding(keybinding: SimpleKeybinding): string {
|
||||
if (!keybinding) {
|
||||
return null;
|
||||
}
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return '';
|
||||
}
|
||||
return KeyCodeUtils.toString(keybinding.keyCode);
|
||||
}
|
||||
|
||||
public getUSLabel(): string {
|
||||
let firstPart = this._getUSLabelForKeybinding(this._firstPart);
|
||||
let chordPart = this._getUSLabelForKeybinding(this._chordPart);
|
||||
return UILabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows);
|
||||
}
|
||||
|
||||
private _getAriaLabelForKeybinding(keybinding: SimpleKeybinding): string {
|
||||
if (!keybinding) {
|
||||
return null;
|
||||
}
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return '';
|
||||
}
|
||||
return this._mapper.getAriaLabelForKeyCode(keybinding.keyCode);
|
||||
}
|
||||
|
||||
public getAriaLabel(): string {
|
||||
let firstPart = this._getAriaLabelForKeybinding(this._firstPart);
|
||||
let chordPart = this._getAriaLabelForKeybinding(this._chordPart);
|
||||
return AriaLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows);
|
||||
}
|
||||
|
||||
private _keyCodeToElectronAccelerator(keyCode: KeyCode): string {
|
||||
if (keyCode >= KeyCode.NUMPAD_0 && keyCode <= KeyCode.NUMPAD_DIVIDE) {
|
||||
// Electron cannot handle numpad keys
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyCode.UpArrow:
|
||||
return 'Up';
|
||||
case KeyCode.DownArrow:
|
||||
return 'Down';
|
||||
case KeyCode.LeftArrow:
|
||||
return 'Left';
|
||||
case KeyCode.RightArrow:
|
||||
return 'Right';
|
||||
}
|
||||
|
||||
// electron menus always do the correct rendering on Windows
|
||||
return KeyCodeUtils.toString(keyCode);
|
||||
}
|
||||
|
||||
private _getElectronAcceleratorLabelForKeybinding(keybinding: SimpleKeybinding): string {
|
||||
if (!keybinding) {
|
||||
return null;
|
||||
}
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return null;
|
||||
}
|
||||
return this._keyCodeToElectronAccelerator(keybinding.keyCode);
|
||||
}
|
||||
|
||||
public getElectronAccelerator(): string {
|
||||
if (this._chordPart !== null) {
|
||||
// Electron cannot handle chords
|
||||
return null;
|
||||
}
|
||||
|
||||
let firstPart = this._getElectronAcceleratorLabelForKeybinding(this._firstPart);
|
||||
return ElectronAcceleratorLabelProvider.toLabel(this._firstPart, firstPart, null, null, OperatingSystem.Windows);
|
||||
}
|
||||
|
||||
private _getUserSettingsLabelForKeybinding(keybinding: SimpleKeybinding): string {
|
||||
if (!keybinding) {
|
||||
return null;
|
||||
}
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return '';
|
||||
}
|
||||
return this._mapper.getUserSettingsLabelForKeyCode(keybinding.keyCode);
|
||||
}
|
||||
|
||||
public getUserSettingsLabel(): string {
|
||||
let firstPart = this._getUserSettingsLabelForKeybinding(this._firstPart);
|
||||
let chordPart = this._getUserSettingsLabelForKeybinding(this._chordPart);
|
||||
let result = UserSettingsLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows);
|
||||
return (result ? result.toLowerCase() : result);
|
||||
}
|
||||
|
||||
public isWYSIWYG(): boolean {
|
||||
if (this._firstPart && !this._isWYSIWYG(this._firstPart.keyCode)) {
|
||||
return false;
|
||||
}
|
||||
if (this._chordPart && !this._isWYSIWYG(this._chordPart.keyCode)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private _isWYSIWYG(keyCode: KeyCode): boolean {
|
||||
if (
|
||||
keyCode === KeyCode.LeftArrow
|
||||
|| keyCode === KeyCode.UpArrow
|
||||
|| keyCode === KeyCode.RightArrow
|
||||
|| keyCode === KeyCode.DownArrow
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const ariaLabel = this._mapper.getAriaLabelForKeyCode(keyCode);
|
||||
const userSettingsLabel = this._mapper.getUserSettingsLabelForKeyCode(keyCode);
|
||||
return (ariaLabel === userSettingsLabel);
|
||||
}
|
||||
|
||||
public isChord(): boolean {
|
||||
return (this._chordPart ? true : false);
|
||||
}
|
||||
|
||||
public getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart] {
|
||||
return [
|
||||
this._toResolvedKeybindingPart(this._firstPart),
|
||||
this._toResolvedKeybindingPart(this._chordPart)
|
||||
];
|
||||
}
|
||||
|
||||
private _toResolvedKeybindingPart(keybinding: SimpleKeybinding): ResolvedKeybindingPart {
|
||||
if (!keybinding) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ResolvedKeybindingPart(
|
||||
keybinding.ctrlKey,
|
||||
keybinding.shiftKey,
|
||||
keybinding.altKey,
|
||||
keybinding.metaKey,
|
||||
this._getUILabelForKeybinding(keybinding),
|
||||
this._getAriaLabelForKeybinding(keybinding)
|
||||
);
|
||||
}
|
||||
|
||||
public getDispatchParts(): [string, string] {
|
||||
let firstPart = this._firstPart ? this._getDispatchStr(this._firstPart) : null;
|
||||
let chordPart = this._chordPart ? this._getDispatchStr(this._chordPart) : null;
|
||||
return [firstPart, chordPart];
|
||||
}
|
||||
|
||||
private _getDispatchStr(keybinding: SimpleKeybinding): string {
|
||||
if (keybinding.isModifierKey()) {
|
||||
return null;
|
||||
}
|
||||
let result = '';
|
||||
|
||||
if (keybinding.ctrlKey) {
|
||||
result += 'ctrl+';
|
||||
}
|
||||
if (keybinding.shiftKey) {
|
||||
result += 'shift+';
|
||||
}
|
||||
if (keybinding.altKey) {
|
||||
result += 'alt+';
|
||||
}
|
||||
if (keybinding.metaKey) {
|
||||
result += 'meta+';
|
||||
}
|
||||
result += KeyCodeUtils.toString(keybinding.keyCode);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static getProducedCharCode(kb: ScanCodeBinding, mapping: IScanCodeMapping): string {
|
||||
if (!mapping) {
|
||||
return null;
|
||||
}
|
||||
if (kb.ctrlKey && kb.shiftKey && kb.altKey) {
|
||||
return mapping.withShiftAltGr;
|
||||
}
|
||||
if (kb.ctrlKey && kb.altKey) {
|
||||
return mapping.withAltGr;
|
||||
}
|
||||
if (kb.shiftKey) {
|
||||
return mapping.withShift;
|
||||
}
|
||||
return mapping.value;
|
||||
}
|
||||
|
||||
public static getProducedChar(kb: ScanCodeBinding, mapping: IScanCodeMapping): string {
|
||||
const char = this.getProducedCharCode(kb, mapping);
|
||||
if (char === null || char.length === 0) {
|
||||
return ' --- ';
|
||||
}
|
||||
return ' ' + char + ' ';
|
||||
}
|
||||
}
|
||||
|
||||
export class WindowsKeyboardMapper implements IKeyboardMapper {
|
||||
|
||||
public readonly isUSStandard: boolean;
|
||||
private readonly _codeInfo: IScanCodeMapping[];
|
||||
private readonly _scanCodeToKeyCode: KeyCode[];
|
||||
private readonly _keyCodeToLabel: string[] = [];
|
||||
private readonly _keyCodeExists: boolean[];
|
||||
|
||||
constructor(isUSStandard: boolean, rawMappings: IWindowsKeyboardMapping) {
|
||||
this.isUSStandard = isUSStandard;
|
||||
this._scanCodeToKeyCode = [];
|
||||
this._keyCodeToLabel = [];
|
||||
this._keyCodeExists = [];
|
||||
this._keyCodeToLabel[KeyCode.Unknown] = KeyCodeUtils.toString(KeyCode.Unknown);
|
||||
|
||||
for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) {
|
||||
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode];
|
||||
if (immutableKeyCode !== -1) {
|
||||
this._scanCodeToKeyCode[scanCode] = immutableKeyCode;
|
||||
this._keyCodeToLabel[immutableKeyCode] = KeyCodeUtils.toString(immutableKeyCode);
|
||||
this._keyCodeExists[immutableKeyCode] = true;
|
||||
}
|
||||
}
|
||||
|
||||
let producesLetter: boolean[] = [];
|
||||
|
||||
this._codeInfo = [];
|
||||
for (let strCode in rawMappings) {
|
||||
if (rawMappings.hasOwnProperty(strCode)) {
|
||||
const scanCode = ScanCodeUtils.toEnum(strCode);
|
||||
if (scanCode === ScanCode.None) {
|
||||
log(`Unknown scanCode ${strCode} in mapping.`);
|
||||
continue;
|
||||
}
|
||||
const rawMapping = rawMappings[strCode];
|
||||
|
||||
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode];
|
||||
if (immutableKeyCode !== -1) {
|
||||
const keyCode = NATIVE_KEY_CODE_TO_KEY_CODE[rawMapping.vkey] || KeyCode.Unknown;
|
||||
if (keyCode === KeyCode.Unknown || immutableKeyCode === keyCode) {
|
||||
continue;
|
||||
}
|
||||
if (scanCode !== ScanCode.NumpadComma) {
|
||||
// Looks like ScanCode.NumpadComma doesn't always map to KeyCode.NUMPAD_SEPARATOR
|
||||
// e.g. on POR - PTB
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const value = rawMapping.value;
|
||||
const withShift = rawMapping.withShift;
|
||||
const withAltGr = rawMapping.withAltGr;
|
||||
const withShiftAltGr = rawMapping.withShiftAltGr;
|
||||
const keyCode = NATIVE_KEY_CODE_TO_KEY_CODE[rawMapping.vkey] || KeyCode.Unknown;
|
||||
|
||||
const mapping: IScanCodeMapping = {
|
||||
scanCode: scanCode,
|
||||
keyCode: keyCode,
|
||||
value: value,
|
||||
withShift: withShift,
|
||||
withAltGr: withAltGr,
|
||||
withShiftAltGr: withShiftAltGr,
|
||||
};
|
||||
this._codeInfo[scanCode] = mapping;
|
||||
this._scanCodeToKeyCode[scanCode] = keyCode;
|
||||
|
||||
if (keyCode === KeyCode.Unknown) {
|
||||
continue;
|
||||
}
|
||||
this._keyCodeExists[keyCode] = true;
|
||||
|
||||
if (value.length === 0) {
|
||||
// This key does not produce strings
|
||||
this._keyCodeToLabel[keyCode] = null;
|
||||
}
|
||||
|
||||
else if (value.length > 1) {
|
||||
// This key produces a letter representable with multiple UTF-16 code units.
|
||||
this._keyCodeToLabel[keyCode] = value;
|
||||
}
|
||||
|
||||
else {
|
||||
const charCode = value.charCodeAt(0);
|
||||
|
||||
if (charCode >= CharCode.a && charCode <= CharCode.z) {
|
||||
const upperCaseValue = CharCode.A + (charCode - CharCode.a);
|
||||
producesLetter[upperCaseValue] = true;
|
||||
this._keyCodeToLabel[keyCode] = String.fromCharCode(CharCode.A + (charCode - CharCode.a));
|
||||
}
|
||||
|
||||
else if (charCode >= CharCode.A && charCode <= CharCode.Z) {
|
||||
producesLetter[charCode] = true;
|
||||
this._keyCodeToLabel[keyCode] = value;
|
||||
}
|
||||
|
||||
else {
|
||||
this._keyCodeToLabel[keyCode] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle keyboard layouts where latin characters are not produced e.g. Cyrillic
|
||||
const _registerLetterIfMissing = (charCode: CharCode, keyCode: KeyCode): void => {
|
||||
if (!producesLetter[charCode]) {
|
||||
this._keyCodeToLabel[keyCode] = String.fromCharCode(charCode);
|
||||
}
|
||||
};
|
||||
_registerLetterIfMissing(CharCode.A, KeyCode.KEY_A);
|
||||
_registerLetterIfMissing(CharCode.B, KeyCode.KEY_B);
|
||||
_registerLetterIfMissing(CharCode.C, KeyCode.KEY_C);
|
||||
_registerLetterIfMissing(CharCode.D, KeyCode.KEY_D);
|
||||
_registerLetterIfMissing(CharCode.E, KeyCode.KEY_E);
|
||||
_registerLetterIfMissing(CharCode.F, KeyCode.KEY_F);
|
||||
_registerLetterIfMissing(CharCode.G, KeyCode.KEY_G);
|
||||
_registerLetterIfMissing(CharCode.H, KeyCode.KEY_H);
|
||||
_registerLetterIfMissing(CharCode.I, KeyCode.KEY_I);
|
||||
_registerLetterIfMissing(CharCode.J, KeyCode.KEY_J);
|
||||
_registerLetterIfMissing(CharCode.K, KeyCode.KEY_K);
|
||||
_registerLetterIfMissing(CharCode.L, KeyCode.KEY_L);
|
||||
_registerLetterIfMissing(CharCode.M, KeyCode.KEY_M);
|
||||
_registerLetterIfMissing(CharCode.N, KeyCode.KEY_N);
|
||||
_registerLetterIfMissing(CharCode.O, KeyCode.KEY_O);
|
||||
_registerLetterIfMissing(CharCode.P, KeyCode.KEY_P);
|
||||
_registerLetterIfMissing(CharCode.Q, KeyCode.KEY_Q);
|
||||
_registerLetterIfMissing(CharCode.R, KeyCode.KEY_R);
|
||||
_registerLetterIfMissing(CharCode.S, KeyCode.KEY_S);
|
||||
_registerLetterIfMissing(CharCode.T, KeyCode.KEY_T);
|
||||
_registerLetterIfMissing(CharCode.U, KeyCode.KEY_U);
|
||||
_registerLetterIfMissing(CharCode.V, KeyCode.KEY_V);
|
||||
_registerLetterIfMissing(CharCode.W, KeyCode.KEY_W);
|
||||
_registerLetterIfMissing(CharCode.X, KeyCode.KEY_X);
|
||||
_registerLetterIfMissing(CharCode.Y, KeyCode.KEY_Y);
|
||||
_registerLetterIfMissing(CharCode.Z, KeyCode.KEY_Z);
|
||||
}
|
||||
|
||||
public dumpDebugInfo(): string {
|
||||
let result: string[] = [];
|
||||
|
||||
let immutableSamples = [
|
||||
ScanCode.ArrowUp,
|
||||
ScanCode.Numpad0
|
||||
];
|
||||
|
||||
let cnt = 0;
|
||||
result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`);
|
||||
for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) {
|
||||
if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== -1) {
|
||||
if (immutableSamples.indexOf(scanCode) === -1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt % 6 === 0) {
|
||||
result.push(`| HW Code combination | Key | KeyCode combination | UI label | User settings | WYSIWYG |`);
|
||||
result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`);
|
||||
}
|
||||
cnt++;
|
||||
|
||||
const mapping = this._codeInfo[scanCode];
|
||||
const strCode = ScanCodeUtils.toString(scanCode);
|
||||
|
||||
let mods = [0b000, 0b010, 0b101, 0b111];
|
||||
for (let modIndex = 0; modIndex < mods.length; modIndex++) {
|
||||
const mod = mods[modIndex];
|
||||
const ctrlKey = (mod & 0b001) ? true : false;
|
||||
const shiftKey = (mod & 0b010) ? true : false;
|
||||
const altKey = (mod & 0b100) ? true : false;
|
||||
const scanCodeBinding = new ScanCodeBinding(ctrlKey, shiftKey, altKey, false, scanCode);
|
||||
const kb = this._resolveSimpleUserBinding(scanCodeBinding);
|
||||
const strKeyCode = (kb ? KeyCodeUtils.toString(kb.keyCode) : null);
|
||||
const resolvedKb = (kb ? new WindowsNativeResolvedKeybinding(this, kb, null) : null);
|
||||
|
||||
const outScanCode = `${ctrlKey ? 'Ctrl+' : ''}${shiftKey ? 'Shift+' : ''}${altKey ? 'Alt+' : ''}${strCode}`;
|
||||
const ariaLabel = (resolvedKb ? resolvedKb.getAriaLabel() : null);
|
||||
const outUILabel = (ariaLabel ? ariaLabel.replace(/Control\+/, 'Ctrl+') : null);
|
||||
const outUserSettings = (resolvedKb ? resolvedKb.getUserSettingsLabel() : null);
|
||||
const outKey = WindowsNativeResolvedKeybinding.getProducedChar(scanCodeBinding, mapping);
|
||||
const outKb = (strKeyCode ? `${ctrlKey ? 'Ctrl+' : ''}${shiftKey ? 'Shift+' : ''}${altKey ? 'Alt+' : ''}${strKeyCode}` : null);
|
||||
const isWYSIWYG = (resolvedKb ? resolvedKb.isWYSIWYG() : false);
|
||||
const outWYSIWYG = (isWYSIWYG ? ' ' : ' NO ');
|
||||
result.push(`| ${this._leftPad(outScanCode, 30)} | ${outKey} | ${this._leftPad(outKb, 25)} | ${this._leftPad(outUILabel, 25)} | ${this._leftPad(outUserSettings, 25)} | ${outWYSIWYG} |`);
|
||||
}
|
||||
result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`);
|
||||
}
|
||||
|
||||
|
||||
return result.join('\n');
|
||||
}
|
||||
|
||||
private _leftPad(str: string, cnt: number): string {
|
||||
if (str === null) {
|
||||
str = 'null';
|
||||
}
|
||||
while (str.length < cnt) {
|
||||
str = ' ' + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public getUILabelForKeyCode(keyCode: KeyCode): string {
|
||||
return this._getLabelForKeyCode(keyCode);
|
||||
}
|
||||
|
||||
public getAriaLabelForKeyCode(keyCode: KeyCode): string {
|
||||
return this._getLabelForKeyCode(keyCode);
|
||||
}
|
||||
|
||||
public getUserSettingsLabelForKeyCode(keyCode: KeyCode): string {
|
||||
if (this.isUSStandard) {
|
||||
return KeyCodeUtils.toUserSettingsUS(keyCode);
|
||||
}
|
||||
return KeyCodeUtils.toUserSettingsGeneral(keyCode);
|
||||
}
|
||||
|
||||
private _getLabelForKeyCode(keyCode: KeyCode): string {
|
||||
return this._keyCodeToLabel[keyCode] || KeyCodeUtils.toString(KeyCode.Unknown);
|
||||
}
|
||||
|
||||
public resolveKeybinding(keybinding: Keybinding): WindowsNativeResolvedKeybinding[] {
|
||||
if (keybinding.type === KeybindingType.Chord) {
|
||||
const firstPartKeyCode = keybinding.firstPart.keyCode;
|
||||
const chordPartKeyCode = keybinding.chordPart.keyCode;
|
||||
if (!this._keyCodeExists[firstPartKeyCode] || !this._keyCodeExists[chordPartKeyCode]) {
|
||||
return [];
|
||||
}
|
||||
return [new WindowsNativeResolvedKeybinding(this, keybinding.firstPart, keybinding.chordPart)];
|
||||
} else {
|
||||
if (!this._keyCodeExists[keybinding.keyCode]) {
|
||||
return [];
|
||||
}
|
||||
return [new WindowsNativeResolvedKeybinding(this, keybinding, null)];
|
||||
}
|
||||
}
|
||||
|
||||
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): WindowsNativeResolvedKeybinding {
|
||||
const keybinding = new SimpleKeybinding(keyboardEvent.ctrlKey, keyboardEvent.shiftKey, keyboardEvent.altKey, keyboardEvent.metaKey, keyboardEvent.keyCode);
|
||||
return new WindowsNativeResolvedKeybinding(this, keybinding, null);
|
||||
}
|
||||
|
||||
private _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding): SimpleKeybinding {
|
||||
if (!binding) {
|
||||
return null;
|
||||
}
|
||||
if (binding instanceof SimpleKeybinding) {
|
||||
if (!this._keyCodeExists[binding.keyCode]) {
|
||||
return null;
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
const keyCode = this._scanCodeToKeyCode[binding.scanCode] || KeyCode.Unknown;
|
||||
if (keyCode === KeyCode.Unknown || !this._keyCodeExists[keyCode]) {
|
||||
return null;
|
||||
}
|
||||
return new SimpleKeybinding(binding.ctrlKey, binding.shiftKey, binding.altKey, binding.metaKey, keyCode);
|
||||
}
|
||||
|
||||
public resolveUserBinding(firstPart: SimpleKeybinding | ScanCodeBinding, chordPart: SimpleKeybinding | ScanCodeBinding): ResolvedKeybinding[] {
|
||||
const _firstPart = this._resolveSimpleUserBinding(firstPart);
|
||||
const _chordPart = this._resolveSimpleUserBinding(chordPart);
|
||||
if (_firstPart && _chordPart) {
|
||||
return [new WindowsNativeResolvedKeybinding(this, _firstPart, _chordPart)];
|
||||
}
|
||||
if (_firstPart) {
|
||||
return [new WindowsNativeResolvedKeybinding(this, _firstPart, null)];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
// See https://github.com/Microsoft/node-native-keymap/blob/master/deps/chromium/keyboard_codes_win.h
|
||||
function _getNativeMap() {
|
||||
return {
|
||||
VK_BACK: KeyCode.Backspace,
|
||||
VK_TAB: KeyCode.Tab,
|
||||
VK_CLEAR: KeyCode.Unknown, // MISSING
|
||||
VK_RETURN: KeyCode.Enter,
|
||||
VK_SHIFT: KeyCode.Shift,
|
||||
VK_CONTROL: KeyCode.Ctrl,
|
||||
VK_MENU: KeyCode.Alt,
|
||||
VK_PAUSE: KeyCode.PauseBreak,
|
||||
VK_CAPITAL: KeyCode.CapsLock,
|
||||
VK_KANA: KeyCode.Unknown, // MISSING
|
||||
VK_HANGUL: KeyCode.Unknown, // MISSING
|
||||
VK_JUNJA: KeyCode.Unknown, // MISSING
|
||||
VK_FINAL: KeyCode.Unknown, // MISSING
|
||||
VK_HANJA: KeyCode.Unknown, // MISSING
|
||||
VK_KANJI: KeyCode.Unknown, // MISSING
|
||||
VK_ESCAPE: KeyCode.Escape,
|
||||
VK_CONVERT: KeyCode.Unknown, // MISSING
|
||||
VK_NONCONVERT: KeyCode.Unknown, // MISSING
|
||||
VK_ACCEPT: KeyCode.Unknown, // MISSING
|
||||
VK_MODECHANGE: KeyCode.Unknown, // MISSING
|
||||
VK_SPACE: KeyCode.Space,
|
||||
VK_PRIOR: KeyCode.PageUp,
|
||||
VK_NEXT: KeyCode.PageDown,
|
||||
VK_END: KeyCode.End,
|
||||
VK_HOME: KeyCode.Home,
|
||||
VK_LEFT: KeyCode.LeftArrow,
|
||||
VK_UP: KeyCode.UpArrow,
|
||||
VK_RIGHT: KeyCode.RightArrow,
|
||||
VK_DOWN: KeyCode.DownArrow,
|
||||
VK_SELECT: KeyCode.Unknown, // MISSING
|
||||
VK_PRINT: KeyCode.Unknown, // MISSING
|
||||
VK_EXECUTE: KeyCode.Unknown, // MISSING
|
||||
VK_SNAPSHOT: KeyCode.Unknown, // MISSING
|
||||
VK_INSERT: KeyCode.Insert,
|
||||
VK_DELETE: KeyCode.Delete,
|
||||
VK_HELP: KeyCode.Unknown, // MISSING
|
||||
|
||||
VK_0: KeyCode.KEY_0,
|
||||
VK_1: KeyCode.KEY_1,
|
||||
VK_2: KeyCode.KEY_2,
|
||||
VK_3: KeyCode.KEY_3,
|
||||
VK_4: KeyCode.KEY_4,
|
||||
VK_5: KeyCode.KEY_5,
|
||||
VK_6: KeyCode.KEY_6,
|
||||
VK_7: KeyCode.KEY_7,
|
||||
VK_8: KeyCode.KEY_8,
|
||||
VK_9: KeyCode.KEY_9,
|
||||
VK_A: KeyCode.KEY_A,
|
||||
VK_B: KeyCode.KEY_B,
|
||||
VK_C: KeyCode.KEY_C,
|
||||
VK_D: KeyCode.KEY_D,
|
||||
VK_E: KeyCode.KEY_E,
|
||||
VK_F: KeyCode.KEY_F,
|
||||
VK_G: KeyCode.KEY_G,
|
||||
VK_H: KeyCode.KEY_H,
|
||||
VK_I: KeyCode.KEY_I,
|
||||
VK_J: KeyCode.KEY_J,
|
||||
VK_K: KeyCode.KEY_K,
|
||||
VK_L: KeyCode.KEY_L,
|
||||
VK_M: KeyCode.KEY_M,
|
||||
VK_N: KeyCode.KEY_N,
|
||||
VK_O: KeyCode.KEY_O,
|
||||
VK_P: KeyCode.KEY_P,
|
||||
VK_Q: KeyCode.KEY_Q,
|
||||
VK_R: KeyCode.KEY_R,
|
||||
VK_S: KeyCode.KEY_S,
|
||||
VK_T: KeyCode.KEY_T,
|
||||
VK_U: KeyCode.KEY_U,
|
||||
VK_V: KeyCode.KEY_V,
|
||||
VK_W: KeyCode.KEY_W,
|
||||
VK_X: KeyCode.KEY_X,
|
||||
VK_Y: KeyCode.KEY_Y,
|
||||
VK_Z: KeyCode.KEY_Z,
|
||||
|
||||
VK_LWIN: KeyCode.Meta,
|
||||
VK_COMMAND: KeyCode.Meta,
|
||||
VK_RWIN: KeyCode.Meta,
|
||||
VK_APPS: KeyCode.Unknown, // MISSING
|
||||
VK_SLEEP: KeyCode.Unknown, // MISSING
|
||||
VK_NUMPAD0: KeyCode.NUMPAD_0,
|
||||
VK_NUMPAD1: KeyCode.NUMPAD_1,
|
||||
VK_NUMPAD2: KeyCode.NUMPAD_2,
|
||||
VK_NUMPAD3: KeyCode.NUMPAD_3,
|
||||
VK_NUMPAD4: KeyCode.NUMPAD_4,
|
||||
VK_NUMPAD5: KeyCode.NUMPAD_5,
|
||||
VK_NUMPAD6: KeyCode.NUMPAD_6,
|
||||
VK_NUMPAD7: KeyCode.NUMPAD_7,
|
||||
VK_NUMPAD8: KeyCode.NUMPAD_8,
|
||||
VK_NUMPAD9: KeyCode.NUMPAD_9,
|
||||
VK_MULTIPLY: KeyCode.NUMPAD_MULTIPLY,
|
||||
VK_ADD: KeyCode.NUMPAD_ADD,
|
||||
VK_SEPARATOR: KeyCode.NUMPAD_SEPARATOR,
|
||||
VK_SUBTRACT: KeyCode.NUMPAD_SUBTRACT,
|
||||
VK_DECIMAL: KeyCode.NUMPAD_DECIMAL,
|
||||
VK_DIVIDE: KeyCode.NUMPAD_DIVIDE,
|
||||
VK_F1: KeyCode.F1,
|
||||
VK_F2: KeyCode.F2,
|
||||
VK_F3: KeyCode.F3,
|
||||
VK_F4: KeyCode.F4,
|
||||
VK_F5: KeyCode.F5,
|
||||
VK_F6: KeyCode.F6,
|
||||
VK_F7: KeyCode.F7,
|
||||
VK_F8: KeyCode.F8,
|
||||
VK_F9: KeyCode.F9,
|
||||
VK_F10: KeyCode.F10,
|
||||
VK_F11: KeyCode.F11,
|
||||
VK_F12: KeyCode.F12,
|
||||
VK_F13: KeyCode.F13,
|
||||
VK_F14: KeyCode.F14,
|
||||
VK_F15: KeyCode.F15,
|
||||
VK_F16: KeyCode.F16,
|
||||
VK_F17: KeyCode.F17,
|
||||
VK_F18: KeyCode.F18,
|
||||
VK_F19: KeyCode.F19,
|
||||
VK_F20: KeyCode.Unknown, // MISSING
|
||||
VK_F21: KeyCode.Unknown, // MISSING
|
||||
VK_F22: KeyCode.Unknown, // MISSING
|
||||
VK_F23: KeyCode.Unknown, // MISSING
|
||||
VK_F24: KeyCode.Unknown, // MISSING
|
||||
VK_NUMLOCK: KeyCode.NumLock,
|
||||
VK_SCROLL: KeyCode.ScrollLock,
|
||||
VK_LSHIFT: KeyCode.Shift,
|
||||
VK_RSHIFT: KeyCode.Shift,
|
||||
VK_LCONTROL: KeyCode.Ctrl,
|
||||
VK_RCONTROL: KeyCode.Ctrl,
|
||||
VK_LMENU: KeyCode.Unknown, // MISSING
|
||||
VK_RMENU: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_BACK: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_FORWARD: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_REFRESH: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_STOP: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_SEARCH: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_FAVORITES: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_HOME: KeyCode.Unknown, // MISSING
|
||||
VK_VOLUME_MUTE: KeyCode.Unknown, // MISSING
|
||||
VK_VOLUME_DOWN: KeyCode.Unknown, // MISSING
|
||||
VK_VOLUME_UP: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_NEXT_TRACK: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_PREV_TRACK: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_STOP: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_PLAY_PAUSE: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_LAUNCH_MAIL: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_LAUNCH_MEDIA_SELECT: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_LAUNCH_APP1: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_LAUNCH_APP2: KeyCode.Unknown, // MISSING
|
||||
VK_OEM_1: KeyCode.US_SEMICOLON,
|
||||
VK_OEM_PLUS: KeyCode.US_EQUAL,
|
||||
VK_OEM_COMMA: KeyCode.US_COMMA,
|
||||
VK_OEM_MINUS: KeyCode.US_MINUS,
|
||||
VK_OEM_PERIOD: KeyCode.US_DOT,
|
||||
VK_OEM_2: KeyCode.US_SLASH,
|
||||
VK_OEM_3: KeyCode.US_BACKTICK,
|
||||
VK_ABNT_C1: KeyCode.ABNT_C1,
|
||||
VK_ABNT_C2: KeyCode.ABNT_C2,
|
||||
VK_OEM_4: KeyCode.US_OPEN_SQUARE_BRACKET,
|
||||
VK_OEM_5: KeyCode.US_BACKSLASH,
|
||||
VK_OEM_6: KeyCode.US_CLOSE_SQUARE_BRACKET,
|
||||
VK_OEM_7: KeyCode.US_QUOTE,
|
||||
VK_OEM_8: KeyCode.OEM_8,
|
||||
VK_OEM_102: KeyCode.OEM_102,
|
||||
VK_PROCESSKEY: KeyCode.Unknown, // MISSING
|
||||
VK_PACKET: KeyCode.Unknown, // MISSING
|
||||
VK_DBE_SBCSCHAR: KeyCode.Unknown, // MISSING
|
||||
VK_DBE_DBCSCHAR: KeyCode.Unknown, // MISSING
|
||||
VK_ATTN: KeyCode.Unknown, // MISSING
|
||||
VK_CRSEL: KeyCode.Unknown, // MISSING
|
||||
VK_EXSEL: KeyCode.Unknown, // MISSING
|
||||
VK_EREOF: KeyCode.Unknown, // MISSING
|
||||
VK_PLAY: KeyCode.Unknown, // MISSING
|
||||
VK_ZOOM: KeyCode.Unknown, // MISSING
|
||||
VK_NONAME: KeyCode.Unknown, // MISSING
|
||||
VK_PA1: KeyCode.Unknown, // MISSING
|
||||
VK_OEM_CLEAR: KeyCode.Unknown, // MISSING
|
||||
VK_UNKNOWN: KeyCode.Unknown,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { ResolvedKeybinding, Keybinding } from 'vs/base/common/keyCodes';
|
||||
import { OS, OperatingSystem } from 'vs/base/common/platform';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService';
|
||||
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IKeybindingEvent, IUserFriendlyKeybinding, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingItem, KeybindingsRegistry, IKeybindingRule2 } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { ConfigWatcher } from 'vs/base/node/config';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
import { KeybindingIO, OutputBuilder, IUserKeybindingItem } from 'vs/workbench/services/keybinding/common/keybindingIO';
|
||||
import * as nativeKeymap from 'native-keymap';
|
||||
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
|
||||
import { WindowsKeyboardMapper, IWindowsKeyboardMapping, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper';
|
||||
import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper';
|
||||
import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Extensions as ConfigExtensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
export class KeyboardMapperFactory {
|
||||
public static INSTANCE = new KeyboardMapperFactory();
|
||||
|
||||
private _isISOKeyboard: boolean;
|
||||
private _layoutInfo: nativeKeymap.IKeyboardLayoutInfo;
|
||||
private _rawMapping: nativeKeymap.IKeyboardMapping;
|
||||
private _keyboardMapper: IKeyboardMapper;
|
||||
private _initialized: boolean;
|
||||
|
||||
private _onDidChangeKeyboardMapper: Emitter<void> = new Emitter<void>();
|
||||
public onDidChangeKeyboardMapper: Event<void> = this._onDidChangeKeyboardMapper.event;
|
||||
|
||||
private constructor() {
|
||||
this._isISOKeyboard = false;
|
||||
this._layoutInfo = null;
|
||||
this._rawMapping = null;
|
||||
this._keyboardMapper = null;
|
||||
this._initialized = false;
|
||||
}
|
||||
|
||||
public _onKeyboardLayoutChanged(isISOKeyboard: boolean): void {
|
||||
isISOKeyboard = !!isISOKeyboard;
|
||||
if (this._initialized) {
|
||||
this._setKeyboardData(isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap());
|
||||
} else {
|
||||
this._isISOKeyboard = isISOKeyboard;
|
||||
}
|
||||
}
|
||||
|
||||
public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper {
|
||||
if (!this._initialized) {
|
||||
this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap());
|
||||
}
|
||||
if (dispatchConfig === DispatchConfig.KeyCode) {
|
||||
// Forcefully set to use keyCode
|
||||
return new MacLinuxFallbackKeyboardMapper(OS);
|
||||
}
|
||||
return this._keyboardMapper;
|
||||
}
|
||||
|
||||
public getCurrentKeyboardLayout(): nativeKeymap.IKeyboardLayoutInfo {
|
||||
if (!this._initialized) {
|
||||
this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap());
|
||||
}
|
||||
return this._layoutInfo;
|
||||
}
|
||||
|
||||
private static _isUSStandard(_kbInfo: nativeKeymap.IKeyboardLayoutInfo): boolean {
|
||||
if (OS === OperatingSystem.Linux) {
|
||||
const kbInfo = <nativeKeymap.ILinuxKeyboardLayoutInfo>_kbInfo;
|
||||
return (kbInfo && kbInfo.layout === 'us');
|
||||
}
|
||||
|
||||
if (OS === OperatingSystem.Macintosh) {
|
||||
const kbInfo = <nativeKeymap.IMacKeyboardLayoutInfo>_kbInfo;
|
||||
return (kbInfo && kbInfo.id === 'com.apple.keylayout.US');
|
||||
}
|
||||
|
||||
if (OS === OperatingSystem.Windows) {
|
||||
const kbInfo = <nativeKeymap.IWindowsKeyboardLayoutInfo>_kbInfo;
|
||||
return (kbInfo && kbInfo.name === '00000409');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public getRawKeyboardMapping(): nativeKeymap.IKeyboardMapping {
|
||||
if (!this._initialized) {
|
||||
this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap());
|
||||
}
|
||||
return this._rawMapping;
|
||||
}
|
||||
|
||||
private _setKeyboardData(isISOKeyboard: boolean, layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): void {
|
||||
this._layoutInfo = layoutInfo;
|
||||
|
||||
if (this._initialized && this._isISOKeyboard === isISOKeyboard && KeyboardMapperFactory._equals(this._rawMapping, rawMapping)) {
|
||||
// nothing to do...
|
||||
return;
|
||||
}
|
||||
|
||||
this._initialized = true;
|
||||
this._isISOKeyboard = isISOKeyboard;
|
||||
this._rawMapping = rawMapping;
|
||||
this._keyboardMapper = KeyboardMapperFactory._createKeyboardMapper(this._isISOKeyboard, this._layoutInfo, this._rawMapping);
|
||||
this._onDidChangeKeyboardMapper.fire();
|
||||
}
|
||||
|
||||
private static _createKeyboardMapper(isISOKeyboard: boolean, layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): IKeyboardMapper {
|
||||
const isUSStandard = KeyboardMapperFactory._isUSStandard(layoutInfo);
|
||||
if (OS === OperatingSystem.Windows) {
|
||||
return new WindowsKeyboardMapper(isUSStandard, <IWindowsKeyboardMapping>rawMapping);
|
||||
}
|
||||
|
||||
if (Object.keys(rawMapping).length === 0) {
|
||||
// Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts)
|
||||
return new MacLinuxFallbackKeyboardMapper(OS);
|
||||
}
|
||||
|
||||
if (OS === OperatingSystem.Macintosh) {
|
||||
const kbInfo = <nativeKeymap.IMacKeyboardLayoutInfo>layoutInfo;
|
||||
if (kbInfo.id === 'com.apple.keylayout.DVORAK-QWERTYCMD') {
|
||||
// Use keyCode based dispatching for DVORAK - QWERTY ⌘
|
||||
return new MacLinuxFallbackKeyboardMapper(OS);
|
||||
}
|
||||
}
|
||||
|
||||
return new MacLinuxKeyboardMapper(isISOKeyboard, isUSStandard, <IMacLinuxKeyboardMapping>rawMapping, OS);
|
||||
}
|
||||
|
||||
private static _equals(a: nativeKeymap.IKeyboardMapping, b: nativeKeymap.IKeyboardMapping): boolean {
|
||||
if (OS === OperatingSystem.Windows) {
|
||||
return windowsKeyboardMappingEquals(<IWindowsKeyboardMapping>a, <IWindowsKeyboardMapping>b);
|
||||
}
|
||||
|
||||
return macLinuxKeyboardMappingEquals(<IMacLinuxKeyboardMapping>a, <IMacLinuxKeyboardMapping>b);
|
||||
}
|
||||
}
|
||||
|
||||
interface ContributedKeyBinding {
|
||||
command: string;
|
||||
key: string;
|
||||
when?: string;
|
||||
mac?: string;
|
||||
linux?: string;
|
||||
win?: string;
|
||||
}
|
||||
|
||||
function isContributedKeyBindingsArray(thing: ContributedKeyBinding | ContributedKeyBinding[]): thing is ContributedKeyBinding[] {
|
||||
return Array.isArray(thing);
|
||||
}
|
||||
|
||||
function isValidContributedKeyBinding(keyBinding: ContributedKeyBinding, rejects: string[]): boolean {
|
||||
if (!keyBinding) {
|
||||
rejects.push(nls.localize('nonempty', "expected non-empty value."));
|
||||
return false;
|
||||
}
|
||||
if (typeof keyBinding.command !== 'string') {
|
||||
rejects.push(nls.localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command'));
|
||||
return false;
|
||||
}
|
||||
if (typeof keyBinding.key !== 'string') {
|
||||
rejects.push(nls.localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'key'));
|
||||
return false;
|
||||
}
|
||||
if (keyBinding.when && typeof keyBinding.when !== 'string') {
|
||||
rejects.push(nls.localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when'));
|
||||
return false;
|
||||
}
|
||||
if (keyBinding.mac && typeof keyBinding.mac !== 'string') {
|
||||
rejects.push(nls.localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'mac'));
|
||||
return false;
|
||||
}
|
||||
if (keyBinding.linux && typeof keyBinding.linux !== 'string') {
|
||||
rejects.push(nls.localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'linux'));
|
||||
return false;
|
||||
}
|
||||
if (keyBinding.win && typeof keyBinding.win !== 'string') {
|
||||
rejects.push(nls.localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'win'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let keybindingType: IJSONSchema = {
|
||||
type: 'object',
|
||||
default: { command: '', key: '' },
|
||||
properties: {
|
||||
command: {
|
||||
description: nls.localize('vscode.extension.contributes.keybindings.command', 'Identifier of the command to run when keybinding is triggered.'),
|
||||
type: 'string'
|
||||
},
|
||||
key: {
|
||||
description: nls.localize('vscode.extension.contributes.keybindings.key', 'Key or key sequence (separate keys with plus-sign and sequences with space, e.g Ctrl+O and Ctrl+L L for a chord).'),
|
||||
type: 'string'
|
||||
},
|
||||
mac: {
|
||||
description: nls.localize('vscode.extension.contributes.keybindings.mac', 'Mac specific key or key sequence.'),
|
||||
type: 'string'
|
||||
},
|
||||
linux: {
|
||||
description: nls.localize('vscode.extension.contributes.keybindings.linux', 'Linux specific key or key sequence.'),
|
||||
type: 'string'
|
||||
},
|
||||
win: {
|
||||
description: nls.localize('vscode.extension.contributes.keybindings.win', 'Windows specific key or key sequence.'),
|
||||
type: 'string'
|
||||
},
|
||||
when: {
|
||||
description: nls.localize('vscode.extension.contributes.keybindings.when', 'Condition when the key is active.'),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let keybindingsExtPoint = ExtensionsRegistry.registerExtensionPoint<ContributedKeyBinding | ContributedKeyBinding[]>('keybindings', [], {
|
||||
description: nls.localize('vscode.extension.contributes.keybindings', "Contributes keybindings."),
|
||||
oneOf: [
|
||||
keybindingType,
|
||||
{
|
||||
type: 'array',
|
||||
items: keybindingType
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
export const enum DispatchConfig {
|
||||
Code,
|
||||
KeyCode
|
||||
}
|
||||
|
||||
function getDispatchConfig(configurationService: IConfigurationService): DispatchConfig {
|
||||
const keyboard = configurationService.getConfiguration('keyboard');
|
||||
const r = (keyboard ? (<any>keyboard).dispatch : null);
|
||||
return (r === 'keyCode' ? DispatchConfig.KeyCode : DispatchConfig.Code);
|
||||
}
|
||||
|
||||
export class WorkbenchKeybindingService extends AbstractKeybindingService {
|
||||
|
||||
private _keyboardMapper: IKeyboardMapper;
|
||||
private _cachedResolver: KeybindingResolver;
|
||||
private _firstTimeComputingResolver: boolean;
|
||||
private userKeybindings: ConfigWatcher<IUserFriendlyKeybinding[]>;
|
||||
|
||||
constructor(
|
||||
windowElement: Window,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ICommandService commandService: ICommandService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IStatusbarService statusBarService: IStatusbarService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super(contextKeyService, commandService, messageService, statusBarService);
|
||||
|
||||
let dispatchConfig = getDispatchConfig(configurationService);
|
||||
configurationService.onDidUpdateConfiguration((e) => {
|
||||
let newDispatchConfig = getDispatchConfig(configurationService);
|
||||
if (dispatchConfig === newDispatchConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatchConfig = newDispatchConfig;
|
||||
this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig);
|
||||
this.updateResolver({ source: KeybindingSource.Default });
|
||||
});
|
||||
|
||||
this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig);
|
||||
KeyboardMapperFactory.INSTANCE.onDidChangeKeyboardMapper(() => {
|
||||
this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig);
|
||||
this.updateResolver({ source: KeybindingSource.Default });
|
||||
});
|
||||
|
||||
this._cachedResolver = null;
|
||||
this._firstTimeComputingResolver = true;
|
||||
|
||||
this.userKeybindings = new ConfigWatcher(environmentService.appKeybindingsPath, { defaultConfig: [], onError: error => onUnexpectedError(error) });
|
||||
this.toDispose.push(toDisposable(() => this.userKeybindings.dispose()));
|
||||
|
||||
keybindingsExtPoint.setHandler((extensions) => {
|
||||
let commandAdded = false;
|
||||
|
||||
for (let extension of extensions) {
|
||||
commandAdded = this._handleKeybindingsExtensionPointUser(extension.description.isBuiltin, extension.value, extension.collector) || commandAdded;
|
||||
}
|
||||
|
||||
if (commandAdded) {
|
||||
this.updateResolver({ source: KeybindingSource.Default });
|
||||
}
|
||||
});
|
||||
|
||||
this.toDispose.push(this.userKeybindings.onDidUpdateConfiguration(event => this.updateResolver({
|
||||
source: KeybindingSource.User,
|
||||
keybindings: event.config
|
||||
})));
|
||||
|
||||
this.toDispose.push(dom.addDisposableListener(windowElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
let keyEvent = new StandardKeyboardEvent(e);
|
||||
let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target);
|
||||
if (shouldPreventDefault) {
|
||||
keyEvent.preventDefault();
|
||||
}
|
||||
}));
|
||||
|
||||
keybindingsTelemetry(telemetryService, this);
|
||||
let data = KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout();
|
||||
telemetryService.publicLog('keyboardLayout', {
|
||||
currentKeyboardLayout: data
|
||||
});
|
||||
}
|
||||
|
||||
public dumpDebugInfo(): string {
|
||||
const layoutInfo = JSON.stringify(KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(), null, '\t');
|
||||
const mapperInfo = this._keyboardMapper.dumpDebugInfo();
|
||||
const rawMapping = JSON.stringify(KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(), null, '\t');
|
||||
return `Layout info:\n${layoutInfo}\n${mapperInfo}\n\nRaw mapping:\n${rawMapping}`;
|
||||
}
|
||||
|
||||
private _safeGetConfig(): IUserFriendlyKeybinding[] {
|
||||
let rawConfig = this.userKeybindings.getConfig();
|
||||
if (Array.isArray(rawConfig)) {
|
||||
return rawConfig;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public customKeybindingsCount(): number {
|
||||
let userKeybindings = this._safeGetConfig();
|
||||
|
||||
return userKeybindings.length;
|
||||
}
|
||||
|
||||
private updateResolver(event: IKeybindingEvent): void {
|
||||
this._cachedResolver = null;
|
||||
this._onDidUpdateKeybindings.fire(event);
|
||||
}
|
||||
|
||||
protected _getResolver(): KeybindingResolver {
|
||||
if (!this._cachedResolver) {
|
||||
const defaults = this._resolveKeybindingItems(KeybindingsRegistry.getDefaultKeybindings(), true);
|
||||
const overrides = this._resolveUserKeybindingItems(this._getExtraKeybindings(this._firstTimeComputingResolver), false);
|
||||
this._cachedResolver = new KeybindingResolver(defaults, overrides);
|
||||
this._firstTimeComputingResolver = false;
|
||||
}
|
||||
return this._cachedResolver;
|
||||
}
|
||||
|
||||
private _resolveKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] {
|
||||
let result: ResolvedKeybindingItem[] = [], resultLen = 0;
|
||||
for (let i = 0, len = items.length; i < len; i++) {
|
||||
const item = items[i];
|
||||
const when = (item.when ? item.when.normalize() : null);
|
||||
const keybinding = item.keybinding;
|
||||
if (!keybinding) {
|
||||
// This might be a removal keybinding item in user settings => accept it
|
||||
result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault);
|
||||
} else {
|
||||
const resolvedKeybindings = this.resolveKeybinding(keybinding);
|
||||
for (let j = 0; j < resolvedKeybindings.length; j++) {
|
||||
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybindings[j], item.command, item.commandArgs, when, isDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _resolveUserKeybindingItems(items: IUserKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] {
|
||||
let result: ResolvedKeybindingItem[] = [], resultLen = 0;
|
||||
for (let i = 0, len = items.length; i < len; i++) {
|
||||
const item = items[i];
|
||||
const when = (item.when ? item.when.normalize() : null);
|
||||
const firstPart = item.firstPart;
|
||||
const chordPart = item.chordPart;
|
||||
if (!firstPart) {
|
||||
// This might be a removal keybinding item in user settings => accept it
|
||||
result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault);
|
||||
} else {
|
||||
const resolvedKeybindings = this._keyboardMapper.resolveUserBinding(firstPart, chordPart);
|
||||
for (let j = 0; j < resolvedKeybindings.length; j++) {
|
||||
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybindings[j], item.command, item.commandArgs, when, isDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _getExtraKeybindings(isFirstTime: boolean): IUserKeybindingItem[] {
|
||||
let extraUserKeybindings: IUserFriendlyKeybinding[] = this._safeGetConfig();
|
||||
if (!isFirstTime) {
|
||||
let cnt = extraUserKeybindings.length;
|
||||
|
||||
this.telemetryService.publicLog('customKeybindingsChanged', {
|
||||
keyCount: cnt
|
||||
});
|
||||
}
|
||||
|
||||
return extraUserKeybindings.map((k) => KeybindingIO.readUserKeybindingItem(k, OS));
|
||||
}
|
||||
|
||||
public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] {
|
||||
return this._keyboardMapper.resolveKeybinding(kb);
|
||||
}
|
||||
|
||||
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
|
||||
return this._keyboardMapper.resolveKeyboardEvent(keyboardEvent);
|
||||
}
|
||||
|
||||
public resolveUserBinding(userBinding: string): ResolvedKeybinding[] {
|
||||
const [firstPart, chordPart] = KeybindingIO._readUserBinding(userBinding);
|
||||
return this._keyboardMapper.resolveUserBinding(firstPart, chordPart);
|
||||
}
|
||||
|
||||
private _handleKeybindingsExtensionPointUser(isBuiltin: boolean, keybindings: ContributedKeyBinding | ContributedKeyBinding[], collector: ExtensionMessageCollector): boolean {
|
||||
if (isContributedKeyBindingsArray(keybindings)) {
|
||||
let commandAdded = false;
|
||||
for (let i = 0, len = keybindings.length; i < len; i++) {
|
||||
commandAdded = this._handleKeybinding(isBuiltin, i + 1, keybindings[i], collector) || commandAdded;
|
||||
}
|
||||
return commandAdded;
|
||||
} else {
|
||||
return this._handleKeybinding(isBuiltin, 1, keybindings, collector);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleKeybinding(isBuiltin: boolean, idx: number, keybindings: ContributedKeyBinding, collector: ExtensionMessageCollector): boolean {
|
||||
|
||||
let rejects: string[] = [];
|
||||
let commandAdded = false;
|
||||
|
||||
if (isValidContributedKeyBinding(keybindings, rejects)) {
|
||||
let rule = this._asCommandRule(isBuiltin, idx++, keybindings);
|
||||
if (rule) {
|
||||
KeybindingsRegistry.registerKeybindingRule2(rule);
|
||||
commandAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rejects.length > 0) {
|
||||
collector.error(nls.localize(
|
||||
'invalid.keybindings',
|
||||
"Invalid `contributes.{0}`: {1}",
|
||||
keybindingsExtPoint.name,
|
||||
rejects.join('\n')
|
||||
));
|
||||
}
|
||||
|
||||
return commandAdded;
|
||||
}
|
||||
|
||||
private _asCommandRule(isBuiltin: boolean, idx: number, binding: ContributedKeyBinding): IKeybindingRule2 {
|
||||
|
||||
let { command, when, key, mac, linux, win } = binding;
|
||||
|
||||
let weight: number;
|
||||
if (isBuiltin) {
|
||||
weight = KeybindingsRegistry.WEIGHT.builtinExtension(idx);
|
||||
} else {
|
||||
weight = KeybindingsRegistry.WEIGHT.externalExtension(idx);
|
||||
}
|
||||
|
||||
let desc = {
|
||||
id: command,
|
||||
when: ContextKeyExpr.deserialize(when),
|
||||
weight: weight,
|
||||
primary: KeybindingIO.readKeybinding(key, OS),
|
||||
mac: mac && { primary: KeybindingIO.readKeybinding(mac, OS) },
|
||||
linux: linux && { primary: KeybindingIO.readKeybinding(linux, OS) },
|
||||
win: win && { primary: KeybindingIO.readKeybinding(win, OS) }
|
||||
};
|
||||
|
||||
if (!desc.primary && !desc.mac && !desc.linux && !desc.win) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
public getDefaultKeybindingsContent(): string {
|
||||
const resolver = this._getResolver();
|
||||
const defaultKeybindings = resolver.getDefaultKeybindings();
|
||||
const boundCommands = resolver.getDefaultBoundCommands();
|
||||
return (
|
||||
WorkbenchKeybindingService._getDefaultKeybindings(defaultKeybindings)
|
||||
+ '\n\n'
|
||||
+ WorkbenchKeybindingService._getAllCommandsAsComment(boundCommands)
|
||||
);
|
||||
}
|
||||
|
||||
private static _getDefaultKeybindings(defaultKeybindings: ResolvedKeybindingItem[]): string {
|
||||
let out = new OutputBuilder();
|
||||
out.writeLine('[');
|
||||
|
||||
let lastIndex = defaultKeybindings.length - 1;
|
||||
defaultKeybindings.forEach((k, index) => {
|
||||
KeybindingIO.writeKeybindingItem(out, k, OS);
|
||||
if (index !== lastIndex) {
|
||||
out.writeLine(',');
|
||||
} else {
|
||||
out.writeLine();
|
||||
}
|
||||
});
|
||||
out.writeLine(']');
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private static _getAllCommandsAsComment(boundCommands: Map<string, boolean>): string {
|
||||
const unboundCommands = KeybindingResolver.getAllUnboundCommands(boundCommands);
|
||||
let pretty = unboundCommands.sort().join('\n// - ');
|
||||
return '// ' + nls.localize('unboundCommands', "Here are other available commands: ") + '\n// - ' + pretty;
|
||||
}
|
||||
}
|
||||
|
||||
let schemaId = 'vscode://schemas/keybindings';
|
||||
let schema: IJSONSchema = {
|
||||
'id': schemaId,
|
||||
'type': 'array',
|
||||
'title': nls.localize('keybindings.json.title', "Keybindings configuration"),
|
||||
'items': {
|
||||
'required': ['key'],
|
||||
'type': 'object',
|
||||
'defaultSnippets': [{ 'body': { 'key': '$1', 'command': '$2', 'when': '$3' } }],
|
||||
'properties': {
|
||||
'key': {
|
||||
'type': 'string',
|
||||
'description': nls.localize('keybindings.json.key', "Key or key sequence (separated by space)"),
|
||||
},
|
||||
'command': {
|
||||
'description': nls.localize('keybindings.json.command', "Name of the command to execute"),
|
||||
},
|
||||
'when': {
|
||||
'type': 'string',
|
||||
'description': nls.localize('keybindings.json.when', "Condition when the key is active.")
|
||||
},
|
||||
'args': {
|
||||
'description': nls.localize('keybindings.json.args', "Arguments to pass to the command to execute.")
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let schemaRegistry = <IJSONContributionRegistry>Registry.as(Extensions.JSONContribution);
|
||||
schemaRegistry.registerSchema(schemaId, schema);
|
||||
|
||||
if (OS === OperatingSystem.Macintosh || OS === OperatingSystem.Linux) {
|
||||
|
||||
const configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigExtensions.Configuration);
|
||||
const keyboardConfiguration: IConfigurationNode = {
|
||||
'id': 'keyboard',
|
||||
'order': 15,
|
||||
'type': 'object',
|
||||
'title': nls.localize('keyboardConfigurationTitle', "Keyboard"),
|
||||
'overridable': true,
|
||||
'properties': {
|
||||
'keyboard.dispatch': {
|
||||
'type': 'string',
|
||||
'enum': ['code', 'keyCode'],
|
||||
'default': 'code',
|
||||
'description': nls.localize('dispatch', "Controls the dispatching logic for key presses to use either `keydown.code` (recommended) or `keydown.keyCode`.")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
configurationRegistry.registerConfiguration(keyboardConfiguration);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user