Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'

This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
4649 changed files with 1311795 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export const enum DispatchConfig {
Code,
KeyCode
}
export function getDispatchConfig(configurationService: IConfigurationService): DispatchConfig {
const keyboard = configurationService.getValue('keyboard');
const r = (keyboard ? (<any>keyboard).dispatch : null);
return (r === 'keyCode' ? DispatchConfig.KeyCode : DispatchConfig.Code);
}

View File

@@ -0,0 +1,288 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { Queue } from 'vs/base/common/async';
import * as json from 'vs/base/common/json';
import * as objects from 'vs/base/common/objects';
import { setProperty } from 'vs/base/common/jsonEdit';
import { Edit } from 'vs/base/common/jsonFormatter';
import { Disposable, IReference } from 'vs/base/common/lifecycle';
import { isArray } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
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 { ITextModel } from 'vs/editor/common/model';
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export const IKeybindingEditingService = createDecorator<IKeybindingEditingService>('keybindingEditingService');
export interface IKeybindingEditingService {
readonly _serviceBrand: undefined;
editKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void>;
removeKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void>;
resetKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void>;
}
export class KeybindingsEditingService extends Disposable implements IKeybindingEditingService {
public _serviceBrand: undefined;
private queue: Queue<void>;
private resource: URI = this.environmentService.keybindingsResource;
constructor(
@ITextModelService private readonly textModelResolverService: ITextModelService,
@ITextFileService private readonly textFileService: ITextFileService,
@IFileService private readonly fileService: IFileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IEnvironmentService private readonly environmentService: IEnvironmentService
) {
super();
this.queue = new Queue<void>();
}
editKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void> {
return this.queue.queue(() => this.doEditKeybinding(keybindingItem, key, when)); // queue up writes to prevent race conditions
}
resetKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> {
return this.queue.queue(() => this.doResetKeybinding(keybindingItem)); // queue up writes to prevent race conditions
}
removeKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> {
return this.queue.queue(() => this.doRemoveKeybinding(keybindingItem)); // queue up writes to prevent race conditions
}
private doEditKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void> {
return this.resolveAndValidate()
.then(reference => {
const model = reference.object.textEditorModel;
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries);
this.updateKeybinding(keybindingItem, key, when, model, userKeybindingEntryIndex);
if (keybindingItem.isDefault && keybindingItem.resolvedKeybinding) {
this.removeDefaultKeybinding(keybindingItem, model);
}
return this.save().finally(() => reference.dispose());
});
}
private doRemoveKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<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().finally(() => reference.dispose());
});
}
private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<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().finally(() => reference.dispose());
});
}
private save(): Promise<any> {
return this.textFileService.save(this.resource);
}
private updateKeybinding(keybindingItem: ResolvedKeybindingItem, newKey: string, when: string | undefined, model: ITextModel, userKeybindingEntryIndex: number): void {
const { tabSize, insertSpaces } = model.getOptions();
const eol = model.getEOL();
if (userKeybindingEntryIndex !== -1) {
// Update the keybinding with new key
this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex, 'key'], newKey, { tabSize, insertSpaces, eol })[0], model);
const edits = setProperty(model.getValue(), [userKeybindingEntryIndex, 'when'], when, { tabSize, insertSpaces, eol });
if (edits.length > 0) {
this.applyEditsToBuffer(edits[0], model);
}
} else {
// Add the new keybinding with new key
this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(newKey, keybindingItem.command, when, false), { tabSize, insertSpaces, eol })[0], model);
}
}
private removeUserKeybinding(keybindingItem: ResolvedKeybindingItem, model: ITextModel): 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], undefined, { tabSize, insertSpaces, eol })[0], model);
}
}
private removeDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: ITextModel): void {
const { tabSize, insertSpaces } = model.getOptions();
const eol = model.getEOL();
const key = keybindingItem.resolvedKeybinding ? keybindingItem.resolvedKeybinding.getUserSettingsLabel() : null;
if (key) {
const entry: IUserFriendlyKeybinding = this.asObject(key, keybindingItem.command, keybindingItem.when ? keybindingItem.when.serialize() : undefined, true);
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
if (userKeybindingEntries.every(e => !this.areSame(e, entry))) {
this.applyEditsToBuffer(setProperty(model.getValue(), [-1], entry, { tabSize, insertSpaces, eol })[0], model);
}
}
}
private removeUnassignedDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: ITextModel): void {
const { tabSize, insertSpaces } = model.getOptions();
const eol = model.getEOL();
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
const indices = this.findUnassignedDefaultKeybindingEntryIndex(keybindingItem, userKeybindingEntries).reverse();
for (const index of indices) {
this.applyEditsToBuffer(setProperty(model.getValue(), [index], undefined, { 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) {
const contextKeyExpr = ContextKeyExpr.deserialize(keybinding.when);
if (contextKeyExpr && contextKeyExpr.serialize() === keybindingItem.when.serialize()) {
return index;
}
}
}
}
return -1;
}
private findUnassignedDefaultKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number[] {
const indices: number[] = [];
for (let index = 0; index < userKeybindingEntries.length; index++) {
if (userKeybindingEntries[index].command === `-${keybindingItem.command}`) {
indices.push(index);
}
}
return indices;
}
private asObject(key: string, command: string | null, when: string | undefined, negate: boolean): any {
const object: any = { key };
if (command) {
object['command'] = negate ? `-${command}` : command;
}
if (when) {
object['when'] = when;
}
return object;
}
private areSame(a: IUserFriendlyKeybinding, b: IUserFriendlyKeybinding): boolean {
if (a.command !== b.command) {
return false;
}
if (a.key !== b.key) {
return false;
}
const whenA = ContextKeyExpr.deserialize(a.when);
const whenB = ContextKeyExpr.deserialize(b.when);
if ((whenA && !whenB) || (!whenA && whenB)) {
return false;
}
if (whenA && whenB && !whenA.equals(whenB)) {
return false;
}
if (!objects.equals(a.args, b.args)) {
return false;
}
return true;
}
private applyEditsToBuffer(edit: Edit, model: ITextModel): 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(): Promise<IReference<IResolvedTextEditorModel>> {
return this.fileService.exists(this.resource)
.then(exists => {
const EOL = this.configurationService.getValue<{ eol: string }>('files', { overrideIdentifier: 'json' })['eol'];
const result: Promise<any> = exists ? Promise.resolve(null) : this.textFileService.write(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' });
return result.then(() => this.textModelResolverService.createModelReference(this.resource));
});
}
private resolveAndValidate(): Promise<IReference<IResolvedTextEditorModel>> {
// Target cannot be dirty if not writing into buffer
if (this.textFileService.isDirty(this.resource)) {
return Promise.reject(new Error(localize('errorKeybindingsFileDirty', "Unable to write because the keybindings configuration file is dirty. Please save it first and then 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) {
reference.dispose();
return Promise.reject<any>(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again.")));
}
if (parsed.result) {
if (!isArray(parsed.result)) {
reference.dispose();
return Promise.reject<any>(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It 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: ITextModel): { result: IUserFriendlyKeybinding[], parseErrors: json.ParseError[] } {
const parseErrors: json.ParseError[] = [];
const result = json.parse(model.getValue(), parseErrors, { allowTrailingComma: true, allowEmptyContent: true });
return { result, parseErrors };
}
private getEmptyContent(EOL: string): string {
return '// ' + localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + EOL + '[]';
}
}
registerSingleton(IKeybindingEditingService, KeybindingsEditingService, true);

View File

@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SimpleKeybinding } from 'vs/base/common/keyCodes';
import { KeybindingParser } from 'vs/base/common/keybindingParser';
import { ScanCodeBinding } from 'vs/base/common/scanCode';
import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
export interface IUserKeybindingItem {
parts: (SimpleKeybinding | ScanCodeBinding)[];
command: string | null;
commandArgs?: any;
when: ContextKeyExpression | undefined;
}
export class KeybindingIO {
public static writeKeybindingItem(out: OutputBuilder, item: ResolvedKeybindingItem): void {
if (!item.resolvedKeybinding) {
return;
}
let quotedSerializedKeybinding = JSON.stringify(item.resolvedKeybinding.getUserSettingsLabel());
out.write(`{ "key": ${rightPaddedString(quotedSerializedKeybinding + ',', 25)} "command": `);
let quotedSerializedWhen = item.when ? JSON.stringify(item.when.serialize()) : '';
let quotedSerializeCommand = JSON.stringify(item.command);
if (quotedSerializedWhen.length > 0) {
out.write(`${quotedSerializeCommand},`);
out.writeLine();
out.write(` "when": ${quotedSerializedWhen}`);
} else {
out.write(`${quotedSerializeCommand}`);
}
if (item.commandArgs) {
out.write(',');
out.writeLine();
out.write(` "args": ${JSON.stringify(item.commandArgs)}`);
}
out.write(' }');
}
public static readUserKeybindingItem(input: IUserFriendlyKeybinding): IUserKeybindingItem {
const parts = (typeof input.key === 'string' ? KeybindingParser.parseUserBinding(input.key) : []);
const when = (typeof input.when === 'string' ? ContextKeyExpr.deserialize(input.when) : undefined);
const command = (typeof input.command === 'string' ? input.command : null);
const commandArgs = (typeof input.args !== 'undefined' ? input.args : undefined);
return {
parts: parts,
command: command,
commandArgs: commandArgs,
when: when
};
}
}
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');
}
}

View File

@@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
import { ScanCodeBinding } from 'vs/base/common/scanCode';
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
export interface IKeyboardMapper {
dumpDebugInfo(): string;
resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
resolveUserBinding(firstPart: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[];
}
export class CachedKeyboardMapper implements IKeyboardMapper {
private _actual: IKeyboardMapper;
private _cache: Map<string, ResolvedKeybinding[]>;
constructor(actual: IKeyboardMapper) {
this._actual = actual;
this._cache = new Map<string, ResolvedKeybinding[]>();
}
public dumpDebugInfo(): string {
return this._actual.dumpDebugInfo();
}
public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] {
const hashCode = keybinding.getHashCode();
const resolved = this._cache.get(hashCode);
if (!resolved) {
const r = this._actual.resolveKeybinding(keybinding);
this._cache.set(hashCode, r);
return r;
}
return resolved;
}
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
return this._actual.resolveKeyboardEvent(keyboardEvent);
}
public resolveUserBinding(parts: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] {
return this._actual.resolveUserBinding(parts);
}
}

View File

@@ -0,0 +1,343 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig';
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;
}
export interface IWindowsKeyboardMapping {
[code: string]: IWindowsKeyMapping;
}
export interface ILinuxKeyMapping {
value: string;
withShift: string;
withAltGr: string;
withShiftAltGr: string;
}
export interface ILinuxKeyboardMapping {
[code: string]: ILinuxKeyMapping;
}
export interface IMacKeyMapping {
value: string;
withShift: string;
withAltGr: string;
withShiftAltGr: string;
valueIsDeadKey: boolean;
withShiftIsDeadKey: boolean;
withAltGrIsDeadKey: boolean;
withShiftAltGrIsDeadKey: boolean;
}
export interface IMacKeyboardMapping {
[code: string]: IMacKeyMapping;
}
export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping;
/* __GDPR__FRAGMENT__
"IKeyboardLayoutInfo" : {
"name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
export interface IWindowsKeyboardLayoutInfo {
name: string;
id: string;
text: string;
}
/* __GDPR__FRAGMENT__
"IKeyboardLayoutInfo" : {
"model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
export interface ILinuxKeyboardLayoutInfo {
model: string;
layout: string;
variant: string;
options: string;
rules: string;
}
/* __GDPR__FRAGMENT__
"IKeyboardLayoutInfo" : {
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"localizedName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
export interface IMacKeyboardLayoutInfo {
id: string;
lang: string;
localizedName?: string;
}
export type IKeyboardLayoutInfo = (IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo) & { isUserKeyboardLayout?: boolean; isUSStandard?: true };
export const IKeymapService = createDecorator<IKeymapService>('keymapService');
export interface IKeymapService {
readonly _serviceBrand: undefined;
onDidChangeKeyboardMapper: Event<void>;
getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper;
getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null;
getAllKeyboardLayouts(): IKeyboardLayoutInfo[];
getRawKeyboardMapping(): IKeyboardMapping | null;
validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void;
}
export function areKeyboardLayoutsEqual(a: IKeyboardLayoutInfo | null, b: IKeyboardLayoutInfo | null): boolean {
if (!a || !b) {
return false;
}
if ((<IWindowsKeyboardLayoutInfo>a).name && (<IWindowsKeyboardLayoutInfo>b).name && (<IWindowsKeyboardLayoutInfo>a).name === (<IWindowsKeyboardLayoutInfo>b).name) {
return true;
}
if ((<IMacKeyboardLayoutInfo>a).id && (<IMacKeyboardLayoutInfo>b).id && (<IMacKeyboardLayoutInfo>a).id === (<IMacKeyboardLayoutInfo>b).id) {
return true;
}
if ((<ILinuxKeyboardLayoutInfo>a).model &&
(<ILinuxKeyboardLayoutInfo>b).model &&
(<ILinuxKeyboardLayoutInfo>a).model === (<ILinuxKeyboardLayoutInfo>b).model &&
(<ILinuxKeyboardLayoutInfo>a).layout === (<ILinuxKeyboardLayoutInfo>b).layout
) {
return true;
}
return false;
}
export function parseKeyboardLayoutDescription(layout: IKeyboardLayoutInfo | null): { label: string, description: string } {
if (!layout) {
return { label: '', description: '' };
}
if ((<IWindowsKeyboardLayoutInfo>layout).name) {
// windows
let windowsLayout = <IWindowsKeyboardLayoutInfo>layout;
return {
label: windowsLayout.text,
description: ''
};
}
if ((<IMacKeyboardLayoutInfo>layout).id) {
let macLayout = <IMacKeyboardLayoutInfo>layout;
if (macLayout.localizedName) {
return {
label: macLayout.localizedName,
description: ''
};
}
if (/^com\.apple\.keylayout\./.test(macLayout.id)) {
return {
label: macLayout.id.replace(/^com\.apple\.keylayout\./, '').replace(/-/, ' '),
description: ''
};
}
if (/^.*inputmethod\./.test(macLayout.id)) {
return {
label: macLayout.id.replace(/^.*inputmethod\./, '').replace(/[-\.]/, ' '),
description: `Input Method (${macLayout.lang})`
};
}
return {
label: macLayout.lang,
description: ''
};
}
let linuxLayout = <ILinuxKeyboardLayoutInfo>layout;
return {
label: linuxLayout.layout,
description: ''
};
}
export function getKeyboardLayoutId(layout: IKeyboardLayoutInfo): string {
if ((<IWindowsKeyboardLayoutInfo>layout).name) {
return (<IWindowsKeyboardLayoutInfo>layout).name;
}
if ((<IMacKeyboardLayoutInfo>layout).id) {
return (<IMacKeyboardLayoutInfo>layout).id;
}
return (<ILinuxKeyboardLayoutInfo>layout).layout;
}
function deserializeMapping(serializedMapping: ISerializedMapping) {
let mapping = serializedMapping;
let ret: { [key: string]: any } = {};
for (let key in mapping) {
let result: (string | number)[] = mapping[key];
if (result.length) {
let value = result[0];
let withShift = result[1];
let withAltGr = result[2];
let withShiftAltGr = result[3];
let mask = Number(result[4]);
let vkey = result.length === 6 ? result[5] : undefined;
ret[key] = {
'value': value,
'vkey': vkey,
'withShift': withShift,
'withAltGr': withAltGr,
'withShiftAltGr': withShiftAltGr,
'valueIsDeadKey': (mask & 1) > 0,
'withShiftIsDeadKey': (mask & 2) > 0,
'withAltGrIsDeadKey': (mask & 4) > 0,
'withShiftAltGrIsDeadKey': (mask & 8) > 0
};
} else {
ret[key] = {
'value': '',
'valueIsDeadKey': false,
'withShift': '',
'withShiftIsDeadKey': false,
'withAltGr': '',
'withAltGrIsDeadKey': false,
'withShiftAltGr': '',
'withShiftAltGrIsDeadKey': false
};
}
}
return ret;
}
export interface IRawMixedKeyboardMapping {
[key: string]: {
value: string,
withShift: string;
withAltGr: string;
withShiftAltGr: string;
valueIsDeadKey?: boolean;
withShiftIsDeadKey?: boolean;
withAltGrIsDeadKey?: boolean;
withShiftAltGrIsDeadKey?: boolean;
};
}
interface ISerializedMapping {
[key: string]: (string | number)[];
}
export interface IKeymapInfo {
layout: IKeyboardLayoutInfo;
secondaryLayouts: IKeyboardLayoutInfo[];
mapping: ISerializedMapping;
isUserKeyboardLayout?: boolean;
}
export class KeymapInfo {
mapping: IRawMixedKeyboardMapping;
isUserKeyboardLayout: boolean;
constructor(public layout: IKeyboardLayoutInfo, public secondaryLayouts: IKeyboardLayoutInfo[], keyboardMapping: ISerializedMapping, isUserKeyboardLayout?: boolean) {
this.mapping = deserializeMapping(keyboardMapping);
this.isUserKeyboardLayout = !!isUserKeyboardLayout;
this.layout.isUserKeyboardLayout = !!isUserKeyboardLayout;
}
static createKeyboardLayoutFromDebugInfo(layout: IKeyboardLayoutInfo, value: IRawMixedKeyboardMapping, isUserKeyboardLayout?: boolean): KeymapInfo {
let keyboardLayoutInfo = new KeymapInfo(layout, [], {}, true);
keyboardLayoutInfo.mapping = value;
return keyboardLayoutInfo;
}
update(other: KeymapInfo) {
this.layout = other.layout;
this.secondaryLayouts = other.secondaryLayouts;
this.mapping = other.mapping;
this.isUserKeyboardLayout = other.isUserKeyboardLayout;
this.layout.isUserKeyboardLayout = other.isUserKeyboardLayout;
}
getScore(other: IRawMixedKeyboardMapping): number {
let score = 0;
for (let key in other) {
if (isWindows && (key === 'Backslash' || key === 'KeyQ')) {
// keymap from Chromium is probably wrong.
continue;
}
if (isLinux && (key === 'Backspace' || key === 'Escape')) {
// native keymap doesn't align with keyboard event
continue;
}
let currentMapping = this.mapping[key];
if (currentMapping === undefined) {
score -= 1;
}
let otherMapping = other[key];
if (currentMapping && otherMapping && currentMapping.value !== otherMapping.value) {
score -= 1;
}
}
return score;
}
equal(other: KeymapInfo): boolean {
if (this.isUserKeyboardLayout !== other.isUserKeyboardLayout) {
return false;
}
if (getKeyboardLayoutId(this.layout) !== getKeyboardLayoutId(other.layout)) {
return false;
}
return this.fuzzyEqual(other.mapping);
}
fuzzyEqual(other: IRawMixedKeyboardMapping): boolean {
for (let key in other) {
if (isWindows && (key === 'Backslash' || key === 'KeyQ')) {
// keymap from Chromium is probably wrong.
continue;
}
if (this.mapping[key] === undefined) {
return false;
}
let currentMapping = this.mapping[key];
let otherMapping = other[key];
if (currentMapping.value !== otherMapping.value) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,128 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChordKeybinding, KeyCode, Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
import { OperatingSystem } from 'vs/base/common/platform';
import { IMMUTABLE_CODE_TO_KEY_CODE, ScanCode, ScanCodeBinding } from 'vs/base/common/scanCode';
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
import { removeElementsAfterNulls } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
/**
* 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.toChord(), 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 | null): SimpleKeybinding | null {
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(input: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] {
const parts: SimpleKeybinding[] = removeElementsAfterNulls(input.map(keybinding => this._resolveSimpleUserBinding(keybinding)));
if (parts.length > 0) {
return [new USLayoutResolvedKeybinding(new ChordKeybinding(parts), this._OS)];
}
return [];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,684 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CharCode } from 'vs/base/common/charCode';
import { KeyCode, KeyCodeUtils, Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
import { UILabelProvider } from 'vs/base/common/keybindingLabels';
import { OperatingSystem } from 'vs/base/common/platform';
import { IMMUTABLE_CODE_TO_KEY_CODE, ScanCode, ScanCodeBinding, ScanCodeUtils } from 'vs/base/common/scanCode';
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
import { BaseResolvedKeybinding } from 'vs/platform/keybinding/common/baseResolvedKeybinding';
import { removeElementsAfterNulls } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
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 | null, b: IWindowsKeyboardMapping | null): 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 BaseResolvedKeybinding<SimpleKeybinding> {
private readonly _mapper: WindowsKeyboardMapper;
constructor(mapper: WindowsKeyboardMapper, parts: SimpleKeybinding[]) {
super(OperatingSystem.Windows, parts);
this._mapper = mapper;
}
protected _getLabel(keybinding: SimpleKeybinding): string | null {
if (keybinding.isDuplicateModifierCase()) {
return '';
}
return this._mapper.getUILabelForKeyCode(keybinding.keyCode);
}
private _getUSLabelForKeybinding(keybinding: SimpleKeybinding): string | null {
if (keybinding.isDuplicateModifierCase()) {
return '';
}
return KeyCodeUtils.toString(keybinding.keyCode);
}
public getUSLabel(): string | null {
return UILabelProvider.toLabel(this._os, this._parts, (keybinding) => this._getUSLabelForKeybinding(keybinding));
}
protected _getAriaLabel(keybinding: SimpleKeybinding): string | null {
if (keybinding.isDuplicateModifierCase()) {
return '';
}
return this._mapper.getAriaLabelForKeyCode(keybinding.keyCode);
}
private _keyCodeToElectronAccelerator(keyCode: KeyCode): string | null {
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);
}
protected _getElectronAccelerator(keybinding: SimpleKeybinding): string | null {
if (keybinding.isDuplicateModifierCase()) {
return null;
}
return this._keyCodeToElectronAccelerator(keybinding.keyCode);
}
protected _getUserSettingsLabel(keybinding: SimpleKeybinding): string | null {
if (keybinding.isDuplicateModifierCase()) {
return '';
}
const result = this._mapper.getUserSettingsLabelForKeyCode(keybinding.keyCode);
return (result ? result.toLowerCase() : result);
}
protected _isWYSIWYG(keybinding: SimpleKeybinding): boolean {
return this.__isWYSIWYG(keybinding.keyCode);
}
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);
}
protected _getDispatchPart(keybinding: SimpleKeybinding): string | null {
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 | null {
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: Array<string | null> = [];
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[] = [];
let producesLetters = false;
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;
producesLetters = true;
this._keyCodeToLabel[keyCode] = String.fromCharCode(CharCode.A + (charCode - CharCode.a));
}
else if (charCode >= CharCode.A && charCode <= CharCode.Z) {
producesLetter[charCode] = true;
producesLetters = 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);
if (!producesLetters) {
// Since this keyboard layout produces no latin letters at all, most of the UI will use the
// US kb layout equivalent for UI labels, so also try to render other keys with the US labels
// for consistency...
const _registerLabel = (keyCode: KeyCode, charCode: CharCode): void => {
// const existingLabel = this._keyCodeToLabel[keyCode];
// const existingCharCode = (existingLabel ? existingLabel.charCodeAt(0) : CharCode.Null);
// if (existingCharCode < 32 || existingCharCode > 126) {
this._keyCodeToLabel[keyCode] = String.fromCharCode(charCode);
// }
};
_registerLabel(KeyCode.US_SEMICOLON, CharCode.Semicolon);
_registerLabel(KeyCode.US_EQUAL, CharCode.Equals);
_registerLabel(KeyCode.US_COMMA, CharCode.Comma);
_registerLabel(KeyCode.US_MINUS, CharCode.Dash);
_registerLabel(KeyCode.US_DOT, CharCode.Period);
_registerLabel(KeyCode.US_SLASH, CharCode.Slash);
_registerLabel(KeyCode.US_BACKTICK, CharCode.BackTick);
_registerLabel(KeyCode.US_OPEN_SQUARE_BRACKET, CharCode.OpenSquareBracket);
_registerLabel(KeyCode.US_BACKSLASH, CharCode.Backslash);
_registerLabel(KeyCode.US_CLOSE_SQUARE_BRACKET, CharCode.CloseSquareBracket);
_registerLabel(KeyCode.US_QUOTE, CharCode.SingleQuote);
}
}
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);
const mods = [0b000, 0b010, 0b101, 0b111];
for (const mod of mods) {
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);
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 | null, 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[] {
const parts = keybinding.parts;
for (let i = 0, len = parts.length; i < len; i++) {
const part = parts[i];
if (!this._keyCodeExists[part.keyCode]) {
return [];
}
}
return [new WindowsNativeResolvedKeybinding(this, parts)];
}
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): WindowsNativeResolvedKeybinding {
const keybinding = new SimpleKeybinding(keyboardEvent.ctrlKey, keyboardEvent.shiftKey, keyboardEvent.altKey, keyboardEvent.metaKey, keyboardEvent.keyCode);
return new WindowsNativeResolvedKeybinding(this, [keybinding]);
}
private _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding | null): SimpleKeybinding | null {
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(input: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] {
const parts: SimpleKeybinding[] = removeElementsAfterNulls(input.map(keybinding => this._resolveSimpleUserBinding(keybinding)));
if (parts.length > 0) {
return [new WindowsNativeResolvedKeybinding(this, parts)];
}
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,
};
}