You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

124 lines
3.9 KiB

'use strict';
import { commands, Disposable } from 'vscode';
import { CommandContext, extensionId, setCommandContext } from './constants';
import { Logger } from './logger';
export declare interface KeyCommand {
onDidPressKey?(key: Keys): Promise<{} | undefined>;
}
const keyNoopCommand = Object.create(null) as KeyCommand;
export { keyNoopCommand as KeyNoopCommand };
export declare type Keys = 'left' | 'right' | ',' | '.' | 'escape';
export const keys: Keys[] = ['left', 'right', ',', '.', 'escape'];
export declare interface KeyMapping {
[id: string]: KeyCommand | (() => Promise<KeyCommand>) | undefined;
}
const mappings: KeyMapping[] = [];
export class KeyboardScope implements Disposable {
constructor(
private readonly mapping: KeyMapping
) {
for (const key in mapping) {
mapping[key] = mapping[key] || keyNoopCommand;
}
}
async dispose() {
const index = mappings.indexOf(this.mapping);
Logger.log('KeyboardScope.dispose', mappings.length, index);
if (index === mappings.length - 1) {
mappings.pop();
await this.updateKeyCommandsContext(mappings[mappings.length - 1]);
}
else {
mappings.splice(index, 1);
}
}
async begin() {
mappings.push(this.mapping);
await this.updateKeyCommandsContext(this.mapping);
return this;
}
async clearKeyCommand(key: Keys) {
const mapping = mappings[mappings.length - 1];
if (mapping !== this.mapping || !mapping[key]) return;
Logger.log('KeyboardScope.clearKeyCommand', mappings.length, key);
mapping[key] = undefined;
await setCommandContext(`${CommandContext.Key}:${key}`, false);
}
async setKeyCommand(key: Keys, command: KeyCommand | (() => Promise<KeyCommand>)) {
const mapping = mappings[mappings.length - 1];
if (mapping !== this.mapping) return;
Logger.log('KeyboardScope.setKeyCommand', mappings.length, key, Boolean(mapping[key]));
if (!mapping[key]) {
mapping[key] = command;
await setCommandContext(`${CommandContext.Key}:${key}`, true);
}
else {
mapping[key] = command;
}
}
private async updateKeyCommandsContext(mapping: KeyMapping) {
const promises = [];
for (const key of keys) {
promises.push(setCommandContext(`${CommandContext.Key}:${key}`, Boolean(mapping && mapping[key])));
}
await Promise.all(promises);
}
}
export class Keyboard implements Disposable {
private _disposable: Disposable;
constructor() {
const subscriptions = keys.map(key =>
commands.registerCommand(`${extensionId}.key.${key}`, () => this.execute(key), this)
);
this._disposable = Disposable.from(...subscriptions);
}
dispose() {
this._disposable && this._disposable.dispose();
}
async beginScope(mapping?: KeyMapping): Promise<KeyboardScope> {
Logger.log('Keyboard.beginScope', mappings.length);
return await new KeyboardScope(
mapping ? Object.assign(Object.create(null), mapping) : Object.create(null)
).begin();
}
async execute(key: Keys): Promise<{} | undefined> {
if (!mappings.length) return undefined;
try {
const mapping = mappings[mappings.length - 1];
let command = mapping[key] as KeyCommand | (() => Promise<KeyCommand>);
if (typeof command === 'function') {
command = await command();
}
if (!command || typeof command.onDidPressKey !== 'function') return undefined;
Logger.log('Keyboard.execute', key);
return await command.onDidPressKey(key);
}
catch (ex) {
Logger.error(ex, 'Keyboard.execute');
return undefined;
}
}
}