Skip to content

Commit

Permalink
Refactors keyboard hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
eamodio committed Sep 14, 2019
1 parent 1f1acb2 commit 7cdc9f7
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 86 deletions.
30 changes: 26 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5279,21 +5279,43 @@
"keybindings": [
{
"command": "gitlens.key.left",
"key": "alt+left",

This comment has been minimized.

Copy link
@hossain666

hossain666 May 15, 2024

package.json

This comment has been minimized.

Copy link
@hossain666

hossain666 May 15, 2024

package.json

This comment has been minimized.

Copy link
@hossain666

hossain666 May 15, 2024

package.json

"key": "left",
"when": "gitlens:key:left"
},
{
"command": "gitlens.key.alt+left",
"key": "alt+left",
"when": "gitlens:key:alt+left"
},
{
"command": "gitlens.key.ctrl+left",
"key": "ctrl+left",
"mac": "cmd+left",
"when": "gitlens:key:ctrl+left"
},
{
"command": "gitlens.key.right",
"key": "alt+right",
"key": "right",
"when": "gitlens:key:right"
},
{
"command": "gitlens.key.,",
"command": "gitlens.key.alt+right",
"key": "alt+right",
"when": "gitlens:key:alt+right"
},
{
"command": "gitlens.key.ctrl+right",
"key": "ctrl+right",
"mac": "cmd+right",
"when": "gitlens:key:ctrl+right"
},
{
"command": "gitlens.key.alt+,",
"key": "alt+,",
"when": "gitlens:key:,"
},
{
"command": "gitlens.key..",
"command": "gitlens.key.alt+.",
"key": "alt+.",
"when": "gitlens:key:."
},
Expand Down
181 changes: 144 additions & 37 deletions src/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,58 @@
import { commands, Disposable } from 'vscode';
import { CommandContext, extensionId, setCommandContext } from './constants';
import { Logger } from './logger';
import { log } from './system/decorators/log';

export declare interface KeyCommand {
onDidPressKey?(key: Keys): Thenable<{} | undefined>;
onDidPressKey?(key: Keys): void | Promise<void>;
}

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 | (() => Thenable<KeyCommand>) | undefined;
}
export const keys = [
'left',
'alt+left',
'ctrl+left',
'right',
'alt+right',
'ctrl+right',
'alt+,',
'alt+.',
'escape'
] as const;
export type Keys = typeof keys[number];

