diff --git a/package.json b/package.json index a4351f7..6ebcfb1 100644 --- a/package.json +++ b/package.json @@ -319,6 +319,16 @@ }, "commands": [ { + "command": "gitlens.key.left", + "title": "Left KeyPress", + "category": "GitLens:KeyPress" + }, + { + "command": "gitlens.key.right", + "title": "Right KeyPress", + "category": "GitLens:KeyPress" + }, + { "command": "gitlens.diffWithPrevious", "title": "Compare with Previous Commit", "category": "GitLens" @@ -401,6 +411,14 @@ "menus": { "commandPalette": [ { + "command": "gitlens.key.left", + "when": "false" + }, + { + "command": "gitlens.key.right", + "when": "false" + }, + { "command": "gitlens.diffWithPrevious", "when": "gitlens:enabled" }, @@ -568,6 +586,16 @@ }, "keybindings": [ { + "command": "gitlens.key.left", + "key": "alt+left", + "when": "gitlens:key" + }, + { + "command": "gitlens.key.right", + "key": "alt+right", + "when": "gitlens:key" + }, + { "command": "gitlens.toggleBlame", "key": "alt+b", "mac": "alt+b", diff --git a/src/commands.ts b/src/commands.ts index 6fc7e2c..fcfef05 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,4 +1,6 @@ 'use strict'; +export { Keyboard } from './commands/keyboard'; + export { ActiveEditorCommand, Command, Commands, EditorCommand } from './commands/commands'; export { CopyMessageToClipboardCommand } from './commands/copyMessageToClipboard'; export { CopyShaToClipboardCommand } from './commands/copyShaToClipboard'; diff --git a/src/commands/keyboard.ts b/src/commands/keyboard.ts new file mode 100644 index 0000000..bed1b6e --- /dev/null +++ b/src/commands/keyboard.ts @@ -0,0 +1,80 @@ +'use strict'; +import { commands, Disposable, ExtensionContext, QuickPickItem } from 'vscode'; +import { BuiltInCommands } from '../constants'; +import { CommandQuickPickItem, OpenFileCommandQuickPickItem } from '../quickPicks/quickPicks'; +//import { Logger } from '../logger'; + +declare type Keys = 'left' | 'right'; +const keys: Keys[] = [ + 'left', + 'right' +]; + +let scopeCount = 0; + +let _instance: Keyboard; + +export class Keyboard extends Disposable { + + static get instance(): Keyboard { + return _instance; + } + + private _disposable: Disposable; + + constructor(private context: ExtensionContext) { + super(() => this.dispose()); + + const subscriptions: Disposable[] = []; + + for (const key of keys) { + subscriptions.push(commands.registerCommand(`gitlens.key.${key}`, () => this.execute(key), this)); + } + + this._disposable = Disposable.from(...subscriptions); + + _instance = this; + } + + dispose() { + this._disposable && this._disposable.dispose(); + } + + execute(key: Keys): any { + const command = this.context.globalState.get(`gitlens:key:${key}`) as CommandQuickPickItem; + if (!command || !(command instanceof CommandQuickPickItem)) return undefined; + + if (command instanceof OpenFileCommandQuickPickItem) { + // Have to open this pinned right now, because vscode doesn't have a way to open a unpinned, but unfocused editor + return command.execute(true); + } + + return command.execute(); + } + + async enterScope(...keyCommands: [Keys, QuickPickItem][]) { + await commands.executeCommand(BuiltInCommands.SetContext, 'gitlens:key', ++scopeCount); + if (keyCommands && Array.isArray(keyCommands) && keyCommands.length) { + for (const [key, command] of keyCommands) { + await this.setKeyCommand(key as Keys, command); + } + } + } + + async exitScope(clear: boolean = true) { + await commands.executeCommand(BuiltInCommands.SetContext, 'gitlens:key', --scopeCount); + if (clear && !scopeCount) { + for (const key of keys) { + await this.clearKeyCommand(key); + } + } + } + + async clearKeyCommand(key: Keys) { + await this.context.globalState.update(`gitlens:key:${key}`, undefined); + } + + async setKeyCommand(key: Keys, command: QuickPickItem) { + await this.context.globalState.update(`gitlens:key:${key}`, command); + } +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 84f8d4c..17fd019 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -9,6 +9,7 @@ import { ShowBlameCommand, ToggleBlameCommand } from './commands'; import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands'; import { ShowQuickCommitDetailsCommand, ShowQuickFileHistoryCommand, ShowQuickRepoHistoryCommand, ShowQuickRepoStatusCommand} from './commands'; import { ToggleCodeLensCommand } from './commands'; +import { Keyboard } from './commands'; import { IAdvancedConfig, IBlameConfig } from './configuration'; import { BuiltInCommands, WorkspaceState } from './constants'; import GitContentProvider from './gitContentProvider'; @@ -72,6 +73,8 @@ export async function activate(context: ExtensionContext) { const activeLineController = new BlameActiveLineController(context, git, annotationController); context.subscriptions.push(activeLineController); + context.subscriptions.push(new Keyboard(context)); + context.subscriptions.push(new CopyMessageToClipboardCommand(git, repoPath)); context.subscriptions.push(new CopyShaToClipboardCommand(git, repoPath)); context.subscriptions.push(new DiffWithWorkingCommand(git)); diff --git a/src/quickPicks/commitDetails.ts b/src/quickPicks/commitDetails.ts index da0ae08..e0fcbbb 100644 --- a/src/quickPicks/commitDetails.ts +++ b/src/quickPicks/commitDetails.ts @@ -1,7 +1,7 @@ 'use strict'; import { Iterables } from '../system'; import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; -import { Commands } from '../commands'; +import { Commands, Keyboard } from '../commands'; import GitProvider, { GitCommit, GitLogCommit, GitUri } from '../gitProvider'; import { CommitWithFileStatusQuickPickItem } from './gitQuickPicks'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks'; @@ -55,19 +55,21 @@ export class CommitDetailsQuickPick { items.splice(0, 0, goBackCommand); } - const result = await window.showQuickPick(items, { + await Keyboard.instance.enterScope(['left', goBackCommand]); + + const pick = await window.showQuickPick(items, { matchOnDescription: true, matchOnDetail: true, placeHolder: `${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - // onDidSelectItem: (item: QuickPickItem) => { - // if (item instanceof FileQuickPickItem) { - // item.preview(); - // } - // } + ignoreFocusOut: getQuickPickIgnoreFocusOut(), + onDidSelectItem: (item: QuickPickItem) => { + Keyboard.instance.setKeyCommand('right', item); + } } as QuickPickOptions); - return result; + await Keyboard.instance.exitScope(); + + return pick; } } @@ -137,10 +139,19 @@ export class CommitFileDetailsQuickPick { items.splice(0, 0, goBackCommand); } - return await window.showQuickPick(items, { + await Keyboard.instance.enterScope(['left', goBackCommand]); + + const pick = await window.showQuickPick(items, { matchOnDescription: true, placeHolder: `${commit.getFormattedPath()} \u2022 ${isUncommitted ? 'Uncommitted \u21E8 ' : '' }${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`, - ignoreFocusOut: getQuickPickIgnoreFocusOut() + ignoreFocusOut: getQuickPickIgnoreFocusOut(), + onDidSelectItem: (item: QuickPickItem) => { + Keyboard.instance.setKeyCommand('right', item); + } } as QuickPickOptions); + + await Keyboard.instance.exitScope(); + + return pick; } } \ No newline at end of file diff --git a/src/quickPicks/fileHistory.ts b/src/quickPicks/fileHistory.ts index 53fbdd2..079a5ea 100644 --- a/src/quickPicks/fileHistory.ts +++ b/src/quickPicks/fileHistory.ts @@ -1,7 +1,7 @@ 'use strict'; import { Iterables } from '../system'; -import { QuickPickOptions, Uri, window } from 'vscode'; -import { Commands } from '../commands'; +import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; +import { Commands, Keyboard } from '../commands'; import { IGitLog } from '../gitProvider'; import { CommitQuickPickItem } from './gitQuickPicks'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './quickPicks'; @@ -37,13 +37,22 @@ export class FileHistoryQuickPick { items.splice(0, 0, goBackCommand); } + await Keyboard.instance.enterScope(['left', goBackCommand]); + const commit = Iterables.first(log.commits.values()); - return await window.showQuickPick(items, { + const pick = await window.showQuickPick(items, { matchOnDescription: true, matchOnDetail: true, placeHolder: commit.getFormattedPath(), - ignoreFocusOut: getQuickPickIgnoreFocusOut() + ignoreFocusOut: getQuickPickIgnoreFocusOut(), + onDidSelectItem: (item: QuickPickItem) => { + Keyboard.instance.setKeyCommand('right', item); + } } as QuickPickOptions); + + await Keyboard.instance.exitScope(); + + return pick; } } \ No newline at end of file diff --git a/src/quickPicks/gitQuickPicks.ts b/src/quickPicks/gitQuickPicks.ts index d56e02a..cecd788 100644 --- a/src/quickPicks/gitQuickPicks.ts +++ b/src/quickPicks/gitQuickPicks.ts @@ -1,7 +1,7 @@ 'use strict'; import { QuickPickItem, Uri } from 'vscode'; import { getGitStatusIcon, GitCommit, GitFileStatus, GitUri } from '../gitProvider'; -import { openEditor } from './quickPicks'; +import { OpenFileCommandQuickPickItem } from './quickPicks'; import * as moment from 'moment'; import * as path from 'path'; @@ -18,31 +18,27 @@ export class CommitQuickPickItem implements QuickPickItem { } } -export class CommitWithFileStatusQuickPickItem implements QuickPickItem { - - label: string; - description: string; - detail: string; +export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickItem { + fileName: string; sha: string; - uri: GitUri; + status: GitFileStatus; - constructor(commit: GitCommit, public fileName: string, public status: GitFileStatus) { + constructor(commit: GitCommit, fileName: string, status: GitFileStatus) { const icon = getGitStatusIcon(status); - this.label = `\u00a0\u00a0\u00a0\u00a0${icon}\u00a0\u00a0 ${path.basename(fileName)}`; let directory = path.dirname(fileName); if (!directory || directory === '.') { directory = undefined; } - this.description = directory; + super(GitUri.fromUri(Uri.file(path.resolve(commit.repoPath, fileName))), { + label: `\u00a0\u00a0\u00a0\u00a0${icon}\u00a0\u00a0 ${path.basename(fileName)}`, + description: directory + }); + this.fileName = fileName; this.sha = commit.sha; - this.uri = GitUri.fromUri(Uri.file(path.resolve(commit.repoPath, fileName))); - } - - async preview(): Promise<{}> { - return openEditor(this.uri, true); + this.status = status; } } \ No newline at end of file diff --git a/src/quickPicks/repoHistory.ts b/src/quickPicks/repoHistory.ts index 615aaef..adc84c9 100644 --- a/src/quickPicks/repoHistory.ts +++ b/src/quickPicks/repoHistory.ts @@ -1,7 +1,7 @@ 'use strict'; import { Iterables } from '../system'; -import { QuickPickOptions, Uri, window } from 'vscode'; -import { Commands } from '../commands'; +import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; +import { Commands, Keyboard } from '../commands'; import { IGitLog } from '../gitProvider'; import { CommitQuickPickItem } from './gitQuickPicks'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './quickPicks'; @@ -25,11 +25,20 @@ export class RepoHistoryQuickPick { items.splice(0, 0, goBackCommand); } - return await window.showQuickPick(items, { + await Keyboard.instance.enterScope(['left', goBackCommand]); + + const pick = await window.showQuickPick(items, { matchOnDescription: true, matchOnDetail: true, placeHolder: 'Search by commit message, filename, or sha', - ignoreFocusOut: getQuickPickIgnoreFocusOut() + ignoreFocusOut: getQuickPickIgnoreFocusOut(), + onDidSelectItem: (item: QuickPickItem) => { + Keyboard.instance.setKeyCommand('right', item); + } } as QuickPickOptions); + + await Keyboard.instance.exitScope(); + + return pick; } } \ No newline at end of file diff --git a/src/quickPicks/repoStatus.ts b/src/quickPicks/repoStatus.ts index 939e6b5..0926cc4 100644 --- a/src/quickPicks/repoStatus.ts +++ b/src/quickPicks/repoStatus.ts @@ -1,6 +1,7 @@ 'use strict'; import { Iterables } from '../system'; import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; +import { Keyboard } from '../commands'; import { getGitStatusIcon, GitFileStatusItem } from '../gitProvider'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks'; import * as path from 'path'; @@ -72,10 +73,19 @@ export class RepoStatusQuickPick { items.splice(0, 0, goBackCommand); } - return await window.showQuickPick(items, { + await Keyboard.instance.enterScope(['left', goBackCommand]); + + const pick = await window.showQuickPick(items, { matchOnDescription: true, placeHolder: statuses.length ? 'Repository has changes' : 'Repository has no changes', - ignoreFocusOut: getQuickPickIgnoreFocusOut() + ignoreFocusOut: getQuickPickIgnoreFocusOut(), + onDidSelectItem: (item: QuickPickItem) => { + Keyboard.instance.setKeyCommand('right', item); + } } as QuickPickOptions); + + await Keyboard.instance.exitScope(); + + return pick; } } \ No newline at end of file