瀏覽代碼

Refactors keyboard hooks

main
Eric Amodio 5 年之前
父節點
當前提交
7cdc9f7ee3
共有 10 個檔案被更改,包括 217 行新增86 行删除
  1. +26
    -4
      package.json
  2. +144
    -37
      src/keyboard.ts
  3. +6
    -6
      src/quickpicks/branchHistoryQuickPick.ts
  4. +8
    -8
      src/quickpicks/commitFileQuickPick.ts
  5. +13
    -11
      src/quickpicks/commitQuickPick.ts
  6. +5
    -5
      src/quickpicks/commitsQuickPick.ts
  7. +3
    -3
      src/quickpicks/commonQuickPicks.ts
  8. +6
    -6
      src/quickpicks/fileHistoryQuickPick.ts
  9. +1
    -1
      src/quickpicks/referencesQuickPick.ts
  10. +5
    -5
      src/quickpicks/repoStatusQuickPick.ts

+ 26
- 4
package.json 查看文件

@ -5279,21 +5279,43 @@
"keybindings": [
{
"command": "gitlens.key.left",
"key": "alt+left",
"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:."
},

+ 144
- 37
src/keyboard.ts 查看文件

@ -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]);
@ -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])))
);
}
}
@ -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];
@ -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;
}
}

+ 6
- 6
src/quickpicks/branchHistoryQuickPick.ts 查看文件

@ -15,9 +15,9 @@ export class BranchHistoryQuickPick {
return showQuickPickProgress(
`${branch} history ${GlyphChars.Dash} search by commit message, filename, or commit id`,
{
left: KeyNoopCommand,
',': KeyNoopCommand,
'.': KeyNoopCommand
'alt+left': KeyNoopCommand,
'alt+,': KeyNoopCommand,
'alt+.': KeyNoopCommand
}
);
}
@ -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();

+ 8
- 8
src/quickpicks/commitFileQuickPick.ts 查看文件

@ -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
});
@ -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
});
@ -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, {
@ -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);
}
});

+ 13
- 11
src/quickpicks/commitQuickPick.ts 查看文件