export type KeyMapping = { [K in Keys]?: KeyCommand | (() => Promise<KeyCommand>) };
type IndexableKeyMapping = KeyMapping & {
[index: 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;
private readonly _mapping: IndexableKeyMapping;
constructor(mapping: KeyMapping) {
this._mapping = mapping;
for (const key in this._mapping) {
this._mapping[key] = this._mapping[key] || keyNoopCommand;
}

mappings.push(this._mapping);
}

@log({
args: false,
prefix: context => `${context.prefix}[${mappings.length}]`
})
async dispose() {
const index = mappings.indexOf(this.mapping);
Logger.log('KeyboardScope.dispose', mappings.length, index);
const index = mappings.indexOf(this._mapping);

const cc = Logger.getCorrelationContext();
if (cc) {
cc.exitDetails = ` \u2022 index=${index}`;
}

if (index === mappings.length - 1) {
mappings.pop();
await this.updateKeyCommandsContext(mappings[mappings.length - 1]);
Expand All @@ -37,41 +62,93 @@ export class KeyboardScope implements Disposable {
}
}

async begin() {
mappings.push(this.mapping);
await this.updateKeyCommandsContext(this.mapping);
return this;
private _paused = true;
get paused() {
return this._paused;
}

@log<KeyboardScope['clearKeyCommand']>({
args: false,
prefix: (context, key) => `${context.prefix}[${mappings.length}](${key})`
})
async clearKeyCommand(key: Keys) {
const cc = Logger.getCorrelationContext();

const mapping = mappings[mappings.length - 1];
if (mapping !== this.mapping || !mapping[key]) return;
if (mapping !== this._mapping || !mapping[key]) {
if (cc) {
cc.exitDetails = ' \u2022 skipped';
}

return;
}

Logger.log('KeyboardScope.clearKeyCommand', mappings.length, key);
mapping[key] = undefined;
await setCommandContext(`${CommandContext.Key}:${key}`, false);
}

@log({
args: false,
prefix: context => `${context.prefix}(paused=${context.instance._paused})`
})
async pause(keys?: Keys[]) {
if (this._paused) return;

this._paused = true;
const mapping = (Object.keys(this._mapping) as Keys[]).reduce(
(accumulator, key) => {
accumulator[key] = keys === undefined ? false : keys.includes(key) ? false : this._mapping[key];
return accumulator;
},
{} as any
);

await this.updateKeyCommandsContext(mapping);
}

@log({
args: false,
prefix: context => `${context.prefix}(paused=${context.instance._paused})`
})
async resume() {
if (!this._paused) return;

this._paused = false;
await this.updateKeyCommandsContext(this._mapping);
}

async start() {
await this.resume();
}

@log<KeyboardScope['setKeyCommand']>({
args: false,
prefix: (context, key) => `${context.prefix}[${mappings.length}](${key})`
})
async setKeyCommand(key: Keys, command: KeyCommand | (() => Promise<KeyCommand>)) {
const cc = Logger.getCorrelationContext();

const mapping = mappings[mappings.length - 1];
if (mapping !== this.mapping) return;
if (mapping !== this._mapping) {
if (cc) {
cc.exitDetails = ' \u2022 skipped';
}

return;
}

Logger.log('KeyboardScope.setKeyCommand', mappings.length, key, Boolean(mapping[key]));
const set = Boolean(mapping[key]);

if (!mapping[key]) {
mapping[key] = command;
mapping[key] = command;
if (!set) {
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);
await Promise.all(
keys.map(key => setCommandContext(`${CommandContext.Key}:${key}`, Boolean(mapping && mapping[key])))
);
}
}

Expand All @@ -89,13 +166,37 @@ export class Keyboard implements Disposable {
this._disposable && this._disposable.dispose();
}

beginScope(mapping?: KeyMapping): Promise<KeyboardScope> {
Logger.log('Keyboard.beginScope', mappings.length);
return new KeyboardScope(mapping ? Object.assign(Object.create(null), mapping) : Object.create(null)).begin();
@log<Keyboard['createScope']>({
args: false,
prefix: (context, mapping) =>
`${context.prefix}[${mappings.length}](${mapping === undefined ? '' : Object.keys(mapping).join(',')})`
})
createScope(mapping?: KeyMapping): KeyboardScope {
return new KeyboardScope({ ...mapping });
}

@log<Keyboard['beginScope']>({
args: false,
prefix: (context, mapping) =>
`${context.prefix}[${mappings.length}](${mapping === undefined ? '' : Object.keys(mapping).join(',')})`
})
async beginScope(mapping?: KeyMapping): Promise<KeyboardScope> {
const scope = this.createScope(mapping);
await scope.start();
return scope;
}

@log()
async execute(key: Keys): Promise<{} | undefined> {
if (!mappings.length) return undefined;
const cc = Logger.getCorrelationContext();

if (!mappings.length) {
if (cc) {
cc.exitDetails = ' \u2022 skipped, no mappings';
}

return undefined;
}

try {
const mapping = mappings[mappings.length - 1];
Expand All @@ -104,13 +205,19 @@ export class Keyboard implements Disposable {
if (typeof command === 'function') {
command = await command();
}
if (!command || typeof command.onDidPressKey !== 'function') return undefined;
if (!command || typeof command.onDidPressKey !== 'function') {
if (cc) {
cc.exitDetails = ' \u2022 skipped, no callback';
}

return undefined;
}

Logger.log('Keyboard.execute', key);
await command.onDidPressKey(key);

return await command.onDidPressKey(key);
return undefined;
} catch (ex) {
Logger.error(ex, 'Keyboard.execute');
Logger.error(ex, cc);
return undefined;
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/quickpicks/branchHistoryQuickPick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export class BranchHistoryQuickPick {
return showQuickPickProgress(

This comment has been minimized.

Copy link
@hossain666

hossain666 May 15, 2024

src/keyboard.ts``

`${branch} history ${GlyphChars.Dash} search by commit message, filename, or commit id`,
{
left: KeyNoopCommand,
',': KeyNoopCommand,
'.': KeyNoopCommand
'alt+left': KeyNoopCommand,
'alt+,': KeyNoopCommand,
'alt+.': KeyNoopCommand
}
);
}
Expand Down Expand Up @@ -136,9 +136,9 @@ export class BranchHistoryQuickPick {
if (progressCancellation.token.isCancellationRequested) return undefined;

const scope = await Container.keyboard.beginScope({
left: goBackCommand,
',': previousPageCommand,
'.': nextPageCommand
'alt+left': goBackCommand,
'alt+,': previousPageCommand,
'alt+.': nextPageCommand
});

progressCancellation.cancel();
Expand Down
16 changes: 8 additions & 8 deletions src/quickpicks/commitFileQuickPick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export class OpenCommitFileCommandQuickPickItem extends CommandQuickPickItem {
return commands.executeCommand(Commands.OpenWorkingFile, undefined, args);
}

onDidPressKey(key: Keys): Thenable<{} | undefined> {
return this.execute({
async onDidPressKey(key: Keys): Promise<void> {
await this.execute({
preserveFocus: true,
preview: false
});
Expand Down Expand Up @@ -105,8 +105,8 @@ export class OpenCommitFileRevisionCommandQuickPickItem extends CommandQuickPick
return openEditor(this._uri, options);
}

onDidPressKey(key: Keys): Thenable<{} | undefined> {
return this.execute({
async onDidPressKey(key: Keys): Promise<void> {
await this.execute({
preserveFocus: true,
preview: false
});
Expand Down Expand Up @@ -360,9 +360,9 @@ export class CommitFileQuickPick {
}

const scope = await Container.keyboard.beginScope({
left: goBackCommand,
',': previousCommand,
'.': nextCommand
'alt+left': goBackCommand,
'alt+,': previousCommand,
'alt+.': nextCommand
});

const pick = await window.showQuickPick(items, {
Expand All @@ -374,7 +374,7 @@ export class CommitFileQuickPick {
} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getShortMessage()}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
void scope.setKeyCommand('right', item as KeyCommand);
void scope.setKeyCommand('alt+right', item as KeyCommand);
}
});

Expand Down

0 comments on commit 7cdc9f7

Please sign in to comment.