@ -54,8 +54,10 @@ export class CommitWithFileStatusQuickPickItem extends CommandQuickPickItem {
return openEditor(GitUri.toRevisionUri(this.commit.sha, this._file, this.commit.repoPath), options);
}
onDidPressKey(key: Keys): Thenable<{} | undefined> {
if (this.commit.previousSha === undefined) return super.onDidPressKey(key);
async onDidPressKey(key: Keys): Promise<void> {
if (this.commit.previousSha === undefined) {
await super.onDidPressKey(key);
}
const commandArgs: DiffWithPreviousCommandArgs = {
commit: this.commit,
@ -64,7 +66,7 @@ export class CommitWithFileStatusQuickPickItem extends CommandQuickPickItem {
preview: false
}
};
return commands.executeCommand(Commands.DiffWithPrevious, this.commit.toGitUri(), commandArgs);
await commands.executeCommand(Commands.DiffWithPrevious, this.commit.toGitUri(), commandArgs);
}
}
@ -96,8 +98,8 @@ export class OpenCommitFilesCommandQuickPickItem extends CommandQuickPickItem {
return undefined;
}
onDidPressKey(key: Keys): Thenable<{} | undefined> {
return this.execute({
async onDidPressKey(key: Keys): Promise<void> {
await this.execute({
preserveFocus: true,
preview: false
});
@ -132,8 +134,8 @@ export class OpenCommitFileRevisionsCommandQuickPickItem extends CommandQuickPic
return undefined;
}
onDidPressKey(key: Keys): Thenable<{} | undefined> {
return this.execute({
async onDidPressKey(key: Keys): Promise<void> {
await this.execute({
preserveFocus: true,
preview: false
});
@ -213,9 +215,9 @@ export class CommitQuickPick {
}
const scope = await Container.keyboard.beginScope({
left: options.goBackCommand,
',': previousCommand,
'.': nextCommand
'alt+left': options.goBackCommand,
'alt+,': previousCommand,
'alt+.': nextCommand
});
const pick = await window.showQuickPick(this.getItems(commit, uri, options), {
@ -226,7 +228,7 @@ export class CommitQuickPick {
}${commit.formattedDate} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getShortMessage()}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
void scope.setKeyCommand('right', item);
void scope.setKeyCommand('alt+right', item);
if (typeof item.onDidSelect === 'function') {
item.onDidSelect();
}

+ 5
- 5
src/quickpicks/commitsQuickPick.ts 查看文件

@ -15,9 +15,9 @@ import { CommitQuickPickItem } from './gitQuickPicks';
export class CommitsQuickPick {
static showProgress(message: string) {
return showQuickPickProgress(message, {
left: KeyNoopCommand,
',': KeyNoopCommand,
'.': KeyNoopCommand
'alt+left': KeyNoopCommand,
'alt+,': KeyNoopCommand,
'alt+.': KeyNoopCommand
});
}
@ -49,7 +49,7 @@ export class CommitsQuickPick {
if (progressCancellation.token.isCancellationRequested) return undefined;
const scope = await Container.keyboard.beginScope({ left: options.goBackCommand });
const scope = await Container.keyboard.beginScope({ 'alt+left': options.goBackCommand });
progressCancellation.cancel();
@ -58,7 +58,7 @@ export class CommitsQuickPick {
placeHolder: placeHolder,
ignoreFocusOut: getQuickPickIgnoreFocusOut()
// onDidSelectItem: (item: QuickPickItem) => {
// scope.setKeyCommand('right', item);
// scope.setKeyCommand('alt+right', item);
// }
});

+ 3
- 3
src/quickpicks/commonQuickPicks.ts 查看文件

@ -48,7 +48,7 @@ function _getInfiniteCancellablePromise(cancellation: CancellationTokenSource) {
export interface QuickPickItem extends QuickPickItem {
onDidSelect?(): void;
onDidPressKey?(key: Keys): Promise<{} | undefined>;
onDidPressKey?(key: Keys): Promise<void>;
}
export class CommandQuickPickItem implements QuickPickItem {
@ -80,8 +80,8 @@ export class CommandQuickPickItem implements QuickPickItem {
return commands.executeCommand(this.command, ...(this.args || []));
}
onDidPressKey(key: Keys): Thenable<{} | undefined> {
return this.execute();
async onDidPressKey(key: Keys): Promise<void> {
await this.execute();
}
}

+ 6
- 6
src/quickpicks/fileHistoryQuickPick.ts 查看文件

@ -18,9 +18,9 @@ import { CommitQuickPickItem } from './gitQuickPicks';
export class FileHistoryQuickPick {
static showProgress(placeHolder: string) {
return showQuickPickProgress(placeHolder, {
left: KeyNoopCommand,
',': KeyNoopCommand,
'.': KeyNoopCommand
'alt+left': KeyNoopCommand,
'alt+,': KeyNoopCommand,
'alt+.': KeyNoopCommand
});
}
@ -178,9 +178,9 @@ export class FileHistoryQuickPick {
}
const scope = await Container.keyboard.beginScope({
left: options.goBackCommand,
',': options.previousPageCommand,
'.': options.nextPageCommand
'alt+left': options.goBackCommand,
'alt+,': options.previousPageCommand,
'alt+.': options.nextPageCommand
});
options.progressCancellation && options.progressCancellation.cancel();

+ 1
- 1
src/quickpicks/referencesQuickPick.ts 查看文件

@ -55,7 +55,7 @@ export class ReferencesQuickPick {
let scope;
if (options.goBack) {
scope = await Container.keyboard.beginScope({ left: options.goBack });
scope = await Container.keyboard.beginScope({ 'alt+left': options.goBack });
}
let autoPick;

+ 5
- 5
src/quickpicks/repoStatusQuickPick.ts 查看文件

@ -83,7 +83,7 @@ export class OpenStatusFileCommandQuickPickItem extends CommandQuickPickItem {
return openEditor(this._status.uri, options);
}
onDidPressKey(key: Keys): Promise<{} | undefined> {
async onDidPressKey(key: Keys): Promise<void> {
const commandArgs: DiffWithPreviousCommandArgs = {
commit: this.commit,
line: 0,
@ -92,11 +92,11 @@ export class OpenStatusFileCommandQuickPickItem extends CommandQuickPickItem {
preview: false
}
};
return commands.executeCommand(
await commands.executeCommand(
Commands.DiffWithPrevious,
GitUri.fromFile(this.status, this.status.repoPath),
commandArgs
) as Promise<{} | undefined>;
);
}
}
@ -420,7 +420,7 @@ export class RepoStatusQuickPick {
items.splice(0, 0, goBackCommand);
}
const scope = await Container.keyboard.beginScope({ left: goBackCommand });
const scope = await Container.keyboard.beginScope({ 'alt+left': goBackCommand });
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
@ -429,7 +429,7 @@ export class RepoStatusQuickPick {
}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
void scope.setKeyCommand('right', item);
void scope.setKeyCommand('alt+right', item);
}
});

Loading…
取消
儲存