From d3c01f4306c4d6a8646e6c83704ae4050eb8b76d Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 25 Jul 2018 18:53:42 -0400 Subject: [PATCH] Renames quickpick folder --- src/quickPicks/branchHistoryQuickPick.ts | 196 ------------ src/quickPicks/branchesAndTagsQuickPick.ts | 80 ----- src/quickPicks/branchesQuickPick.ts | 43 --- src/quickPicks/commitFileQuickPick.ts | 423 ------------------------- src/quickPicks/commitQuickPick.ts | 419 ------------------------- src/quickPicks/commitsQuickPick.ts | 69 ----- src/quickPicks/commonQuickPicks.ts | 282 ----------------- src/quickPicks/fileHistoryQuickPick.ts | 224 -------------- src/quickPicks/modesQuickPick.ts | 41 --- src/quickPicks/remotesQuickPick.ts | 167 ---------- src/quickPicks/repoStatusQuickPick.ts | 478 ----------------------------- src/quickPicks/repositoriesQuickPick.ts | 49 --- src/quickPicks/stashListQuickPick.ts | 84 ----- src/quickpicks/branchHistoryQuickPick.ts | 196 ++++++++++++ src/quickpicks/branchesAndTagsQuickPick.ts | 80 +++++ src/quickpicks/branchesQuickPick.ts | 43 +++ src/quickpicks/commitFileQuickPick.ts | 423 +++++++++++++++++++++++++ src/quickpicks/commitQuickPick.ts | 419 +++++++++++++++++++++++++ src/quickpicks/commitsQuickPick.ts | 69 +++++ src/quickpicks/commonQuickPicks.ts | 282 +++++++++++++++++ src/quickpicks/fileHistoryQuickPick.ts | 224 ++++++++++++++ src/quickpicks/modesQuickPick.ts | 41 +++ src/quickpicks/remotesQuickPick.ts | 167 ++++++++++ src/quickpicks/repoStatusQuickPick.ts | 478 +++++++++++++++++++++++++++++ src/quickpicks/repositoriesQuickPick.ts | 49 +++ src/quickpicks/stashListQuickPick.ts | 84 +++++ 26 files changed, 2555 insertions(+), 2555 deletions(-) delete mode 100644 src/quickPicks/branchHistoryQuickPick.ts delete mode 100644 src/quickPicks/branchesAndTagsQuickPick.ts delete mode 100644 src/quickPicks/branchesQuickPick.ts delete mode 100644 src/quickPicks/commitFileQuickPick.ts delete mode 100644 src/quickPicks/commitQuickPick.ts delete mode 100644 src/quickPicks/commitsQuickPick.ts delete mode 100644 src/quickPicks/commonQuickPicks.ts delete mode 100644 src/quickPicks/fileHistoryQuickPick.ts delete mode 100644 src/quickPicks/modesQuickPick.ts delete mode 100644 src/quickPicks/remotesQuickPick.ts delete mode 100644 src/quickPicks/repoStatusQuickPick.ts delete mode 100644 src/quickPicks/repositoriesQuickPick.ts delete mode 100644 src/quickPicks/stashListQuickPick.ts create mode 100644 src/quickpicks/branchHistoryQuickPick.ts create mode 100644 src/quickpicks/branchesAndTagsQuickPick.ts create mode 100644 src/quickpicks/branchesQuickPick.ts create mode 100644 src/quickpicks/commitFileQuickPick.ts create mode 100644 src/quickpicks/commitQuickPick.ts create mode 100644 src/quickpicks/commitsQuickPick.ts create mode 100644 src/quickpicks/commonQuickPicks.ts create mode 100644 src/quickpicks/fileHistoryQuickPick.ts create mode 100644 src/quickpicks/modesQuickPick.ts create mode 100644 src/quickpicks/remotesQuickPick.ts create mode 100644 src/quickpicks/repoStatusQuickPick.ts create mode 100644 src/quickpicks/repositoriesQuickPick.ts create mode 100644 src/quickpicks/stashListQuickPick.ts diff --git a/src/quickPicks/branchHistoryQuickPick.ts b/src/quickPicks/branchHistoryQuickPick.ts deleted file mode 100644 index 0ab0cf0..0000000 --- a/src/quickPicks/branchHistoryQuickPick.ts +++ /dev/null @@ -1,196 +0,0 @@ -'use strict'; -import { CancellationTokenSource, QuickPickOptions, window } from 'vscode'; -import { Commands, ShowCommitSearchCommandArgs, ShowQuickBranchHistoryCommandArgs } from '../commands'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { GitLog, GitUri, RemoteResource } from '../gitService'; -import { KeyNoopCommand } from '../keyboard'; -import { Iterables, Strings } from '../system'; -import { - CommandQuickPickItem, - CommitQuickPickItem, - getQuickPickIgnoreFocusOut, - showQuickPickProgress -} from './commonQuickPicks'; -import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; - -export class BranchHistoryQuickPick { - static showProgress(branch: string) { - return showQuickPickProgress( - `${branch} history ${GlyphChars.Dash} search by commit message, filename, or commit id`, - { - left: KeyNoopCommand, - ',': KeyNoopCommand, - '.': KeyNoopCommand - } - ); - } - - static async show( - log: GitLog, - uri: GitUri | undefined, - branch: string, - progressCancellation: CancellationTokenSource, - goBackCommand?: CommandQuickPickItem, - nextPageCommand?: CommandQuickPickItem - ): Promise { - const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as ( - | CommitQuickPickItem - | CommandQuickPickItem)[]; - - const currentCommand = new CommandQuickPickItem( - { - label: `go back ${GlyphChars.ArrowBack}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to ${ - GlyphChars.Space - }$(git-branch) ${branch} history` - }, - Commands.ShowQuickBranchHistory, - [ - uri, - { - branch, - log, - maxCount: log.maxCount, - goBackCommand - } as ShowQuickBranchHistoryCommandArgs - ] - ); - - const remotes = await Container.git.getRemotes((uri && uri.repoPath) || log.repoPath); - if (remotes.length) { - items.splice( - 0, - 0, - new OpenRemotesCommandQuickPickItem( - remotes, - { - type: 'branch', - branch - } as RemoteResource, - currentCommand - ) - ); - } - - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `$(search) Show Commit Search`, - description: `${Strings.pad( - GlyphChars.Dash, - 2, - 3 - )} search for commits by message, author, files, or commit id` - }, - Commands.ShowCommitSearch, - [ - GitUri.fromRepoPath(log.repoPath), - { - goBackCommand: currentCommand - } as ShowCommitSearchCommandArgs - ] - ) - ); - - let previousPageCommand: CommandQuickPickItem | undefined = undefined; - - if (log.truncated || log.sha) { - if (log.truncated) { - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `$(sync) Show All Commits`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} this may take a while` - }, - Commands.ShowQuickBranchHistory, - [ - GitUri.fromRepoPath(log.repoPath), - { - branch, - maxCount: 0, - goBackCommand - } as ShowQuickBranchHistoryCommandArgs - ] - ) - ); - } - - if (nextPageCommand) { - items.splice(0, 0, nextPageCommand); - } - - if (log.truncated) { - const npc = new CommandQuickPickItem( - { - label: `$(arrow-right) Show Next Commits`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} newer commits` - }, - Commands.ShowQuickBranchHistory, - [ - uri, - { - branch, - maxCount: log.maxCount, - nextPageCommand - } as ShowQuickBranchHistoryCommandArgs - ] - ); - - const last = Iterables.last(log.commits.values()); - if (last != null) { - previousPageCommand = new CommandQuickPickItem( - { - label: `$(arrow-left) Show Previous Commits`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} older commits` - }, - Commands.ShowQuickBranchHistory, - [ - new GitUri(uri ? uri : last.uri, last), - { - branch, - maxCount: log.maxCount, - goBackCommand, - nextPageCommand: npc - } as ShowQuickBranchHistoryCommandArgs - ] - ); - - items.splice(0, 0, previousPageCommand); - } - } - } - - if (goBackCommand) { - items.splice(0, 0, goBackCommand); - } - - if (progressCancellation.token.isCancellationRequested) return undefined; - - const scope = await Container.keyboard.beginScope({ - left: goBackCommand, - ',': previousPageCommand, - '.': nextPageCommand - }); - - progressCancellation.cancel(); - - const pick = await window.showQuickPick(items, { - matchOnDescription: true, - matchOnDetail: true, - placeHolder: `${branch} history ${GlyphChars.Dash} search by commit message, filename, or commit id`, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - // onDidSelectItem: (item: QuickPickItem) => { - // scope.setKeyCommand('right', item); - // } - } as QuickPickOptions); - - await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/branchesAndTagsQuickPick.ts b/src/quickPicks/branchesAndTagsQuickPick.ts deleted file mode 100644 index 03002ed..0000000 --- a/src/quickPicks/branchesAndTagsQuickPick.ts +++ /dev/null @@ -1,80 +0,0 @@ -'use strict'; -import { CancellationTokenSource, QuickPickItem, QuickPickOptions, window } from 'vscode'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { GitBranch, GitTag } from '../gitService'; -import { KeyNoopCommand } from '../keyboard'; -import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, showQuickPickProgress } from './commonQuickPicks'; - -export class BranchOrTagQuickPickItem implements QuickPickItem { - label: string; - description: string; - detail: string | undefined; - - constructor( - public readonly branchOrTag: GitBranch | GitTag - ) { - if (branchOrTag instanceof GitBranch) { - this.label = `${branchOrTag.current ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${ - branchOrTag.name - }`; - this.description = branchOrTag.remote ? `${GlyphChars.Space.repeat(2)} remote branch` : ''; - } - else { - this.label = `${GlyphChars.Space.repeat(4)} ${branchOrTag.name}`; - this.description = `${GlyphChars.Space.repeat(2)} tag`; - } - } - - get name() { - return this.branchOrTag.name; - } - - get remote() { - return this.branchOrTag instanceof GitBranch && this.branchOrTag.remote; - } -} - -export class BranchesAndTagsQuickPick { - static showProgress(placeHolder: string) { - return showQuickPickProgress(placeHolder, { - left: KeyNoopCommand, - ',': KeyNoopCommand, - '.': KeyNoopCommand - }); - } - - static async show( - branches: GitBranch[], - tags: GitTag[], - placeHolder: string, - options: { goBackCommand?: CommandQuickPickItem; progressCancellation?: CancellationTokenSource } = {} - ): Promise { - const items = [ - ...branches.filter(b => !b.remote).map(b => new BranchOrTagQuickPickItem(b)), - ...tags.map(t => new BranchOrTagQuickPickItem(t)), - ...branches.filter(b => b.remote).map(b => new BranchOrTagQuickPickItem(b)) - ] as (BranchOrTagQuickPickItem | CommandQuickPickItem)[]; - - if (options.goBackCommand !== undefined) { - items.splice(0, 0, options.goBackCommand); - } - - if (options.progressCancellation !== undefined && options.progressCancellation.token.isCancellationRequested) { - return undefined; - } - - const scope = await Container.keyboard.beginScope({ left: options.goBackCommand || KeyNoopCommand }); - - options.progressCancellation && options.progressCancellation.cancel(); - - const pick = await window.showQuickPick(items, { - placeHolder: placeHolder, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - } as QuickPickOptions); - - await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/branchesQuickPick.ts b/src/quickPicks/branchesQuickPick.ts deleted file mode 100644 index 9e98a16..0000000 --- a/src/quickPicks/branchesQuickPick.ts +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; -import { QuickPickItem, QuickPickOptions, window } from 'vscode'; -import { GlyphChars } from '../constants'; -import { GitBranch } from '../gitService'; -import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; - -export class BranchQuickPickItem implements QuickPickItem { - label: string; - description: string; - detail: string | undefined; - - constructor( - public readonly branch: GitBranch - ) { - this.label = `${branch.current ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${branch.name}`; - this.description = branch.remote ? `${GlyphChars.Space.repeat(2)} remote branch` : ''; - } -} - -export class BranchesQuickPick { - static async show( - branches: GitBranch[], - placeHolder: string, - options: { goBackCommand?: CommandQuickPickItem } = {} - ): Promise { - const items = branches.map(b => new BranchQuickPickItem(b)) as (BranchQuickPickItem | CommandQuickPickItem)[]; - - if (options.goBackCommand !== undefined) { - items.splice(0, 0, options.goBackCommand); - } - - // const scope = await Container.keyboard.beginScope({ left: goBackCommand }); - - const pick = await window.showQuickPick(items, { - placeHolder: placeHolder, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - } as QuickPickOptions); - - // await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/commitFileQuickPick.ts b/src/quickPicks/commitFileQuickPick.ts deleted file mode 100644 index 1c14d61..0000000 --- a/src/quickPicks/commitFileQuickPick.ts +++ /dev/null @@ -1,423 +0,0 @@ -'use strict'; -import * as path from 'path'; -import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; -import { - Commands, - CopyMessageToClipboardCommandArgs, - CopyShaToClipboardCommandArgs, - DiffWithPreviousCommandArgs, - DiffWithWorkingCommandArgs, - openEditor, - ShowQuickCommitDetailsCommandArgs, - ShowQuickCommitFileDetailsCommandArgs, - ShowQuickFileHistoryCommandArgs -} from '../commands'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { GitLog, GitLogCommit, GitUri, RemoteResource } from '../gitService'; -import { KeyCommand, KeyNoopCommand } from '../keyboard'; -import { Iterables, Strings } from '../system'; -import { - CommandQuickPickItem, - getQuickPickIgnoreFocusOut, - KeyCommandQuickPickItem, - OpenFileCommandQuickPickItem -} from './commonQuickPicks'; -import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; - -export class ApplyCommitFileChangesCommandQuickPickItem extends CommandQuickPickItem { - constructor( - private readonly commit: GitLogCommit, - item?: QuickPickItem - ) { - super( - item || { - label: `$(git-pull-request) Apply Changes`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(file-text) ${path.basename(commit.fileName)} in ${ - GlyphChars.Space - }$(git-commit) ${commit.shortSha}` - }, - undefined, - undefined - ); - } - - async execute(): Promise<{} | undefined> { - const uri = this.commit.toGitUri(); - await Container.git.checkoutFile(uri); - return openEditor(uri, { preserveFocus: true, preview: false }); - } -} - -export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem { - constructor(commit: GitLogCommit, item?: QuickPickItem) { - const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName)); - super( - uri, - item || { - label: `$(file-symlink-file) Open File`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}` - } - ); - } -} - -export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQuickPickItem { - constructor(commit: GitLogCommit, item?: QuickPickItem) { - let description: string; - let uri: Uri; - if (commit.status === 'D') { - uri = GitUri.toRevisionUri(commit.previousFileSha, commit.previousUri.fsPath, commit.repoPath); - description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${ - GlyphChars.Space - }$(git-commit) ${commit.previousShortSha} (deleted in ${GlyphChars.Space}$(git-commit) ${commit.shortSha})`; - } - else { - uri = GitUri.toRevisionUri(commit.sha, commit.uri.fsPath, commit.repoPath); - description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${ - GlyphChars.Space - }$(git-commit) ${commit.shortSha}`; - } - super( - uri, - item || { - label: `$(file-symlink-file) Open Revision`, - description: description - } - ); - } -} - -export class CommitFileQuickPick { - static async show( - commit: GitLogCommit, - uri: Uri, - goBackCommand?: CommandQuickPickItem, - currentCommand?: CommandQuickPickItem, - fileLog?: GitLog - ): Promise { - const items: CommandQuickPickItem[] = []; - - const stash = commit.isStash; - - const workingName = - (commit.workingFileName && path.basename(commit.workingFileName)) || path.basename(commit.fileName); - - const isUncommitted = commit.isUncommitted; - if (isUncommitted) { - // Since we can't trust the previous sha on an uncommitted commit, find the last commit for this file - const c = await Container.git.getRecentLogCommitForFile(undefined, commit.uri.fsPath); - if (c === undefined) return undefined; - - commit = c; - } - - await commit.resolvePreviousFileSha(); - - if (stash) { - items.push(new ApplyCommitFileChangesCommandQuickPickItem(commit)); - } - - if (commit.previousFileShortSha) { - items.push( - new CommandQuickPickItem( - { - label: `$(git-compare) Open Changes`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${ - commit.previousFileShortSha - } ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}` - }, - Commands.DiffWithPrevious, - [ - commit.uri, - { - commit - } as DiffWithPreviousCommandArgs - ] - ) - ); - } - - if (commit.workingFileName) { - items.push( - new CommandQuickPickItem( - { - label: `$(git-compare) Open Changes with Working Tree`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${ - GlyphChars.Space - } $(git-compare) ${GlyphChars.Space} $(file-text) ${workingName}` - }, - Commands.DiffWithWorking, - [ - Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), - { - commit - } as DiffWithWorkingCommandArgs - ] - ) - ); - } - - if (commit.workingFileName && commit.status !== 'D') { - items.push(new OpenCommitFileCommandQuickPickItem(commit)); - } - items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit)); - - const remotes = await Container.git.getRemotes(commit.repoPath); - if (remotes.length) { - if (commit.workingFileName && commit.status !== 'D') { - const branch = await Container.git.getBranch(commit.repoPath); - if (branch !== undefined) { - items.push( - new OpenRemotesCommandQuickPickItem( - remotes, - { - type: 'file', - fileName: commit.workingFileName, - branch: branch.name - } as RemoteResource, - currentCommand - ) - ); - } - } - - if (!stash) { - items.push( - new OpenRemotesCommandQuickPickItem( - remotes, - { - type: 'revision', - fileName: commit.fileName, - commit - } as RemoteResource, - currentCommand - ) - ); - } - } - - if (!stash) { - items.push(new ApplyCommitFileChangesCommandQuickPickItem(commit)); - - items.push( - new CommandQuickPickItem( - { - label: `$(clippy) Copy Commit ID to Clipboard`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.shortSha}` - }, - Commands.CopyShaToClipboard, - [ - uri, - { - sha: commit.sha - } as CopyShaToClipboardCommandArgs - ] - ) - ); - - items.push( - new CommandQuickPickItem( - { - label: `$(clippy) Copy Commit Message to Clipboard`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage()}` - }, - Commands.CopyMessageToClipboard, - [ - uri, - { - message: commit.message, - sha: commit.sha - } as CopyMessageToClipboardCommandArgs - ] - ) - ); - } - - if (commit.workingFileName) { - items.push( - new CommandQuickPickItem( - { - label: `$(history) Show File History`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename(commit.fileName)}` - }, - Commands.ShowQuickFileHistory, - [ - Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), - { - fileLog, - goBackCommand: currentCommand - } as ShowQuickFileHistoryCommandArgs - ] - ) - ); - } - - if (!stash) { - items.push( - new CommandQuickPickItem( - { - label: `$(history) Show ${commit.workingFileName ? 'Previous ' : ''}File History`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename( - commit.fileName - )} ${Strings.pad(GlyphChars.Dot, 1, 1)} from ${GlyphChars.Space}$(git-commit) ${ - commit.shortSha - }` - }, - Commands.ShowQuickFileHistory, - [ - commit.toGitUri(), - { - goBackCommand: currentCommand - } as ShowQuickFileHistoryCommandArgs - ] - ) - ); - - items.push( - new CommandQuickPickItem( - { - label: `$(git-commit) Show Commit Details`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha}` - }, - Commands.ShowQuickCommitDetails, - [ - commit.toGitUri(), - { - commit, - sha: commit.sha, - goBackCommand: currentCommand - } as ShowQuickCommitDetailsCommandArgs - ] - ) - ); - } - - if (goBackCommand) { - items.splice(0, 0, goBackCommand); - } - - let previousCommand: KeyCommand | (() => Promise) | undefined = undefined; - let nextCommand: KeyCommand | (() => Promise) | undefined = undefined; - if (!stash) { - // If we have the full history, we are good - if (fileLog !== undefined && !fileLog.truncated && fileLog.sha === undefined) { - previousCommand = - commit.previousSha === undefined - ? undefined - : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ - commit.previousUri, - { - fileLog, - sha: commit.previousSha, - goBackCommand - } as ShowQuickCommitFileDetailsCommandArgs - ]); - - nextCommand = - commit.nextSha === undefined - ? undefined - : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ - commit.nextUri, - { - fileLog, - sha: commit.nextSha, - goBackCommand - } as ShowQuickCommitFileDetailsCommandArgs - ]); - } - else { - previousCommand = async () => { - let log = fileLog; - let c = log && log.commits.get(commit.sha); - - // If we can't find the commit or the previous commit isn't available (since it isn't trustworthy) - if (c === undefined || c.previousSha === undefined) { - log = await Container.git.getLogForFile(commit.repoPath, uri.fsPath, { - maxCount: Container.config.advanced.maxListItems, - ref: commit.sha, - renames: true - }); - if (log === undefined) return KeyNoopCommand; - - c = log && log.commits.get(commit.sha); - // Since we exclude merge commits in file log, just grab the first returned commit - if (c === undefined && commit.isMerge) { - c = Iterables.first(log.commits.values()); - } - - if (c) { - // Copy over next info, since it is trustworthy at this point - c.nextSha = commit.nextSha; - c.nextFileName = commit.nextFileName; - } - } - - if (c === undefined || c.previousSha === undefined) return KeyNoopCommand; - - return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ - c.previousUri, - { - fileLog: log, - sha: c.previousSha, - goBackCommand - } as ShowQuickCommitFileDetailsCommandArgs - ]); - }; - - nextCommand = async () => { - let log = fileLog; - let c = log && log.commits.get(commit.sha); - - // If we can't find the commit or the next commit isn't available (since it isn't trustworthy) - if (c === undefined || c.nextSha === undefined) { - log = undefined; - c = undefined; - - // Try to find the next commit - const next = await Container.git.findNextCommit(commit.repoPath, uri.fsPath, commit.sha); - if (next !== undefined && next.sha !== commit.sha) { - c = commit; - c.nextSha = next.sha; - c.nextFileName = next.originalFileName || next.fileName; - } - } - - if (c === undefined || c.nextSha === undefined) return KeyNoopCommand; - - return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ - c.nextUri, - { - fileLog: log, - sha: c.nextSha, - goBackCommand - } as ShowQuickCommitFileDetailsCommandArgs - ]); - }; - } - } - - const scope = await Container.keyboard.beginScope({ - left: goBackCommand, - ',': previousCommand, - '.': nextCommand - }); - - const pick = await window.showQuickPick(items, { - matchOnDescription: true, - placeHolder: `${commit.getFormattedPath()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${ - isUncommitted ? `Uncommitted ${GlyphChars.ArrowRightHollow} ` : '' - }${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.author}, ${ - commit.formattedDate - } ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getShortMessage()}`, - ignoreFocusOut: getQuickPickIgnoreFocusOut(), - onDidSelectItem: (item: QuickPickItem) => { - scope.setKeyCommand('right', item as KeyCommand); - } - } as QuickPickOptions); - - await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/commitQuickPick.ts b/src/quickPicks/commitQuickPick.ts deleted file mode 100644 index 11a79e8..0000000 --- a/src/quickPicks/commitQuickPick.ts +++ /dev/null @@ -1,419 +0,0 @@ -'use strict'; -import * as path from 'path'; -import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from 'vscode'; -import { - Commands, - CopyMessageToClipboardCommandArgs, - CopyShaToClipboardCommandArgs, - DiffDirectoryCommandArgs, - DiffWithPreviousCommandArgs, - ShowQuickCommitDetailsCommandArgs, - StashApplyCommandArgs, - StashDeleteCommandArgs -} from '../commands'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { - getGitStatusOcticon, - GitLog, - GitLogCommit, - GitStashCommit, - GitStatusFile, - GitStatusFileStatus, - GitUri, - IGitStatusFile, - RemoteResource -} from '../gitService'; -import { KeyCommand, KeyNoopCommand, Keys } from '../keyboard'; -import { Arrays, Iterables, Strings } from '../system'; -import { - CommandQuickPickItem, - getQuickPickIgnoreFocusOut, - KeyCommandQuickPickItem, - OpenFileCommandQuickPickItem, - OpenFilesCommandQuickPickItem, - QuickPickItem, - ShowCommitInResultsQuickPickItem -} from './commonQuickPicks'; -import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; - -export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickItem { - readonly status: GitStatusFileStatus; - - readonly commit: GitLogCommit; - - constructor(commit: GitLogCommit, status: IGitStatusFile) { - const octicon = getGitStatusOcticon(status.status); - const description = GitStatusFile.getFormattedDirectory(status, true); - - super(GitUri.toRevisionUri(commit.sha, status, commit.repoPath), { - label: `${Strings.pad(octicon, 4, 2)} ${path.basename(status.fileName)}`, - description: description - }); - - this.commit = commit.toFileCommit(status); - this.status = status.status; - } - - get sha(): string { - return this.commit.sha; - } - - onDidPressKey(key: Keys): Promise<{} | undefined> { - if (this.commit.previousSha === undefined) return super.onDidPressKey(key); - - return commands.executeCommand(Commands.DiffWithPrevious, this.commit.toGitUri(), { - commit: this.commit, - showOptions: { - preserveFocus: true, - preview: false - } as TextDocumentShowOptions - } as DiffWithPreviousCommandArgs) as Promise<{} | undefined>; - } -} - -export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem { - constructor(commit: GitLogCommit, versioned: boolean = false, item?: QuickPickItem) { - const repoPath = commit.repoPath; - const uris = Arrays.filterMap(commit.fileStatuses, f => GitUri.fromFileStatus(f, repoPath)); - - super( - uris, - item || { - label: `$(file-symlink-file) Open Files`, - description: '' - // detail: `Opens all of the changed file in the working tree` - } - ); - } -} - -export class OpenCommitFileRevisionsCommandQuickPickItem extends OpenFilesCommandQuickPickItem { - constructor(commit: GitLogCommit, item?: QuickPickItem) { - const uris = Arrays.filterMap(commit.fileStatuses, f => - GitUri.toRevisionUri(f.status === 'D' ? commit.previousFileSha : commit.sha, f, commit.repoPath) - ); - - super( - uris, - item || { - label: `$(file-symlink-file) Open Revisions`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} in ${GlyphChars.Space}$(git-commit) ${ - commit.shortSha - }` - // detail: `Opens all of the changed files in $(git-commit) ${commit.shortSha}` - } - ); - } -} - -export class CommitQuickPick { - static async show( - commit: GitLogCommit, - uri: Uri, - goBackCommand?: CommandQuickPickItem, - currentCommand?: CommandQuickPickItem, - repoLog?: GitLog - ): Promise { - await commit.resolvePreviousFileSha(); - - const items: (CommitWithFileStatusQuickPickItem | CommandQuickPickItem)[] = commit.fileStatuses.map( - fs => new CommitWithFileStatusQuickPickItem(commit, fs) - ); - - const stash = commit.isStash; - - let index = 0; - - if (stash) { - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `$(git-pull-request) Apply Stashed Changes`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage()}` - }, - Commands.StashApply, - [ - { - confirm: true, - deleteAfter: false, - stashItem: commit as GitStashCommit, - goBackCommand: currentCommand - } as StashApplyCommandArgs - ] - ) - ); - - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `$(x) Delete Stashed Changes`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage()}` - }, - Commands.StashDelete, - [ - { - confirm: true, - stashItem: commit as GitStashCommit, - goBackCommand: currentCommand - } as StashDeleteCommandArgs - ] - ) - ); - - items.splice(index++, 0, new ShowCommitInResultsQuickPickItem(commit)); - } - else { - items.splice(index++, 0, new ShowCommitInResultsQuickPickItem(commit)); - - const remotes = await Container.git.getRemotes(commit.repoPath); - if (remotes.length) { - items.splice( - index++, - 0, - new OpenRemotesCommandQuickPickItem( - remotes, - { - type: 'commit', - sha: commit.sha - } as RemoteResource, - currentCommand - ) - ); - } - } - - items.splice(index++, 0, new OpenCommitFilesCommandQuickPickItem(commit)); - items.splice(index++, 0, new OpenCommitFileRevisionsCommandQuickPickItem(commit)); - - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `$(git-compare) Open Directory Compare with Previous Revision`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.previousFileShortSha} ${ - GlyphChars.Space - } $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}` - }, - Commands.DiffDirectory, - [ - commit.uri, - { - ref1: commit.previousFileSha, - ref2: commit.sha - } as DiffDirectoryCommandArgs - ] - ) - ); - - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `$(git-compare) Open Directory Compare with Working Tree`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${ - GlyphChars.Space - } $(git-compare) ${GlyphChars.Space} $(file-directory) Working Tree` - }, - Commands.DiffDirectory, - [ - uri, - { - ref1: commit.sha - } as DiffDirectoryCommandArgs - ] - ) - ); - - if (!stash) { - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `$(clippy) Copy Commit ID to Clipboard`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.shortSha}` - }, - Commands.CopyShaToClipboard, - [ - uri, - { - sha: commit.sha - } as CopyShaToClipboardCommandArgs - ] - ) - ); - } - - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `$(clippy) Copy Commit Message to Clipboard`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage()}` - }, - Commands.CopyMessageToClipboard, - [ - uri, - { - message: commit.message, - sha: commit.sha - } as CopyMessageToClipboardCommandArgs - ] - ) - ); - - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `Changed Files`, - description: commit.getDiffStatus() - }, - Commands.ShowQuickCommitDetails, - [ - uri, - { - commit, - repoLog, - sha: commit.sha, - goBackCommand - } as ShowQuickCommitDetailsCommandArgs - ] - ) - ); - - if (goBackCommand) { - items.splice(0, 0, goBackCommand); - } - - let previousCommand: KeyCommand | (() => Promise) | undefined = undefined; - let nextCommand: KeyCommand | (() => Promise) | undefined = undefined; - if (!stash) { - // If we have the full history, we are good - if (repoLog !== undefined && !repoLog.truncated && repoLog.sha === undefined) { - previousCommand = - commit.previousSha === undefined - ? undefined - : new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [ - commit.previousUri, - { - repoLog, - sha: commit.previousSha, - goBackCommand - } as ShowQuickCommitDetailsCommandArgs - ]); - - nextCommand = - commit.nextSha === undefined - ? undefined - : new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [ - commit.nextUri, - { - repoLog, - sha: commit.nextSha, - goBackCommand - } as ShowQuickCommitDetailsCommandArgs - ]); - } - else { - previousCommand = async () => { - let log = repoLog; - let c = log && log.commits.get(commit.sha); - - // If we can't find the commit or the previous commit isn't available (since it isn't trustworthy) - if (c === undefined || c.previousSha === undefined) { - log = await Container.git.getLog(commit.repoPath, { - maxCount: Container.config.advanced.maxListItems, - ref: commit.sha - }); - c = log && log.commits.get(commit.sha); - - if (c) { - // Copy over next info, since it is trustworthy at this point - c.nextSha = commit.nextSha; - } - } - - if (c === undefined || c.previousSha === undefined) return KeyNoopCommand; - - return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [ - c.previousUri, - { - repoLog: log, - sha: c.previousSha, - goBackCommand - } as ShowQuickCommitDetailsCommandArgs - ]); - }; - - nextCommand = async () => { - let log = repoLog; - let c = log && log.commits.get(commit.sha); - - // If we can't find the commit or the next commit isn't available (since it isn't trustworthy) - if (c === undefined || c.nextSha === undefined) { - log = undefined; - c = undefined; - - // Try to find the next commit - const nextLog = await Container.git.getLog(commit.repoPath, { - maxCount: 1, - reverse: true, - ref: commit.sha - }); - const next = nextLog && Iterables.first(nextLog.commits.values()); - if (next !== undefined && next.sha !== commit.sha) { - c = commit; - c.nextSha = next.sha; - } - } - - if (c === undefined || c.nextSha === undefined) return KeyNoopCommand; - - return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [ - c.nextUri, - { - repoLog: log, - sha: c.nextSha, - goBackCommand - } as ShowQuickCommitDetailsCommandArgs - ]); - }; - } - } - - const scope = await Container.keyboard.beginScope({ - left: goBackCommand, - ',': previousCommand, - '.': nextCommand - }); - - const pick = await window.showQuickPick(items, { - matchOnDescription: true, - matchOnDetail: true, - placeHolder: `${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${ - commit.author ? `${commit.author}, ` : '' - }${commit.formattedDate} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getShortMessage()}`, - ignoreFocusOut: getQuickPickIgnoreFocusOut(), - onDidSelectItem: (item: QuickPickItem) => { - scope.setKeyCommand('right', item); - if (typeof item.onDidSelect === 'function') { - item.onDidSelect(); - } - } - } as QuickPickOptions); - - await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/commitsQuickPick.ts b/src/quickPicks/commitsQuickPick.ts deleted file mode 100644 index 33bcb48..0000000 --- a/src/quickPicks/commitsQuickPick.ts +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; -import { CancellationTokenSource, QuickPickOptions, window } from 'vscode'; -import { Container } from '../container'; -import { GitLog } from '../gitService'; -import { KeyNoopCommand } from '../keyboard'; -import { Iterables } from '../system'; -import { - CommandQuickPickItem, - CommitQuickPickItem, - getQuickPickIgnoreFocusOut, - MessageQuickPickItem, - showQuickPickProgress -} from './commonQuickPicks'; - -export class CommitsQuickPick { - static showProgress(message: string) { - return showQuickPickProgress(message, { - left: KeyNoopCommand, - ',': KeyNoopCommand, - '.': KeyNoopCommand - }); - } - - static async show( - log: GitLog | undefined, - placeHolder: string, - progressCancellation: CancellationTokenSource, - options: { - goBackCommand?: CommandQuickPickItem; - showAllCommand?: CommandQuickPickItem; - showInResultsExplorerCommand?: CommandQuickPickItem; - } - ): Promise { - const items = ((log && [...Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))]) || [ - new MessageQuickPickItem('No results found') - ]) as (CommitQuickPickItem | CommandQuickPickItem)[]; - - if (options.showInResultsExplorerCommand !== undefined) { - items.splice(0, 0, options.showInResultsExplorerCommand); - } - - if (options.showAllCommand !== undefined) { - items.splice(0, 0, options.showAllCommand); - } - - if (options.goBackCommand !== undefined) { - items.splice(0, 0, options.goBackCommand); - } - - if (progressCancellation.token.isCancellationRequested) return undefined; - - const scope = await Container.keyboard.beginScope({ left: options.goBackCommand }); - - progressCancellation.cancel(); - - const pick = await window.showQuickPick(items, { - matchOnDescription: true, - placeHolder: placeHolder, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - // onDidSelectItem: (item: QuickPickItem) => { - // scope.setKeyCommand('right', item); - // } - } as QuickPickOptions); - - await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/commonQuickPicks.ts b/src/quickPicks/commonQuickPicks.ts deleted file mode 100644 index 4f43a9b..0000000 --- a/src/quickPicks/commonQuickPicks.ts +++ /dev/null @@ -1,282 +0,0 @@ -'use strict'; -import { - CancellationTokenSource, - commands, - QuickPickItem, - QuickPickOptions, - TextDocumentShowOptions, - TextEditor, - Uri, - window -} from 'vscode'; -import { Commands, openEditor } from '../commands'; -import { configuration } from '../configuration'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { GitLog, GitLogCommit, GitStashCommit } from '../gitService'; -import { KeyMapping, Keys } from '../keyboard'; -import { Strings } from '../system'; -import { BranchesAndTagsQuickPick, BranchOrTagQuickPickItem } from './branchesAndTagsQuickPick'; - -export function getQuickPickIgnoreFocusOut() { - return !configuration.get(configuration.name('advanced')('quickPick')('closeOnFocusOut').value); -} - -export function showQuickPickProgress(message: string, mapping?: KeyMapping): CancellationTokenSource { - const cancellation = new CancellationTokenSource(); - _showQuickPickProgress(message, cancellation, mapping); - return cancellation; -} - -async function _showQuickPickProgress(message: string, cancellation: CancellationTokenSource, mapping?: KeyMapping) { - const scope = mapping && (await Container.keyboard.beginScope(mapping)); - - try { - await window.showQuickPick( - _getInfiniteCancellablePromise(cancellation), - { - placeHolder: message, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - } as QuickPickOptions, - cancellation.token - ); - } - catch (ex) { - // Not sure why this throws - } - finally { - cancellation.cancel(); - scope && scope.dispose(); - } -} - -function _getInfiniteCancellablePromise(cancellation: CancellationTokenSource) { - return new Promise((resolve, reject) => { - const disposable = cancellation.token.onCancellationRequested(() => { - disposable.dispose(); - resolve([]); - }); - }); -} - -export interface QuickPickItem extends QuickPickItem { - onDidSelect?(): void; - onDidPressKey?(key: Keys): Promise<{} | undefined>; -} - -export class CommandQuickPickItem implements QuickPickItem { - label!: string; - description!: string; - detail?: string | undefined; - protected command: Commands | undefined; - protected args: any[] | undefined; - - constructor(item: QuickPickItem, args?: [Commands, any[]]); - constructor(item: QuickPickItem, command?: Commands, args?: any[]); - constructor(item: QuickPickItem, commandOrArgs?: Commands | [Commands, any[]], args?: any[]) { - if (commandOrArgs === undefined) { - this.command = undefined; - this.args = args; - } - else if (typeof commandOrArgs === 'string') { - this.command = commandOrArgs; - this.args = args; - } - else { - this.command = commandOrArgs[0]; - this.args = commandOrArgs.slice(1); - } - Object.assign(this, item); - } - - execute(): Promise<{} | undefined> { - if (this.command === undefined) return Promise.resolve(undefined); - - return commands.executeCommand(this.command, ...(this.args || [])) as Promise<{} | undefined>; - } - - onDidPressKey(key: Keys): Promise<{} | undefined> { - return this.execute(); - } -} - -export class CommitQuickPickItem implements QuickPickItem { - label: string; - description: string; - detail: string; - - constructor( - public readonly commit: GitLogCommit - ) { - const message = commit.getShortMessage(); - if (commit.isStash) { - this.label = message; - this.description = ''; - this.detail = `${GlyphChars.Space} ${(commit as GitStashCommit).stashName || commit.shortSha} ${Strings.pad( - GlyphChars.Dot, - 1, - 1 - )} ${commit.formattedDate} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getDiffStatus()}`; - } - else { - this.label = message; - this.description = `${Strings.pad('$(git-commit)', 1, 1)} ${commit.shortSha}`; - this.detail = `${GlyphChars.Space} ${commit.author}, ${commit.formattedDate}${ - commit.isFile ? '' : ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getDiffStatus()}` - }`; - } - } -} - -export class ChooseFromBranchesAndTagsQuickPickItem extends CommandQuickPickItem { - constructor( - private readonly repoPath: string, - private readonly placeHolder: string, - private readonly goBackCommand?: CommandQuickPickItem, - item: QuickPickItem = { - label: 'Choose from Branch or Tag History...', - description: `${Strings.pad(GlyphChars.Dash, 2, 2)} shows list of branches and tags` - } - ) { - super(item, undefined, undefined); - } - - async execute( - options: TextDocumentShowOptions = { preserveFocus: false, preview: false } - ): Promise { - const progressCancellation = BranchesAndTagsQuickPick.showProgress(this.placeHolder); - - try { - const [branches, tags] = await Promise.all([ - Container.git.getBranches(this.repoPath), - Container.git.getTags(this.repoPath) - ]); - - if (progressCancellation.token.isCancellationRequested) return undefined; - - return BranchesAndTagsQuickPick.show(branches, tags, this.placeHolder, { - progressCancellation: progressCancellation, - goBackCommand: this.goBackCommand - }); - } - finally { - progressCancellation.cancel(); - } - } -} - -export class KeyCommandQuickPickItem extends CommandQuickPickItem { - constructor(command: Commands, args?: any[]) { - super({ label: '', description: '' } as QuickPickItem, command, args); - } -} - -export class MessageQuickPickItem extends CommandQuickPickItem { - constructor(message: string) { - super({ label: message, description: '' } as QuickPickItem); - } -} - -export class OpenFileCommandQuickPickItem extends CommandQuickPickItem { - constructor( - public readonly uri: Uri, - item: QuickPickItem - ) { - super(item, undefined, undefined); - } - - async execute(options?: TextDocumentShowOptions): Promise { - return openEditor(this.uri, options); - } - - // onDidSelect(): Promise<{} | undefined> { - // return this.execute({ - // preserveFocus: true, - // preview: true - // }); - // } - - onDidPressKey(key: Keys): Promise<{} | undefined> { - return this.execute({ - preserveFocus: true, - preview: false - }); - } -} - -export class OpenFilesCommandQuickPickItem extends CommandQuickPickItem { - constructor( - public readonly uris: Uri[], - item: QuickPickItem - ) { - super(item, undefined, undefined); - } - - async execute( - options: TextDocumentShowOptions = { preserveFocus: false, preview: false } - ): Promise<{} | undefined> { - for (const uri of this.uris) { - await openEditor(uri, options); - } - return undefined; - } - - async onDidPressKey(key: Keys): Promise<{} | undefined> { - return this.execute({ - preserveFocus: true, - preview: false - }); - } -} - -export class ShowCommitInResultsQuickPickItem extends CommandQuickPickItem { - constructor( - public readonly commit: GitLogCommit, - item: QuickPickItem = { - label: 'Show in Results', - description: `${Strings.pad(GlyphChars.Dash, 2, 2)} displays commit in the GitLens Results explorer` - } - ) { - super(item, undefined, undefined); - } - - async execute( - options: TextDocumentShowOptions = { preserveFocus: false, preview: false } - ): Promise<{} | undefined> { - Container.resultsExplorer.showCommitInResults(this.commit); - return undefined; - } -} - -export class ShowCommitsInResultsQuickPickItem extends CommandQuickPickItem { - constructor( - public readonly results: GitLog, - public readonly resultsLabel: string | { label: string; resultsType?: { singular: string; plural: string } }, - item: QuickPickItem = { - label: 'Show in Results', - description: `${Strings.pad(GlyphChars.Dash, 2, 2)} displays commits in the GitLens Results explorer` - } - ) { - super(item, undefined, undefined); - } - - async execute( - options: TextDocumentShowOptions = { preserveFocus: false, preview: false } - ): Promise<{} | undefined> { - Container.resultsExplorer.showCommitsInResults(this.results, this.resultsLabel); - return undefined; - } -} - -export class ShowCommitsSearchInResultsQuickPickItem extends ShowCommitsInResultsQuickPickItem { - constructor( - public readonly results: GitLog, - public readonly search: string, - item: QuickPickItem = { - label: 'Show in Results', - description: `${Strings.pad(GlyphChars.Dash, 2, 2)} displays results in the GitLens Results explorer` - } - ) { - super(results, { label: search }, item); - } -} diff --git a/src/quickPicks/fileHistoryQuickPick.ts b/src/quickPicks/fileHistoryQuickPick.ts deleted file mode 100644 index c6c6340..0000000 --- a/src/quickPicks/fileHistoryQuickPick.ts +++ /dev/null @@ -1,224 +0,0 @@ -'use strict'; -import * as path from 'path'; -import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode'; -import { Commands, ShowQuickCurrentBranchHistoryCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { GitLog, GitUri, RemoteResource } from '../gitService'; -import { KeyNoopCommand } from '../keyboard'; -import { Iterables, Strings } from '../system'; -import { - ChooseFromBranchesAndTagsQuickPickItem, - CommandQuickPickItem, - CommitQuickPickItem, - getQuickPickIgnoreFocusOut, - showQuickPickProgress -} from './commonQuickPicks'; -import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; - -export class FileHistoryQuickPick { - static showProgress(placeHolder: string) { - return showQuickPickProgress(placeHolder, { - left: KeyNoopCommand, - ',': KeyNoopCommand, - '.': KeyNoopCommand - }); - } - - static async show( - log: GitLog, - uri: GitUri, - placeHolder: string, - options: { - currentCommand?: CommandQuickPickItem; - goBackCommand?: CommandQuickPickItem; - nextPageCommand?: CommandQuickPickItem; - previousPageCommand?: CommandQuickPickItem; - pickerOnly?: boolean; - progressCancellation?: CancellationTokenSource; - showAllCommand?: CommandQuickPickItem; - showInResultsExplorerCommand?: CommandQuickPickItem; - } = {} - ): Promise { - options = { pickerOnly: false, ...options }; - - const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as ( - | CommitQuickPickItem - | CommandQuickPickItem)[]; - - let index = 0; - - index++; - items.splice( - 0, - 0, - new ChooseFromBranchesAndTagsQuickPickItem(log.repoPath, placeHolder, options.currentCommand) - ); - - if (options.showInResultsExplorerCommand !== undefined) { - index++; - items.splice(0, 0, options.showInResultsExplorerCommand); - } - - if (log.truncated || log.sha) { - if (options.showAllCommand !== undefined) { - index++; - items.splice(0, 0, options.showAllCommand); - } - else if (!options.pickerOnly) { - const [workingFileName] = await Container.git.findWorkingFileName( - path.relative(log.repoPath, uri.fsPath), - log.repoPath - ); - if (workingFileName) { - index++; - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `$(history) Show File History`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename( - workingFileName - )}` - }, - Commands.ShowQuickFileHistory, - [ - Uri.file(path.resolve(log.repoPath, workingFileName)), - { - goBackCommand: new CommandQuickPickItem( - { - label: `go back ${GlyphChars.ArrowBack}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${ - GlyphChars.Space - }$(file-text) ${path.basename(uri.fsPath)}${ - uri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${uri.shortSha}` : '' - }` - }, - Commands.ShowQuickFileHistory, - [ - uri, - { - log: log, - maxCount: log.maxCount, - range: log.range, - goBackCommand: options.goBackCommand - } as ShowQuickFileHistoryCommandArgs - ] - ) - } as ShowQuickFileHistoryCommandArgs - ] - ) - ); - } - } - - if (options.nextPageCommand !== undefined) { - index++; - items.splice(0, 0, options.nextPageCommand); - } - - if (options.previousPageCommand !== undefined) { - index++; - items.splice(0, 0, options.previousPageCommand); - } - } - - if (!options.pickerOnly) { - const branch = await Container.git.getBranch(uri.repoPath!); - - if (branch !== undefined) { - const currentCommand = new CommandQuickPickItem( - { - label: `go back ${GlyphChars.ArrowBack}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${ - GlyphChars.Space - }$(file-text) ${path.basename(uri.fsPath)}${ - uri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${uri.shortSha}` : '' - }` - }, - Commands.ShowQuickFileHistory, - [ - uri, - { - log, - maxCount: log.maxCount, - range: log.range - } as ShowQuickFileHistoryCommandArgs - ] - ); - - // Only show the full repo option if we are the root - if (options.goBackCommand === undefined) { - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `$(history) Show Branch History`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${ - GlyphChars.Space - }$(git-branch) ${branch.name} history` - }, - Commands.ShowQuickCurrentBranchHistory, - [ - undefined, - { - goBackCommand: currentCommand - } as ShowQuickCurrentBranchHistoryCommandArgs - ] - ) - ); - } - - const remotes = await Container.git.getRemotes(uri.repoPath!); - if (remotes.length) { - const resource = - uri.sha !== undefined - ? ({ - type: 'revision', - branch: branch.name, - fileName: uri.getRelativePath(), - sha: uri.sha - } as RemoteResource) - : ({ - type: 'file', - branch: branch.name, - fileName: uri.getRelativePath() - } as RemoteResource); - items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, resource, currentCommand)); - } - } - - if (options.goBackCommand) { - items.splice(0, 0, options.goBackCommand); - } - } - - if (options.progressCancellation !== undefined && options.progressCancellation.token.isCancellationRequested) { - return undefined; - } - - const scope = await Container.keyboard.beginScope({ - left: options.goBackCommand, - ',': options.previousPageCommand, - '.': options.nextPageCommand - }); - - options.progressCancellation && options.progressCancellation.cancel(); - - const pick = await window.showQuickPick(items, { - matchOnDescription: true, - matchOnDetail: true, - placeHolder: placeHolder, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - // onDidSelectItem: (item: QuickPickItem) => { - // scope.setKeyCommand('right', item); - // } - } as QuickPickOptions); - - await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/modesQuickPick.ts b/src/quickPicks/modesQuickPick.ts deleted file mode 100644 index 3fcee6e..0000000 --- a/src/quickPicks/modesQuickPick.ts +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; -import { QuickPickItem, QuickPickOptions, window } from 'vscode'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; - -export interface ModesQuickPickItem extends QuickPickItem { - key: string | undefined; -} - -export class ModesQuickPick { - static async show(): Promise { - const modes = Object.keys(Container.config.modes); - if (modes.length === 0) return undefined; - - const mode = Container.config.mode.active; - - const items = modes.map(key => { - const modeCfg = Container.config.modes[key]; - return { - label: `${mode === key ? '$(check)\u00a0\u00a0' : '\u00a0\u00a0\u00a0\u00a0\u00a0'}${ - modeCfg.name - } mode`, - description: modeCfg.description ? `\u00a0${GlyphChars.Dash}\u00a0 ${modeCfg.description}` : '', - key: key - } as ModesQuickPickItem; - }); - - if (mode) { - items.splice(0, 0, { - label: `Exit ${Container.config.modes[mode].name} mode`, - key: undefined - } as ModesQuickPickItem); - } - - const pick = await window.showQuickPick(items, { - placeHolder: 'select a GitLens mode to enter' - } as QuickPickOptions); - - return pick; - } -} diff --git a/src/quickPicks/remotesQuickPick.ts b/src/quickPicks/remotesQuickPick.ts deleted file mode 100644 index 347ab76..0000000 --- a/src/quickPicks/remotesQuickPick.ts +++ /dev/null @@ -1,167 +0,0 @@ -'use strict'; -import * as path from 'path'; -import { QuickPickOptions, window } from 'vscode'; -import { Commands, OpenInRemoteCommandArgs } from '../commands'; -import { GlyphChars } from '../constants'; -import { - getNameFromRemoteResource, - GitLogCommit, - GitRemote, - GitService, - RemoteResource, - RemoteResourceType -} from '../gitService'; -import { Strings } from '../system'; -import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; - -export class OpenRemoteCommandQuickPickItem extends CommandQuickPickItem { - private remote: GitRemote; - private resource: RemoteResource; - - constructor(remote: GitRemote, resource: RemoteResource) { - super( - { - label: `$(link-external) Open ${getNameFromRemoteResource(resource)} in ${remote.provider!.name}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(repo) ${remote.provider!.path}` - }, - undefined, - undefined - ); - - this.remote = remote; - this.resource = resource; - } - - async execute(): Promise<{} | undefined> { - return this.remote.provider!.open(this.resource); - } -} - -export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem { - constructor(remotes: GitRemote[], resource: RemoteResource, goBackCommand?: CommandQuickPickItem) { - const name = getNameFromRemoteResource(resource); - - let description = ''; - switch (resource.type) { - case RemoteResourceType.Branch: - description = `$(git-branch) ${resource.branch}`; - break; - - case RemoteResourceType.Branches: - description = `$(git-branch) Branches`; - break; - - case RemoteResourceType.Commit: - const shortSha = GitService.shortenSha(resource.sha); - description = `$(git-commit) ${shortSha}`; - break; - - case RemoteResourceType.File: - description = `$(file-text) ${path.basename(resource.fileName)}`; - break; - - case RemoteResourceType.Repo: - description = `$(repo) Repository`; - break; - - case RemoteResourceType.Revision: - if (resource.commit !== undefined && resource.commit instanceof GitLogCommit) { - if (resource.commit.status === 'D') { - resource.sha = resource.commit.previousSha; - description = `$(file-text) ${path.basename(resource.fileName)} in ${ - GlyphChars.Space - }$(git-commit) ${resource.commit.previousShortSha} (deleted in ${ - GlyphChars.Space - }$(git-commit) ${resource.commit.shortSha})`; - } - else { - resource.sha = resource.commit.sha; - description = `$(file-text) ${path.basename(resource.fileName)} in ${ - GlyphChars.Space - }$(git-commit) ${resource.commit.shortSha}`; - } - } - else { - const shortFileSha = resource.sha === undefined ? '' : GitService.shortenSha(resource.sha); - description = `$(file-text) ${path.basename(resource.fileName)}${ - shortFileSha ? ` in ${GlyphChars.Space}$(git-commit) ${shortFileSha}` : '' - }`; - } - break; - } - - const remote = remotes[0]; - if (remotes.length === 1) { - super( - { - label: `$(link-external) Open ${name} in ${remote.provider!.name}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(repo) ${remote.provider!.path} ${Strings.pad( - GlyphChars.Dot, - 1, - 1 - )} ${description}` - }, - Commands.OpenInRemote, - [ - undefined, - { - remotes, - resource, - goBackCommand - } as OpenInRemoteCommandArgs - ] - ); - - return; - } - - const provider = remotes.every(r => r.provider !== undefined && r.provider.name === remote.provider!.name) - ? remote.provider!.name - : 'Remote'; - - super( - { - label: `$(link-external) Open ${name} in ${provider}${GlyphChars.Ellipsis}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${description}` - }, - Commands.OpenInRemote, - [ - undefined, - { - remotes, - resource, - goBackCommand - } as OpenInRemoteCommandArgs - ] - ); - } -} - -export class RemotesQuickPick { - static async show( - remotes: GitRemote[], - placeHolder: string, - resource: RemoteResource, - goBackCommand?: CommandQuickPickItem - ): Promise { - const items = remotes.map(r => new OpenRemoteCommandQuickPickItem(r, resource)) as ( - | OpenRemoteCommandQuickPickItem - | CommandQuickPickItem)[]; - - if (goBackCommand) { - items.splice(0, 0, goBackCommand); - } - - // const scope = await Container.keyboard.beginScope({ left: goBackCommand }); - - const pick = await window.showQuickPick(items, { - placeHolder: placeHolder, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - } as QuickPickOptions); - if (pick === undefined) return undefined; - - // await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/repoStatusQuickPick.ts b/src/quickPicks/repoStatusQuickPick.ts deleted file mode 100644 index 86e8f53..0000000 --- a/src/quickPicks/repoStatusQuickPick.ts +++ /dev/null @@ -1,478 +0,0 @@ -'use strict'; -import * as path from 'path'; -import { commands, QuickPickOptions, TextDocumentShowOptions, window } from 'vscode'; -import { - Commands, - DiffWithPreviousCommandArgs, - OpenChangedFilesCommandArgs, - ShowQuickBranchHistoryCommandArgs, - ShowQuickRepoStatusCommandArgs, - ShowQuickStashListCommandArgs -} from '../commands'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { - GitCommitType, - GitLogCommit, - GitService, - GitStatus, - GitStatusFile, - GitStatusFileStatus, - GitUri -} from '../gitService'; -import { Keys } from '../keyboard'; -import { Iterables, Strings } from '../system'; -import { - CommandQuickPickItem, - getQuickPickIgnoreFocusOut, - OpenFileCommandQuickPickItem, - QuickPickItem -} from './commonQuickPicks'; - -export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPickItem { - public readonly status: GitStatusFile; - private readonly commit: GitLogCommit; - - constructor(status: GitStatusFile, realIndexStatus?: GitStatusFileStatus, item?: QuickPickItem) { - const octicon = status.getOcticon(); - const description = status.getFormattedDirectory(true); - - super( - status.uri, - item || { - label: `${status.staged ? '$(check)' : GlyphChars.Space.repeat(3)}${Strings.pad( - octicon, - 2, - 2 - )} ${path.basename(status.fileName)}`, - description: description - } - ); - - this.status = status; - if (status.indexStatus !== undefined) { - this.commit = new GitLogCommit( - GitCommitType.File, - status.repoPath, - GitService.stagedUncommittedSha, - 'You', - undefined, - new Date(), - '', - status.fileName, - [status], - status.status, - status.originalFileName, - 'HEAD', - status.fileName - ); - } - else { - this.commit = new GitLogCommit( - GitCommitType.File, - status.repoPath, - GitService.uncommittedSha, - 'You', - undefined, - new Date(), - '', - status.fileName, - [status], - status.status, - status.originalFileName, - realIndexStatus !== undefined ? GitService.stagedUncommittedSha : 'HEAD', - status.fileName - ); - } - } - - onDidPressKey(key: Keys): Promise<{} | undefined> { - return commands.executeCommand( - Commands.DiffWithPrevious, - GitUri.fromFileStatus(this.status, this.status.repoPath), - { - commit: this.commit, - line: 0, - showOptions: { - preserveFocus: true, - preview: false - } as TextDocumentShowOptions - } as DiffWithPreviousCommandArgs - ) as Promise<{} | undefined>; - } -} - -export class OpenStatusFilesCommandQuickPickItem extends CommandQuickPickItem { - constructor(statuses: GitStatusFile[], item?: QuickPickItem) { - const uris = statuses.map(f => f.uri); - - super( - item || { - label: `$(file-symlink-file) Open Changed Files`, - description: '' - // detail: `Opens all of the changed files in the repository` - }, - Commands.OpenChangedFiles, - [ - undefined, - { - uris - } as OpenChangedFilesCommandArgs - ] - ); - } -} - -interface ComputedStatus { - staged: number; - stagedAddsAndChanges: GitStatusFile[]; - stagedStatus: string; - - unstaged: number; - unstagedAddsAndChanges: GitStatusFile[]; - unstagedStatus: string; -} - -export class RepoStatusQuickPick { - private static computeStatus(files: GitStatusFile[]): ComputedStatus { - let stagedAdds = 0; - let unstagedAdds = 0; - let stagedChanges = 0; - let unstagedChanges = 0; - let stagedDeletes = 0; - let unstagedDeletes = 0; - - const stagedAddsAndChanges: GitStatusFile[] = []; - const unstagedAddsAndChanges: GitStatusFile[] = []; - - for (const f of files) { - switch (f.indexStatus) { - case 'A': - case '?': - stagedAdds++; - stagedAddsAndChanges.push(f); - break; - - case 'D': - stagedDeletes++; - break; - - case undefined: - break; - - default: - stagedChanges++; - stagedAddsAndChanges.push(f); - break; - } - - switch (f.workTreeStatus) { - case 'A': - case '?': - unstagedAdds++; - unstagedAddsAndChanges.push(f); - break; - - case 'D': - unstagedDeletes++; - break; - - case undefined: - break; - - default: - unstagedChanges++; - unstagedAddsAndChanges.push(f); - break; - } - } - - const staged = stagedAdds + stagedChanges + stagedDeletes; - const unstaged = unstagedAdds + unstagedChanges + unstagedDeletes; - - return { - staged: staged, - stagedStatus: staged > 0 ? `+${stagedAdds} ~${stagedChanges} -${stagedDeletes}` : '', - stagedAddsAndChanges: stagedAddsAndChanges, - unstaged: unstaged, - unstagedStatus: unstaged > 0 ? `+${unstagedAdds} ~${unstagedChanges} -${unstagedDeletes}` : '', - unstagedAddsAndChanges: unstagedAddsAndChanges - }; - } - - static async show( - status: GitStatus, - goBackCommand?: CommandQuickPickItem - ): Promise< - OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem | undefined - > { - const items = [ - ...Iterables.flatMap(status.files, s => { - if (s.workTreeStatus !== undefined && s.indexStatus !== undefined) { - return [ - new OpenStatusFileCommandQuickPickItem(s.with({ indexStatus: null }), s.indexStatus), - new OpenStatusFileCommandQuickPickItem(s.with({ workTreeStatus: null })) - ]; - } - else { - return [new OpenStatusFileCommandQuickPickItem(s)]; - } - }) - ] as (OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem)[]; - - // Sort the status by staged and then filename - items.sort( - (a, b) => - ((a as OpenStatusFileCommandQuickPickItem).status.staged ? -1 : 1) - - ((b as OpenStatusFileCommandQuickPickItem).status.staged ? -1 : 1) || - (a as OpenStatusFileCommandQuickPickItem).status.fileName.localeCompare( - (b as OpenStatusFileCommandQuickPickItem).status.fileName - ) - ); - - const currentCommand = new CommandQuickPickItem( - { - label: `go back ${GlyphChars.ArrowBack}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to ${GlyphChars.Space}$(git-branch) ${ - status.branch - } status` - }, - Commands.ShowQuickRepoStatus, - [ - undefined, - { - goBackCommand - } as ShowQuickRepoStatusCommandArgs - ] - ); - - const computed = this.computeStatus(status.files); - if (computed.staged > 0) { - let index = 0; - const unstagedIndex = computed.unstaged > 0 ? status.files.findIndex(f => !f.staged) : -1; - if (unstagedIndex > -1) { - items.splice( - unstagedIndex, - 0, - new CommandQuickPickItem( - { - label: `Unstaged Files`, - description: computed.unstagedStatus - }, - Commands.ShowQuickRepoStatus, - [ - undefined, - { - goBackCommand - } as ShowQuickRepoStatusCommandArgs - ] - ) - ); - - items.splice( - unstagedIndex, - 0, - new OpenStatusFilesCommandQuickPickItem(computed.stagedAddsAndChanges, { - label: `${GlyphChars.Space.repeat(4)} $(file-symlink-file) Open Staged Files`, - description: '' - }) - ); - - items.push( - new OpenStatusFilesCommandQuickPickItem(computed.unstagedAddsAndChanges, { - label: `${GlyphChars.Space.repeat(4)} $(file-symlink-file) Open Unstaged Files`, - description: '' - }) - ); - } - - items.splice( - index++, - 0, - new CommandQuickPickItem( - { - label: `Staged Files`, - description: computed.stagedStatus - }, - Commands.ShowQuickRepoStatus, - [ - undefined, - { - goBackCommand - } as ShowQuickRepoStatusCommandArgs - ] - ) - ); - } - else if (status.files.some(f => !f.staged)) { - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `Unstaged Files`, - description: computed.unstagedStatus - }, - Commands.ShowQuickRepoStatus, - [ - undefined, - { - goBackCommand - } as ShowQuickRepoStatusCommandArgs - ] - ) - ); - } - - if (status.files.length) { - items.push( - new OpenStatusFilesCommandQuickPickItem( - computed.stagedAddsAndChanges.concat(computed.unstagedAddsAndChanges) - ) - ); - items.push( - new CommandQuickPickItem( - { - label: '$(x) Close Unchanged Files', - description: '' - }, - Commands.CloseUnchangedFiles - ) - ); - } - else { - items.push( - new CommandQuickPickItem( - { - label: `No changes in the working tree`, - description: '' - }, - Commands.ShowQuickRepoStatus, - [ - undefined, - { - goBackCommand - } as ShowQuickRepoStatusCommandArgs - ] - ) - ); - } - - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `$(inbox) Show Stashed Changes`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows stashed changes in the repository` - }, - Commands.ShowQuickStashList, - [ - GitUri.fromRepoPath(status.repoPath), - { - goBackCommand: currentCommand - } as ShowQuickStashListCommandArgs - ] - ) - ); - - if (status.upstream && status.state.ahead) { - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `$(cloud-upload)${GlyphChars.Space} ${status.state.ahead} Commit${ - status.state.ahead > 1 ? 's' : '' - } ahead of ${GlyphChars.Space}$(git-branch) ${status.upstream}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows commits in ${ - GlyphChars.Space - }$(git-branch) ${status.branch} but not ${GlyphChars.Space}$(git-branch) ${status.upstream}` - }, - Commands.ShowQuickBranchHistory, - [ - GitUri.fromRepoPath(status.repoPath, `${status.upstream}..${status.branch}`), - { - branch: status.branch, - maxCount: 0, - goBackCommand: currentCommand - } as ShowQuickBranchHistoryCommandArgs - ] - ) - ); - } - - if (status.upstream && status.state.behind) { - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `$(cloud-download)${GlyphChars.Space} ${status.state.behind} Commit${ - status.state.behind > 1 ? 's' : '' - } behind ${GlyphChars.Space}$(git-branch) ${status.upstream}`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows commits in ${ - GlyphChars.Space - }$(git-branch) ${status.upstream} but not ${GlyphChars.Space}$(git-branch) ${status.branch}${ - status.sha - ? ` (since ${GlyphChars.Space}$(git-commit) ${GitService.shortenSha(status.sha)})` - : '' - }` - }, - Commands.ShowQuickBranchHistory, - [ - GitUri.fromRepoPath(status.repoPath, `${status.branch}..${status.upstream}`), - { - branch: status.upstream, - maxCount: 0, - goBackCommand: currentCommand - } as ShowQuickBranchHistoryCommandArgs - ] - ) - ); - } - - if (status.upstream && !status.state.ahead && !status.state.behind) { - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `$(git-branch) ${status.branch} is up-to-date with ${GlyphChars.Space}$(git-branch) ${ - status.upstream - }`, - description: '' - }, - Commands.ShowQuickRepoStatus, - [ - undefined, - { - goBackCommand - } as ShowQuickRepoStatusCommandArgs - ] - ) - ); - } - - if (goBackCommand) { - items.splice(0, 0, goBackCommand); - } - - const scope = await Container.keyboard.beginScope({ left: goBackCommand }); - - const pick = await window.showQuickPick(items, { - matchOnDescription: true, - placeHolder: `status of ${status.branch}${ - status.upstream ? ` ${Strings.pad(GlyphChars.ArrowLeftRightLong, 1, 1)} ${status.upstream}` : '' - }`, - ignoreFocusOut: getQuickPickIgnoreFocusOut(), - onDidSelectItem: (item: QuickPickItem) => { - scope.setKeyCommand('right', item); - } - } as QuickPickOptions); - - await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/repositoriesQuickPick.ts b/src/quickPicks/repositoriesQuickPick.ts deleted file mode 100644 index e4077f0..0000000 --- a/src/quickPicks/repositoriesQuickPick.ts +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; -import { QuickPickItem, QuickPickOptions, window } from 'vscode'; -import { Container } from '../container'; -import { Repository } from '../gitService'; -import { Iterables } from '../system'; -import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; - -export class RepositoryQuickPickItem implements QuickPickItem { - label: string; - description: string; - detail: string | undefined; - - constructor( - public readonly repository: Repository - ) { - this.label = repository.name; - this.description = repository.path; - } - - get repoPath(): string { - return this.repository.path; - } -} - -export class RepositoriesQuickPick { - static async show( - placeHolder: string, - goBackCommand?: CommandQuickPickItem - ): Promise { - const items = [ - ...Iterables.map(await Container.git.getRepositories(), r => new RepositoryQuickPickItem(r)) - ] as (RepositoryQuickPickItem | CommandQuickPickItem)[]; - - if (goBackCommand !== undefined) { - items.splice(0, 0, goBackCommand); - } - - // const scope = await Container.keyboard.beginScope({ left: goBackCommand }); - - const pick = await window.showQuickPick(items, { - placeHolder: placeHolder, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - } as QuickPickOptions); - - // await scope.dispose(); - - return pick; - } -} diff --git a/src/quickPicks/stashListQuickPick.ts b/src/quickPicks/stashListQuickPick.ts deleted file mode 100644 index 3c40d13..0000000 --- a/src/quickPicks/stashListQuickPick.ts +++ /dev/null @@ -1,84 +0,0 @@ -'use strict'; -import { CancellationTokenSource, QuickPickOptions, window } from 'vscode'; -import { Commands, StashSaveCommandArgs } from '../commands'; -import { GlyphChars } from '../constants'; -import { Container } from '../container'; -import { GitStash } from '../gitService'; -import { KeyNoopCommand } from '../keyboard'; -import { Iterables, Strings } from '../system'; -import { - CommandQuickPickItem, - CommitQuickPickItem, - getQuickPickIgnoreFocusOut, - showQuickPickProgress -} from './commonQuickPicks'; - -export class StashListQuickPick { - static showProgress(mode: 'list' | 'apply') { - const message = - mode === 'apply' - ? `Apply stashed changes to your working tree${GlyphChars.Ellipsis}` - : `stashed changes ${GlyphChars.Dash} search by message, filename, or commit id`; - return showQuickPickProgress(message, { - left: KeyNoopCommand, - ',': KeyNoopCommand, - '.': KeyNoopCommand - }); - } - - static async show( - stash: GitStash, - mode: 'list' | 'apply', - progressCancellation: CancellationTokenSource, - goBackCommand?: CommandQuickPickItem, - currentCommand?: CommandQuickPickItem - ): Promise { - const items = ((stash && Array.from(Iterables.map(stash.commits.values(), c => new CommitQuickPickItem(c)))) || - []) as (CommitQuickPickItem | CommandQuickPickItem)[]; - - if (mode === 'list') { - items.splice( - 0, - 0, - new CommandQuickPickItem( - { - label: `$(plus) Stash Changes`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} stashes all changes` - }, - Commands.StashSave, - [ - { - goBackCommand: currentCommand - } as StashSaveCommandArgs - ] - ) - ); - } - - if (goBackCommand) { - items.splice(0, 0, goBackCommand); - } - - if (progressCancellation.token.isCancellationRequested) return undefined; - - const scope = await Container.keyboard.beginScope({ left: goBackCommand }); - - progressCancellation.cancel(); - - const pick = await window.showQuickPick(items, { - matchOnDescription: true, - placeHolder: - mode === 'apply' - ? `Apply stashed changes to your working tree${GlyphChars.Ellipsis}` - : `stashed changes ${GlyphChars.Dash} search by message, filename, or commit id`, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - // onDidSelectItem: (item: QuickPickItem) => { - // scope.setKeyCommand('right', item); - // } - } as QuickPickOptions); - - await scope.dispose(); - - return pick; - } -} diff --git a/src/quickpicks/branchHistoryQuickPick.ts b/src/quickpicks/branchHistoryQuickPick.ts new file mode 100644 index 0000000..0ab0cf0 --- /dev/null +++ b/src/quickpicks/branchHistoryQuickPick.ts @@ -0,0 +1,196 @@ +'use strict'; +import { CancellationTokenSource, QuickPickOptions, window } from 'vscode'; +import { Commands, ShowCommitSearchCommandArgs, ShowQuickBranchHistoryCommandArgs } from '../commands'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { GitLog, GitUri, RemoteResource } from '../gitService'; +import { KeyNoopCommand } from '../keyboard'; +import { Iterables, Strings } from '../system'; +import { + CommandQuickPickItem, + CommitQuickPickItem, + getQuickPickIgnoreFocusOut, + showQuickPickProgress +} from './commonQuickPicks'; +import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; + +export class BranchHistoryQuickPick { + static showProgress(branch: string) { + return showQuickPickProgress( + `${branch} history ${GlyphChars.Dash} search by commit message, filename, or commit id`, + { + left: KeyNoopCommand, + ',': KeyNoopCommand, + '.': KeyNoopCommand + } + ); + } + + static async show( + log: GitLog, + uri: GitUri | undefined, + branch: string, + progressCancellation: CancellationTokenSource, + goBackCommand?: CommandQuickPickItem, + nextPageCommand?: CommandQuickPickItem + ): Promise { + const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as ( + | CommitQuickPickItem + | CommandQuickPickItem)[]; + + const currentCommand = new CommandQuickPickItem( + { + label: `go back ${GlyphChars.ArrowBack}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to ${ + GlyphChars.Space + }$(git-branch) ${branch} history` + }, + Commands.ShowQuickBranchHistory, + [ + uri, + { + branch, + log, + maxCount: log.maxCount, + goBackCommand + } as ShowQuickBranchHistoryCommandArgs + ] + ); + + const remotes = await Container.git.getRemotes((uri && uri.repoPath) || log.repoPath); + if (remotes.length) { + items.splice( + 0, + 0, + new OpenRemotesCommandQuickPickItem( + remotes, + { + type: 'branch', + branch + } as RemoteResource, + currentCommand + ) + ); + } + + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `$(search) Show Commit Search`, + description: `${Strings.pad( + GlyphChars.Dash, + 2, + 3 + )} search for commits by message, author, files, or commit id` + }, + Commands.ShowCommitSearch, + [ + GitUri.fromRepoPath(log.repoPath), + { + goBackCommand: currentCommand + } as ShowCommitSearchCommandArgs + ] + ) + ); + + let previousPageCommand: CommandQuickPickItem | undefined = undefined; + + if (log.truncated || log.sha) { + if (log.truncated) { + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `$(sync) Show All Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} this may take a while` + }, + Commands.ShowQuickBranchHistory, + [ + GitUri.fromRepoPath(log.repoPath), + { + branch, + maxCount: 0, + goBackCommand + } as ShowQuickBranchHistoryCommandArgs + ] + ) + ); + } + + if (nextPageCommand) { + items.splice(0, 0, nextPageCommand); + } + + if (log.truncated) { + const npc = new CommandQuickPickItem( + { + label: `$(arrow-right) Show Next Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} newer commits` + }, + Commands.ShowQuickBranchHistory, + [ + uri, + { + branch, + maxCount: log.maxCount, + nextPageCommand + } as ShowQuickBranchHistoryCommandArgs + ] + ); + + const last = Iterables.last(log.commits.values()); + if (last != null) { + previousPageCommand = new CommandQuickPickItem( + { + label: `$(arrow-left) Show Previous Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} older commits` + }, + Commands.ShowQuickBranchHistory, + [ + new GitUri(uri ? uri : last.uri, last), + { + branch, + maxCount: log.maxCount, + goBackCommand, + nextPageCommand: npc + } as ShowQuickBranchHistoryCommandArgs + ] + ); + + items.splice(0, 0, previousPageCommand); + } + } + } + + if (goBackCommand) { + items.splice(0, 0, goBackCommand); + } + + if (progressCancellation.token.isCancellationRequested) return undefined; + + const scope = await Container.keyboard.beginScope({ + left: goBackCommand, + ',': previousPageCommand, + '.': nextPageCommand + }); + + progressCancellation.cancel(); + + const pick = await window.showQuickPick(items, { + matchOnDescription: true, + matchOnDetail: true, + placeHolder: `${branch} history ${GlyphChars.Dash} search by commit message, filename, or commit id`, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + // onDidSelectItem: (item: QuickPickItem) => { + // scope.setKeyCommand('right', item); + // } + } as QuickPickOptions); + + await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/branchesAndTagsQuickPick.ts b/src/quickpicks/branchesAndTagsQuickPick.ts new file mode 100644 index 0000000..03002ed --- /dev/null +++ b/src/quickpicks/branchesAndTagsQuickPick.ts @@ -0,0 +1,80 @@ +'use strict'; +import { CancellationTokenSource, QuickPickItem, QuickPickOptions, window } from 'vscode'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { GitBranch, GitTag } from '../gitService'; +import { KeyNoopCommand } from '../keyboard'; +import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, showQuickPickProgress } from './commonQuickPicks'; + +export class BranchOrTagQuickPickItem implements QuickPickItem { + label: string; + description: string; + detail: string | undefined; + + constructor( + public readonly branchOrTag: GitBranch | GitTag + ) { + if (branchOrTag instanceof GitBranch) { + this.label = `${branchOrTag.current ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${ + branchOrTag.name + }`; + this.description = branchOrTag.remote ? `${GlyphChars.Space.repeat(2)} remote branch` : ''; + } + else { + this.label = `${GlyphChars.Space.repeat(4)} ${branchOrTag.name}`; + this.description = `${GlyphChars.Space.repeat(2)} tag`; + } + } + + get name() { + return this.branchOrTag.name; + } + + get remote() { + return this.branchOrTag instanceof GitBranch && this.branchOrTag.remote; + } +} + +export class BranchesAndTagsQuickPick { + static showProgress(placeHolder: string) { + return showQuickPickProgress(placeHolder, { + left: KeyNoopCommand, + ',': KeyNoopCommand, + '.': KeyNoopCommand + }); + } + + static async show( + branches: GitBranch[], + tags: GitTag[], + placeHolder: string, + options: { goBackCommand?: CommandQuickPickItem; progressCancellation?: CancellationTokenSource } = {} + ): Promise { + const items = [ + ...branches.filter(b => !b.remote).map(b => new BranchOrTagQuickPickItem(b)), + ...tags.map(t => new BranchOrTagQuickPickItem(t)), + ...branches.filter(b => b.remote).map(b => new BranchOrTagQuickPickItem(b)) + ] as (BranchOrTagQuickPickItem | CommandQuickPickItem)[]; + + if (options.goBackCommand !== undefined) { + items.splice(0, 0, options.goBackCommand); + } + + if (options.progressCancellation !== undefined && options.progressCancellation.token.isCancellationRequested) { + return undefined; + } + + const scope = await Container.keyboard.beginScope({ left: options.goBackCommand || KeyNoopCommand }); + + options.progressCancellation && options.progressCancellation.cancel(); + + const pick = await window.showQuickPick(items, { + placeHolder: placeHolder, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + } as QuickPickOptions); + + await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/branchesQuickPick.ts b/src/quickpicks/branchesQuickPick.ts new file mode 100644 index 0000000..9e98a16 --- /dev/null +++ b/src/quickpicks/branchesQuickPick.ts @@ -0,0 +1,43 @@ +'use strict'; +import { QuickPickItem, QuickPickOptions, window } from 'vscode'; +import { GlyphChars } from '../constants'; +import { GitBranch } from '../gitService'; +import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; + +export class BranchQuickPickItem implements QuickPickItem { + label: string; + description: string; + detail: string | undefined; + + constructor( + public readonly branch: GitBranch + ) { + this.label = `${branch.current ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${branch.name}`; + this.description = branch.remote ? `${GlyphChars.Space.repeat(2)} remote branch` : ''; + } +} + +export class BranchesQuickPick { + static async show( + branches: GitBranch[], + placeHolder: string, + options: { goBackCommand?: CommandQuickPickItem } = {} + ): Promise { + const items = branches.map(b => new BranchQuickPickItem(b)) as (BranchQuickPickItem | CommandQuickPickItem)[]; + + if (options.goBackCommand !== undefined) { + items.splice(0, 0, options.goBackCommand); + } + + // const scope = await Container.keyboard.beginScope({ left: goBackCommand }); + + const pick = await window.showQuickPick(items, { + placeHolder: placeHolder, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + } as QuickPickOptions); + + // await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/commitFileQuickPick.ts b/src/quickpicks/commitFileQuickPick.ts new file mode 100644 index 0000000..1c14d61 --- /dev/null +++ b/src/quickpicks/commitFileQuickPick.ts @@ -0,0 +1,423 @@ +'use strict'; +import * as path from 'path'; +import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; +import { + Commands, + CopyMessageToClipboardCommandArgs, + CopyShaToClipboardCommandArgs, + DiffWithPreviousCommandArgs, + DiffWithWorkingCommandArgs, + openEditor, + ShowQuickCommitDetailsCommandArgs, + ShowQuickCommitFileDetailsCommandArgs, + ShowQuickFileHistoryCommandArgs +} from '../commands'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { GitLog, GitLogCommit, GitUri, RemoteResource } from '../gitService'; +import { KeyCommand, KeyNoopCommand } from '../keyboard'; +import { Iterables, Strings } from '../system'; +import { + CommandQuickPickItem, + getQuickPickIgnoreFocusOut, + KeyCommandQuickPickItem, + OpenFileCommandQuickPickItem +} from './commonQuickPicks'; +import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; + +export class ApplyCommitFileChangesCommandQuickPickItem extends CommandQuickPickItem { + constructor( + private readonly commit: GitLogCommit, + item?: QuickPickItem + ) { + super( + item || { + label: `$(git-pull-request) Apply Changes`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(file-text) ${path.basename(commit.fileName)} in ${ + GlyphChars.Space + }$(git-commit) ${commit.shortSha}` + }, + undefined, + undefined + ); + } + + async execute(): Promise<{} | undefined> { + const uri = this.commit.toGitUri(); + await Container.git.checkoutFile(uri); + return openEditor(uri, { preserveFocus: true, preview: false }); + } +} + +export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem { + constructor(commit: GitLogCommit, item?: QuickPickItem) { + const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName)); + super( + uri, + item || { + label: `$(file-symlink-file) Open File`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}` + } + ); + } +} + +export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQuickPickItem { + constructor(commit: GitLogCommit, item?: QuickPickItem) { + let description: string; + let uri: Uri; + if (commit.status === 'D') { + uri = GitUri.toRevisionUri(commit.previousFileSha, commit.previousUri.fsPath, commit.repoPath); + description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${ + GlyphChars.Space + }$(git-commit) ${commit.previousShortSha} (deleted in ${GlyphChars.Space}$(git-commit) ${commit.shortSha})`; + } + else { + uri = GitUri.toRevisionUri(commit.sha, commit.uri.fsPath, commit.repoPath); + description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${ + GlyphChars.Space + }$(git-commit) ${commit.shortSha}`; + } + super( + uri, + item || { + label: `$(file-symlink-file) Open Revision`, + description: description + } + ); + } +} + +export class CommitFileQuickPick { + static async show( + commit: GitLogCommit, + uri: Uri, + goBackCommand?: CommandQuickPickItem, + currentCommand?: CommandQuickPickItem, + fileLog?: GitLog + ): Promise { + const items: CommandQuickPickItem[] = []; + + const stash = commit.isStash; + + const workingName = + (commit.workingFileName && path.basename(commit.workingFileName)) || path.basename(commit.fileName); + + const isUncommitted = commit.isUncommitted; + if (isUncommitted) { + // Since we can't trust the previous sha on an uncommitted commit, find the last commit for this file + const c = await Container.git.getRecentLogCommitForFile(undefined, commit.uri.fsPath); + if (c === undefined) return undefined; + + commit = c; + } + + await commit.resolvePreviousFileSha(); + + if (stash) { + items.push(new ApplyCommitFileChangesCommandQuickPickItem(commit)); + } + + if (commit.previousFileShortSha) { + items.push( + new CommandQuickPickItem( + { + label: `$(git-compare) Open Changes`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${ + commit.previousFileShortSha + } ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}` + }, + Commands.DiffWithPrevious, + [ + commit.uri, + { + commit + } as DiffWithPreviousCommandArgs + ] + ) + ); + } + + if (commit.workingFileName) { + items.push( + new CommandQuickPickItem( + { + label: `$(git-compare) Open Changes with Working Tree`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${ + GlyphChars.Space + } $(git-compare) ${GlyphChars.Space} $(file-text) ${workingName}` + }, + Commands.DiffWithWorking, + [ + Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), + { + commit + } as DiffWithWorkingCommandArgs + ] + ) + ); + } + + if (commit.workingFileName && commit.status !== 'D') { + items.push(new OpenCommitFileCommandQuickPickItem(commit)); + } + items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit)); + + const remotes = await Container.git.getRemotes(commit.repoPath); + if (remotes.length) { + if (commit.workingFileName && commit.status !== 'D') { + const branch = await Container.git.getBranch(commit.repoPath); + if (branch !== undefined) { + items.push( + new OpenRemotesCommandQuickPickItem( + remotes, + { + type: 'file', + fileName: commit.workingFileName, + branch: branch.name + } as RemoteResource, + currentCommand + ) + ); + } + } + + if (!stash) { + items.push( + new OpenRemotesCommandQuickPickItem( + remotes, + { + type: 'revision', + fileName: commit.fileName, + commit + } as RemoteResource, + currentCommand + ) + ); + } + } + + if (!stash) { + items.push(new ApplyCommitFileChangesCommandQuickPickItem(commit)); + + items.push( + new CommandQuickPickItem( + { + label: `$(clippy) Copy Commit ID to Clipboard`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.shortSha}` + }, + Commands.CopyShaToClipboard, + [ + uri, + { + sha: commit.sha + } as CopyShaToClipboardCommandArgs + ] + ) + ); + + items.push( + new CommandQuickPickItem( + { + label: `$(clippy) Copy Commit Message to Clipboard`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage()}` + }, + Commands.CopyMessageToClipboard, + [ + uri, + { + message: commit.message, + sha: commit.sha + } as CopyMessageToClipboardCommandArgs + ] + ) + ); + } + + if (commit.workingFileName) { + items.push( + new CommandQuickPickItem( + { + label: `$(history) Show File History`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename(commit.fileName)}` + }, + Commands.ShowQuickFileHistory, + [ + Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), + { + fileLog, + goBackCommand: currentCommand + } as ShowQuickFileHistoryCommandArgs + ] + ) + ); + } + + if (!stash) { + items.push( + new CommandQuickPickItem( + { + label: `$(history) Show ${commit.workingFileName ? 'Previous ' : ''}File History`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename( + commit.fileName + )} ${Strings.pad(GlyphChars.Dot, 1, 1)} from ${GlyphChars.Space}$(git-commit) ${ + commit.shortSha + }` + }, + Commands.ShowQuickFileHistory, + [ + commit.toGitUri(), + { + goBackCommand: currentCommand + } as ShowQuickFileHistoryCommandArgs + ] + ) + ); + + items.push( + new CommandQuickPickItem( + { + label: `$(git-commit) Show Commit Details`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha}` + }, + Commands.ShowQuickCommitDetails, + [ + commit.toGitUri(), + { + commit, + sha: commit.sha, + goBackCommand: currentCommand + } as ShowQuickCommitDetailsCommandArgs + ] + ) + ); + } + + if (goBackCommand) { + items.splice(0, 0, goBackCommand); + } + + let previousCommand: KeyCommand | (() => Promise) | undefined = undefined; + let nextCommand: KeyCommand | (() => Promise) | undefined = undefined; + if (!stash) { + // If we have the full history, we are good + if (fileLog !== undefined && !fileLog.truncated && fileLog.sha === undefined) { + previousCommand = + commit.previousSha === undefined + ? undefined + : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ + commit.previousUri, + { + fileLog, + sha: commit.previousSha, + goBackCommand + } as ShowQuickCommitFileDetailsCommandArgs + ]); + + nextCommand = + commit.nextSha === undefined + ? undefined + : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ + commit.nextUri, + { + fileLog, + sha: commit.nextSha, + goBackCommand + } as ShowQuickCommitFileDetailsCommandArgs + ]); + } + else { + previousCommand = async () => { + let log = fileLog; + let c = log && log.commits.get(commit.sha); + + // If we can't find the commit or the previous commit isn't available (since it isn't trustworthy) + if (c === undefined || c.previousSha === undefined) { + log = await Container.git.getLogForFile(commit.repoPath, uri.fsPath, { + maxCount: Container.config.advanced.maxListItems, + ref: commit.sha, + renames: true + }); + if (log === undefined) return KeyNoopCommand; + + c = log && log.commits.get(commit.sha); + // Since we exclude merge commits in file log, just grab the first returned commit + if (c === undefined && commit.isMerge) { + c = Iterables.first(log.commits.values()); + } + + if (c) { + // Copy over next info, since it is trustworthy at this point + c.nextSha = commit.nextSha; + c.nextFileName = commit.nextFileName; + } + } + + if (c === undefined || c.previousSha === undefined) return KeyNoopCommand; + + return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ + c.previousUri, + { + fileLog: log, + sha: c.previousSha, + goBackCommand + } as ShowQuickCommitFileDetailsCommandArgs + ]); + }; + + nextCommand = async () => { + let log = fileLog; + let c = log && log.commits.get(commit.sha); + + // If we can't find the commit or the next commit isn't available (since it isn't trustworthy) + if (c === undefined || c.nextSha === undefined) { + log = undefined; + c = undefined; + + // Try to find the next commit + const next = await Container.git.findNextCommit(commit.repoPath, uri.fsPath, commit.sha); + if (next !== undefined && next.sha !== commit.sha) { + c = commit; + c.nextSha = next.sha; + c.nextFileName = next.originalFileName || next.fileName; + } + } + + if (c === undefined || c.nextSha === undefined) return KeyNoopCommand; + + return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ + c.nextUri, + { + fileLog: log, + sha: c.nextSha, + goBackCommand + } as ShowQuickCommitFileDetailsCommandArgs + ]); + }; + } + } + + const scope = await Container.keyboard.beginScope({ + left: goBackCommand, + ',': previousCommand, + '.': nextCommand + }); + + const pick = await window.showQuickPick(items, { + matchOnDescription: true, + placeHolder: `${commit.getFormattedPath()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${ + isUncommitted ? `Uncommitted ${GlyphChars.ArrowRightHollow} ` : '' + }${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.author}, ${ + commit.formattedDate + } ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getShortMessage()}`, + ignoreFocusOut: getQuickPickIgnoreFocusOut(), + onDidSelectItem: (item: QuickPickItem) => { + scope.setKeyCommand('right', item as KeyCommand); + } + } as QuickPickOptions); + + await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/commitQuickPick.ts b/src/quickpicks/commitQuickPick.ts new file mode 100644 index 0000000..11a79e8 --- /dev/null +++ b/src/quickpicks/commitQuickPick.ts @@ -0,0 +1,419 @@ +'use strict'; +import * as path from 'path'; +import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from 'vscode'; +import { + Commands, + CopyMessageToClipboardCommandArgs, + CopyShaToClipboardCommandArgs, + DiffDirectoryCommandArgs, + DiffWithPreviousCommandArgs, + ShowQuickCommitDetailsCommandArgs, + StashApplyCommandArgs, + StashDeleteCommandArgs +} from '../commands'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { + getGitStatusOcticon, + GitLog, + GitLogCommit, + GitStashCommit, + GitStatusFile, + GitStatusFileStatus, + GitUri, + IGitStatusFile, + RemoteResource +} from '../gitService'; +import { KeyCommand, KeyNoopCommand, Keys } from '../keyboard'; +import { Arrays, Iterables, Strings } from '../system'; +import { + CommandQuickPickItem, + getQuickPickIgnoreFocusOut, + KeyCommandQuickPickItem, + OpenFileCommandQuickPickItem, + OpenFilesCommandQuickPickItem, + QuickPickItem, + ShowCommitInResultsQuickPickItem +} from './commonQuickPicks'; +import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; + +export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickItem { + readonly status: GitStatusFileStatus; + + readonly commit: GitLogCommit; + + constructor(commit: GitLogCommit, status: IGitStatusFile) { + const octicon = getGitStatusOcticon(status.status); + const description = GitStatusFile.getFormattedDirectory(status, true); + + super(GitUri.toRevisionUri(commit.sha, status, commit.repoPath), { + label: `${Strings.pad(octicon, 4, 2)} ${path.basename(status.fileName)}`, + description: description + }); + + this.commit = commit.toFileCommit(status); + this.status = status.status; + } + + get sha(): string { + return this.commit.sha; + } + + onDidPressKey(key: Keys): Promise<{} | undefined> { + if (this.commit.previousSha === undefined) return super.onDidPressKey(key); + + return commands.executeCommand(Commands.DiffWithPrevious, this.commit.toGitUri(), { + commit: this.commit, + showOptions: { + preserveFocus: true, + preview: false + } as TextDocumentShowOptions + } as DiffWithPreviousCommandArgs) as Promise<{} | undefined>; + } +} + +export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem { + constructor(commit: GitLogCommit, versioned: boolean = false, item?: QuickPickItem) { + const repoPath = commit.repoPath; + const uris = Arrays.filterMap(commit.fileStatuses, f => GitUri.fromFileStatus(f, repoPath)); + + super( + uris, + item || { + label: `$(file-symlink-file) Open Files`, + description: '' + // detail: `Opens all of the changed file in the working tree` + } + ); + } +} + +export class OpenCommitFileRevisionsCommandQuickPickItem extends OpenFilesCommandQuickPickItem { + constructor(commit: GitLogCommit, item?: QuickPickItem) { + const uris = Arrays.filterMap(commit.fileStatuses, f => + GitUri.toRevisionUri(f.status === 'D' ? commit.previousFileSha : commit.sha, f, commit.repoPath) + ); + + super( + uris, + item || { + label: `$(file-symlink-file) Open Revisions`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} in ${GlyphChars.Space}$(git-commit) ${ + commit.shortSha + }` + // detail: `Opens all of the changed files in $(git-commit) ${commit.shortSha}` + } + ); + } +} + +export class CommitQuickPick { + static async show( + commit: GitLogCommit, + uri: Uri, + goBackCommand?: CommandQuickPickItem, + currentCommand?: CommandQuickPickItem, + repoLog?: GitLog + ): Promise { + await commit.resolvePreviousFileSha(); + + const items: (CommitWithFileStatusQuickPickItem | CommandQuickPickItem)[] = commit.fileStatuses.map( + fs => new CommitWithFileStatusQuickPickItem(commit, fs) + ); + + const stash = commit.isStash; + + let index = 0; + + if (stash) { + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `$(git-pull-request) Apply Stashed Changes`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage()}` + }, + Commands.StashApply, + [ + { + confirm: true, + deleteAfter: false, + stashItem: commit as GitStashCommit, + goBackCommand: currentCommand + } as StashApplyCommandArgs + ] + ) + ); + + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `$(x) Delete Stashed Changes`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage()}` + }, + Commands.StashDelete, + [ + { + confirm: true, + stashItem: commit as GitStashCommit, + goBackCommand: currentCommand + } as StashDeleteCommandArgs + ] + ) + ); + + items.splice(index++, 0, new ShowCommitInResultsQuickPickItem(commit)); + } + else { + items.splice(index++, 0, new ShowCommitInResultsQuickPickItem(commit)); + + const remotes = await Container.git.getRemotes(commit.repoPath); + if (remotes.length) { + items.splice( + index++, + 0, + new OpenRemotesCommandQuickPickItem( + remotes, + { + type: 'commit', + sha: commit.sha + } as RemoteResource, + currentCommand + ) + ); + } + } + + items.splice(index++, 0, new OpenCommitFilesCommandQuickPickItem(commit)); + items.splice(index++, 0, new OpenCommitFileRevisionsCommandQuickPickItem(commit)); + + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `$(git-compare) Open Directory Compare with Previous Revision`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.previousFileShortSha} ${ + GlyphChars.Space + } $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}` + }, + Commands.DiffDirectory, + [ + commit.uri, + { + ref1: commit.previousFileSha, + ref2: commit.sha + } as DiffDirectoryCommandArgs + ] + ) + ); + + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `$(git-compare) Open Directory Compare with Working Tree`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${ + GlyphChars.Space + } $(git-compare) ${GlyphChars.Space} $(file-directory) Working Tree` + }, + Commands.DiffDirectory, + [ + uri, + { + ref1: commit.sha + } as DiffDirectoryCommandArgs + ] + ) + ); + + if (!stash) { + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `$(clippy) Copy Commit ID to Clipboard`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.shortSha}` + }, + Commands.CopyShaToClipboard, + [ + uri, + { + sha: commit.sha + } as CopyShaToClipboardCommandArgs + ] + ) + ); + } + + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `$(clippy) Copy Commit Message to Clipboard`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage()}` + }, + Commands.CopyMessageToClipboard, + [ + uri, + { + message: commit.message, + sha: commit.sha + } as CopyMessageToClipboardCommandArgs + ] + ) + ); + + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `Changed Files`, + description: commit.getDiffStatus() + }, + Commands.ShowQuickCommitDetails, + [ + uri, + { + commit, + repoLog, + sha: commit.sha, + goBackCommand + } as ShowQuickCommitDetailsCommandArgs + ] + ) + ); + + if (goBackCommand) { + items.splice(0, 0, goBackCommand); + } + + let previousCommand: KeyCommand | (() => Promise) | undefined = undefined; + let nextCommand: KeyCommand | (() => Promise) | undefined = undefined; + if (!stash) { + // If we have the full history, we are good + if (repoLog !== undefined && !repoLog.truncated && repoLog.sha === undefined) { + previousCommand = + commit.previousSha === undefined + ? undefined + : new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [ + commit.previousUri, + { + repoLog, + sha: commit.previousSha, + goBackCommand + } as ShowQuickCommitDetailsCommandArgs + ]); + + nextCommand = + commit.nextSha === undefined + ? undefined + : new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [ + commit.nextUri, + { + repoLog, + sha: commit.nextSha, + goBackCommand + } as ShowQuickCommitDetailsCommandArgs + ]); + } + else { + previousCommand = async () => { + let log = repoLog; + let c = log && log.commits.get(commit.sha); + + // If we can't find the commit or the previous commit isn't available (since it isn't trustworthy) + if (c === undefined || c.previousSha === undefined) { + log = await Container.git.getLog(commit.repoPath, { + maxCount: Container.config.advanced.maxListItems, + ref: commit.sha + }); + c = log && log.commits.get(commit.sha); + + if (c) { + // Copy over next info, since it is trustworthy at this point + c.nextSha = commit.nextSha; + } + } + + if (c === undefined || c.previousSha === undefined) return KeyNoopCommand; + + return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [ + c.previousUri, + { + repoLog: log, + sha: c.previousSha, + goBackCommand + } as ShowQuickCommitDetailsCommandArgs + ]); + }; + + nextCommand = async () => { + let log = repoLog; + let c = log && log.commits.get(commit.sha); + + // If we can't find the commit or the next commit isn't available (since it isn't trustworthy) + if (c === undefined || c.nextSha === undefined) { + log = undefined; + c = undefined; + + // Try to find the next commit + const nextLog = await Container.git.getLog(commit.repoPath, { + maxCount: 1, + reverse: true, + ref: commit.sha + }); + const next = nextLog && Iterables.first(nextLog.commits.values()); + if (next !== undefined && next.sha !== commit.sha) { + c = commit; + c.nextSha = next.sha; + } + } + + if (c === undefined || c.nextSha === undefined) return KeyNoopCommand; + + return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [ + c.nextUri, + { + repoLog: log, + sha: c.nextSha, + goBackCommand + } as ShowQuickCommitDetailsCommandArgs + ]); + }; + } + } + + const scope = await Container.keyboard.beginScope({ + left: goBackCommand, + ',': previousCommand, + '.': nextCommand + }); + + const pick = await window.showQuickPick(items, { + matchOnDescription: true, + matchOnDetail: true, + placeHolder: `${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${ + commit.author ? `${commit.author}, ` : '' + }${commit.formattedDate} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getShortMessage()}`, + ignoreFocusOut: getQuickPickIgnoreFocusOut(), + onDidSelectItem: (item: QuickPickItem) => { + scope.setKeyCommand('right', item); + if (typeof item.onDidSelect === 'function') { + item.onDidSelect(); + } + } + } as QuickPickOptions); + + await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/commitsQuickPick.ts b/src/quickpicks/commitsQuickPick.ts new file mode 100644 index 0000000..33bcb48 --- /dev/null +++ b/src/quickpicks/commitsQuickPick.ts @@ -0,0 +1,69 @@ +'use strict'; +import { CancellationTokenSource, QuickPickOptions, window } from 'vscode'; +import { Container } from '../container'; +import { GitLog } from '../gitService'; +import { KeyNoopCommand } from '../keyboard'; +import { Iterables } from '../system'; +import { + CommandQuickPickItem, + CommitQuickPickItem, + getQuickPickIgnoreFocusOut, + MessageQuickPickItem, + showQuickPickProgress +} from './commonQuickPicks'; + +export class CommitsQuickPick { + static showProgress(message: string) { + return showQuickPickProgress(message, { + left: KeyNoopCommand, + ',': KeyNoopCommand, + '.': KeyNoopCommand + }); + } + + static async show( + log: GitLog | undefined, + placeHolder: string, + progressCancellation: CancellationTokenSource, + options: { + goBackCommand?: CommandQuickPickItem; + showAllCommand?: CommandQuickPickItem; + showInResultsExplorerCommand?: CommandQuickPickItem; + } + ): Promise { + const items = ((log && [...Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))]) || [ + new MessageQuickPickItem('No results found') + ]) as (CommitQuickPickItem | CommandQuickPickItem)[]; + + if (options.showInResultsExplorerCommand !== undefined) { + items.splice(0, 0, options.showInResultsExplorerCommand); + } + + if (options.showAllCommand !== undefined) { + items.splice(0, 0, options.showAllCommand); + } + + if (options.goBackCommand !== undefined) { + items.splice(0, 0, options.goBackCommand); + } + + if (progressCancellation.token.isCancellationRequested) return undefined; + + const scope = await Container.keyboard.beginScope({ left: options.goBackCommand }); + + progressCancellation.cancel(); + + const pick = await window.showQuickPick(items, { + matchOnDescription: true, + placeHolder: placeHolder, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + // onDidSelectItem: (item: QuickPickItem) => { + // scope.setKeyCommand('right', item); + // } + } as QuickPickOptions); + + await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/commonQuickPicks.ts b/src/quickpicks/commonQuickPicks.ts new file mode 100644 index 0000000..4f43a9b --- /dev/null +++ b/src/quickpicks/commonQuickPicks.ts @@ -0,0 +1,282 @@ +'use strict'; +import { + CancellationTokenSource, + commands, + QuickPickItem, + QuickPickOptions, + TextDocumentShowOptions, + TextEditor, + Uri, + window +} from 'vscode'; +import { Commands, openEditor } from '../commands'; +import { configuration } from '../configuration'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { GitLog, GitLogCommit, GitStashCommit } from '../gitService'; +import { KeyMapping, Keys } from '../keyboard'; +import { Strings } from '../system'; +import { BranchesAndTagsQuickPick, BranchOrTagQuickPickItem } from './branchesAndTagsQuickPick'; + +export function getQuickPickIgnoreFocusOut() { + return !configuration.get(configuration.name('advanced')('quickPick')('closeOnFocusOut').value); +} + +export function showQuickPickProgress(message: string, mapping?: KeyMapping): CancellationTokenSource { + const cancellation = new CancellationTokenSource(); + _showQuickPickProgress(message, cancellation, mapping); + return cancellation; +} + +async function _showQuickPickProgress(message: string, cancellation: CancellationTokenSource, mapping?: KeyMapping) { + const scope = mapping && (await Container.keyboard.beginScope(mapping)); + + try { + await window.showQuickPick( + _getInfiniteCancellablePromise(cancellation), + { + placeHolder: message, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + } as QuickPickOptions, + cancellation.token + ); + } + catch (ex) { + // Not sure why this throws + } + finally { + cancellation.cancel(); + scope && scope.dispose(); + } +} + +function _getInfiniteCancellablePromise(cancellation: CancellationTokenSource) { + return new Promise((resolve, reject) => { + const disposable = cancellation.token.onCancellationRequested(() => { + disposable.dispose(); + resolve([]); + }); + }); +} + +export interface QuickPickItem extends QuickPickItem { + onDidSelect?(): void; + onDidPressKey?(key: Keys): Promise<{} | undefined>; +} + +export class CommandQuickPickItem implements QuickPickItem { + label!: string; + description!: string; + detail?: string | undefined; + protected command: Commands | undefined; + protected args: any[] | undefined; + + constructor(item: QuickPickItem, args?: [Commands, any[]]); + constructor(item: QuickPickItem, command?: Commands, args?: any[]); + constructor(item: QuickPickItem, commandOrArgs?: Commands | [Commands, any[]], args?: any[]) { + if (commandOrArgs === undefined) { + this.command = undefined; + this.args = args; + } + else if (typeof commandOrArgs === 'string') { + this.command = commandOrArgs; + this.args = args; + } + else { + this.command = commandOrArgs[0]; + this.args = commandOrArgs.slice(1); + } + Object.assign(this, item); + } + + execute(): Promise<{} | undefined> { + if (this.command === undefined) return Promise.resolve(undefined); + + return commands.executeCommand(this.command, ...(this.args || [])) as Promise<{} | undefined>; + } + + onDidPressKey(key: Keys): Promise<{} | undefined> { + return this.execute(); + } +} + +export class CommitQuickPickItem implements QuickPickItem { + label: string; + description: string; + detail: string; + + constructor( + public readonly commit: GitLogCommit + ) { + const message = commit.getShortMessage(); + if (commit.isStash) { + this.label = message; + this.description = ''; + this.detail = `${GlyphChars.Space} ${(commit as GitStashCommit).stashName || commit.shortSha} ${Strings.pad( + GlyphChars.Dot, + 1, + 1 + )} ${commit.formattedDate} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getDiffStatus()}`; + } + else { + this.label = message; + this.description = `${Strings.pad('$(git-commit)', 1, 1)} ${commit.shortSha}`; + this.detail = `${GlyphChars.Space} ${commit.author}, ${commit.formattedDate}${ + commit.isFile ? '' : ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getDiffStatus()}` + }`; + } + } +} + +export class ChooseFromBranchesAndTagsQuickPickItem extends CommandQuickPickItem { + constructor( + private readonly repoPath: string, + private readonly placeHolder: string, + private readonly goBackCommand?: CommandQuickPickItem, + item: QuickPickItem = { + label: 'Choose from Branch or Tag History...', + description: `${Strings.pad(GlyphChars.Dash, 2, 2)} shows list of branches and tags` + } + ) { + super(item, undefined, undefined); + } + + async execute( + options: TextDocumentShowOptions = { preserveFocus: false, preview: false } + ): Promise { + const progressCancellation = BranchesAndTagsQuickPick.showProgress(this.placeHolder); + + try { + const [branches, tags] = await Promise.all([ + Container.git.getBranches(this.repoPath), + Container.git.getTags(this.repoPath) + ]); + + if (progressCancellation.token.isCancellationRequested) return undefined; + + return BranchesAndTagsQuickPick.show(branches, tags, this.placeHolder, { + progressCancellation: progressCancellation, + goBackCommand: this.goBackCommand + }); + } + finally { + progressCancellation.cancel(); + } + } +} + +export class KeyCommandQuickPickItem extends CommandQuickPickItem { + constructor(command: Commands, args?: any[]) { + super({ label: '', description: '' } as QuickPickItem, command, args); + } +} + +export class MessageQuickPickItem extends CommandQuickPickItem { + constructor(message: string) { + super({ label: message, description: '' } as QuickPickItem); + } +} + +export class OpenFileCommandQuickPickItem extends CommandQuickPickItem { + constructor( + public readonly uri: Uri, + item: QuickPickItem + ) { + super(item, undefined, undefined); + } + + async execute(options?: TextDocumentShowOptions): Promise { + return openEditor(this.uri, options); + } + + // onDidSelect(): Promise<{} | undefined> { + // return this.execute({ + // preserveFocus: true, + // preview: true + // }); + // } + + onDidPressKey(key: Keys): Promise<{} | undefined> { + return this.execute({ + preserveFocus: true, + preview: false + }); + } +} + +export class OpenFilesCommandQuickPickItem extends CommandQuickPickItem { + constructor( + public readonly uris: Uri[], + item: QuickPickItem + ) { + super(item, undefined, undefined); + } + + async execute( + options: TextDocumentShowOptions = { preserveFocus: false, preview: false } + ): Promise<{} | undefined> { + for (const uri of this.uris) { + await openEditor(uri, options); + } + return undefined; + } + + async onDidPressKey(key: Keys): Promise<{} | undefined> { + return this.execute({ + preserveFocus: true, + preview: false + }); + } +} + +export class ShowCommitInResultsQuickPickItem extends CommandQuickPickItem { + constructor( + public readonly commit: GitLogCommit, + item: QuickPickItem = { + label: 'Show in Results', + description: `${Strings.pad(GlyphChars.Dash, 2, 2)} displays commit in the GitLens Results explorer` + } + ) { + super(item, undefined, undefined); + } + + async execute( + options: TextDocumentShowOptions = { preserveFocus: false, preview: false } + ): Promise<{} | undefined> { + Container.resultsExplorer.showCommitInResults(this.commit); + return undefined; + } +} + +export class ShowCommitsInResultsQuickPickItem extends CommandQuickPickItem { + constructor( + public readonly results: GitLog, + public readonly resultsLabel: string | { label: string; resultsType?: { singular: string; plural: string } }, + item: QuickPickItem = { + label: 'Show in Results', + description: `${Strings.pad(GlyphChars.Dash, 2, 2)} displays commits in the GitLens Results explorer` + } + ) { + super(item, undefined, undefined); + } + + async execute( + options: TextDocumentShowOptions = { preserveFocus: false, preview: false } + ): Promise<{} | undefined> { + Container.resultsExplorer.showCommitsInResults(this.results, this.resultsLabel); + return undefined; + } +} + +export class ShowCommitsSearchInResultsQuickPickItem extends ShowCommitsInResultsQuickPickItem { + constructor( + public readonly results: GitLog, + public readonly search: string, + item: QuickPickItem = { + label: 'Show in Results', + description: `${Strings.pad(GlyphChars.Dash, 2, 2)} displays results in the GitLens Results explorer` + } + ) { + super(results, { label: search }, item); + } +} diff --git a/src/quickpicks/fileHistoryQuickPick.ts b/src/quickpicks/fileHistoryQuickPick.ts new file mode 100644 index 0000000..c6c6340 --- /dev/null +++ b/src/quickpicks/fileHistoryQuickPick.ts @@ -0,0 +1,224 @@ +'use strict'; +import * as path from 'path'; +import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode'; +import { Commands, ShowQuickCurrentBranchHistoryCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { GitLog, GitUri, RemoteResource } from '../gitService'; +import { KeyNoopCommand } from '../keyboard'; +import { Iterables, Strings } from '../system'; +import { + ChooseFromBranchesAndTagsQuickPickItem, + CommandQuickPickItem, + CommitQuickPickItem, + getQuickPickIgnoreFocusOut, + showQuickPickProgress +} from './commonQuickPicks'; +import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; + +export class FileHistoryQuickPick { + static showProgress(placeHolder: string) { + return showQuickPickProgress(placeHolder, { + left: KeyNoopCommand, + ',': KeyNoopCommand, + '.': KeyNoopCommand + }); + } + + static async show( + log: GitLog, + uri: GitUri, + placeHolder: string, + options: { + currentCommand?: CommandQuickPickItem; + goBackCommand?: CommandQuickPickItem; + nextPageCommand?: CommandQuickPickItem; + previousPageCommand?: CommandQuickPickItem; + pickerOnly?: boolean; + progressCancellation?: CancellationTokenSource; + showAllCommand?: CommandQuickPickItem; + showInResultsExplorerCommand?: CommandQuickPickItem; + } = {} + ): Promise { + options = { pickerOnly: false, ...options }; + + const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as ( + | CommitQuickPickItem + | CommandQuickPickItem)[]; + + let index = 0; + + index++; + items.splice( + 0, + 0, + new ChooseFromBranchesAndTagsQuickPickItem(log.repoPath, placeHolder, options.currentCommand) + ); + + if (options.showInResultsExplorerCommand !== undefined) { + index++; + items.splice(0, 0, options.showInResultsExplorerCommand); + } + + if (log.truncated || log.sha) { + if (options.showAllCommand !== undefined) { + index++; + items.splice(0, 0, options.showAllCommand); + } + else if (!options.pickerOnly) { + const [workingFileName] = await Container.git.findWorkingFileName( + path.relative(log.repoPath, uri.fsPath), + log.repoPath + ); + if (workingFileName) { + index++; + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `$(history) Show File History`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename( + workingFileName + )}` + }, + Commands.ShowQuickFileHistory, + [ + Uri.file(path.resolve(log.repoPath, workingFileName)), + { + goBackCommand: new CommandQuickPickItem( + { + label: `go back ${GlyphChars.ArrowBack}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${ + GlyphChars.Space + }$(file-text) ${path.basename(uri.fsPath)}${ + uri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${uri.shortSha}` : '' + }` + }, + Commands.ShowQuickFileHistory, + [ + uri, + { + log: log, + maxCount: log.maxCount, + range: log.range, + goBackCommand: options.goBackCommand + } as ShowQuickFileHistoryCommandArgs + ] + ) + } as ShowQuickFileHistoryCommandArgs + ] + ) + ); + } + } + + if (options.nextPageCommand !== undefined) { + index++; + items.splice(0, 0, options.nextPageCommand); + } + + if (options.previousPageCommand !== undefined) { + index++; + items.splice(0, 0, options.previousPageCommand); + } + } + + if (!options.pickerOnly) { + const branch = await Container.git.getBranch(uri.repoPath!); + + if (branch !== undefined) { + const currentCommand = new CommandQuickPickItem( + { + label: `go back ${GlyphChars.ArrowBack}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${ + GlyphChars.Space + }$(file-text) ${path.basename(uri.fsPath)}${ + uri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${uri.shortSha}` : '' + }` + }, + Commands.ShowQuickFileHistory, + [ + uri, + { + log, + maxCount: log.maxCount, + range: log.range + } as ShowQuickFileHistoryCommandArgs + ] + ); + + // Only show the full repo option if we are the root + if (options.goBackCommand === undefined) { + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `$(history) Show Branch History`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${ + GlyphChars.Space + }$(git-branch) ${branch.name} history` + }, + Commands.ShowQuickCurrentBranchHistory, + [ + undefined, + { + goBackCommand: currentCommand + } as ShowQuickCurrentBranchHistoryCommandArgs + ] + ) + ); + } + + const remotes = await Container.git.getRemotes(uri.repoPath!); + if (remotes.length) { + const resource = + uri.sha !== undefined + ? ({ + type: 'revision', + branch: branch.name, + fileName: uri.getRelativePath(), + sha: uri.sha + } as RemoteResource) + : ({ + type: 'file', + branch: branch.name, + fileName: uri.getRelativePath() + } as RemoteResource); + items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, resource, currentCommand)); + } + } + + if (options.goBackCommand) { + items.splice(0, 0, options.goBackCommand); + } + } + + if (options.progressCancellation !== undefined && options.progressCancellation.token.isCancellationRequested) { + return undefined; + } + + const scope = await Container.keyboard.beginScope({ + left: options.goBackCommand, + ',': options.previousPageCommand, + '.': options.nextPageCommand + }); + + options.progressCancellation && options.progressCancellation.cancel(); + + const pick = await window.showQuickPick(items, { + matchOnDescription: true, + matchOnDetail: true, + placeHolder: placeHolder, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + // onDidSelectItem: (item: QuickPickItem) => { + // scope.setKeyCommand('right', item); + // } + } as QuickPickOptions); + + await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/modesQuickPick.ts b/src/quickpicks/modesQuickPick.ts new file mode 100644 index 0000000..3fcee6e --- /dev/null +++ b/src/quickpicks/modesQuickPick.ts @@ -0,0 +1,41 @@ +'use strict'; +import { QuickPickItem, QuickPickOptions, window } from 'vscode'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; + +export interface ModesQuickPickItem extends QuickPickItem { + key: string | undefined; +} + +export class ModesQuickPick { + static async show(): Promise { + const modes = Object.keys(Container.config.modes); + if (modes.length === 0) return undefined; + + const mode = Container.config.mode.active; + + const items = modes.map(key => { + const modeCfg = Container.config.modes[key]; + return { + label: `${mode === key ? '$(check)\u00a0\u00a0' : '\u00a0\u00a0\u00a0\u00a0\u00a0'}${ + modeCfg.name + } mode`, + description: modeCfg.description ? `\u00a0${GlyphChars.Dash}\u00a0 ${modeCfg.description}` : '', + key: key + } as ModesQuickPickItem; + }); + + if (mode) { + items.splice(0, 0, { + label: `Exit ${Container.config.modes[mode].name} mode`, + key: undefined + } as ModesQuickPickItem); + } + + const pick = await window.showQuickPick(items, { + placeHolder: 'select a GitLens mode to enter' + } as QuickPickOptions); + + return pick; + } +} diff --git a/src/quickpicks/remotesQuickPick.ts b/src/quickpicks/remotesQuickPick.ts new file mode 100644 index 0000000..347ab76 --- /dev/null +++ b/src/quickpicks/remotesQuickPick.ts @@ -0,0 +1,167 @@ +'use strict'; +import * as path from 'path'; +import { QuickPickOptions, window } from 'vscode'; +import { Commands, OpenInRemoteCommandArgs } from '../commands'; +import { GlyphChars } from '../constants'; +import { + getNameFromRemoteResource, + GitLogCommit, + GitRemote, + GitService, + RemoteResource, + RemoteResourceType +} from '../gitService'; +import { Strings } from '../system'; +import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; + +export class OpenRemoteCommandQuickPickItem extends CommandQuickPickItem { + private remote: GitRemote; + private resource: RemoteResource; + + constructor(remote: GitRemote, resource: RemoteResource) { + super( + { + label: `$(link-external) Open ${getNameFromRemoteResource(resource)} in ${remote.provider!.name}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(repo) ${remote.provider!.path}` + }, + undefined, + undefined + ); + + this.remote = remote; + this.resource = resource; + } + + async execute(): Promise<{} | undefined> { + return this.remote.provider!.open(this.resource); + } +} + +export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem { + constructor(remotes: GitRemote[], resource: RemoteResource, goBackCommand?: CommandQuickPickItem) { + const name = getNameFromRemoteResource(resource); + + let description = ''; + switch (resource.type) { + case RemoteResourceType.Branch: + description = `$(git-branch) ${resource.branch}`; + break; + + case RemoteResourceType.Branches: + description = `$(git-branch) Branches`; + break; + + case RemoteResourceType.Commit: + const shortSha = GitService.shortenSha(resource.sha); + description = `$(git-commit) ${shortSha}`; + break; + + case RemoteResourceType.File: + description = `$(file-text) ${path.basename(resource.fileName)}`; + break; + + case RemoteResourceType.Repo: + description = `$(repo) Repository`; + break; + + case RemoteResourceType.Revision: + if (resource.commit !== undefined && resource.commit instanceof GitLogCommit) { + if (resource.commit.status === 'D') { + resource.sha = resource.commit.previousSha; + description = `$(file-text) ${path.basename(resource.fileName)} in ${ + GlyphChars.Space + }$(git-commit) ${resource.commit.previousShortSha} (deleted in ${ + GlyphChars.Space + }$(git-commit) ${resource.commit.shortSha})`; + } + else { + resource.sha = resource.commit.sha; + description = `$(file-text) ${path.basename(resource.fileName)} in ${ + GlyphChars.Space + }$(git-commit) ${resource.commit.shortSha}`; + } + } + else { + const shortFileSha = resource.sha === undefined ? '' : GitService.shortenSha(resource.sha); + description = `$(file-text) ${path.basename(resource.fileName)}${ + shortFileSha ? ` in ${GlyphChars.Space}$(git-commit) ${shortFileSha}` : '' + }`; + } + break; + } + + const remote = remotes[0]; + if (remotes.length === 1) { + super( + { + label: `$(link-external) Open ${name} in ${remote.provider!.name}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(repo) ${remote.provider!.path} ${Strings.pad( + GlyphChars.Dot, + 1, + 1 + )} ${description}` + }, + Commands.OpenInRemote, + [ + undefined, + { + remotes, + resource, + goBackCommand + } as OpenInRemoteCommandArgs + ] + ); + + return; + } + + const provider = remotes.every(r => r.provider !== undefined && r.provider.name === remote.provider!.name) + ? remote.provider!.name + : 'Remote'; + + super( + { + label: `$(link-external) Open ${name} in ${provider}${GlyphChars.Ellipsis}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${description}` + }, + Commands.OpenInRemote, + [ + undefined, + { + remotes, + resource, + goBackCommand + } as OpenInRemoteCommandArgs + ] + ); + } +} + +export class RemotesQuickPick { + static async show( + remotes: GitRemote[], + placeHolder: string, + resource: RemoteResource, + goBackCommand?: CommandQuickPickItem + ): Promise { + const items = remotes.map(r => new OpenRemoteCommandQuickPickItem(r, resource)) as ( + | OpenRemoteCommandQuickPickItem + | CommandQuickPickItem)[]; + + if (goBackCommand) { + items.splice(0, 0, goBackCommand); + } + + // const scope = await Container.keyboard.beginScope({ left: goBackCommand }); + + const pick = await window.showQuickPick(items, { + placeHolder: placeHolder, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + } as QuickPickOptions); + if (pick === undefined) return undefined; + + // await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/repoStatusQuickPick.ts b/src/quickpicks/repoStatusQuickPick.ts new file mode 100644 index 0000000..86e8f53 --- /dev/null +++ b/src/quickpicks/repoStatusQuickPick.ts @@ -0,0 +1,478 @@ +'use strict'; +import * as path from 'path'; +import { commands, QuickPickOptions, TextDocumentShowOptions, window } from 'vscode'; +import { + Commands, + DiffWithPreviousCommandArgs, + OpenChangedFilesCommandArgs, + ShowQuickBranchHistoryCommandArgs, + ShowQuickRepoStatusCommandArgs, + ShowQuickStashListCommandArgs +} from '../commands'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { + GitCommitType, + GitLogCommit, + GitService, + GitStatus, + GitStatusFile, + GitStatusFileStatus, + GitUri +} from '../gitService'; +import { Keys } from '../keyboard'; +import { Iterables, Strings } from '../system'; +import { + CommandQuickPickItem, + getQuickPickIgnoreFocusOut, + OpenFileCommandQuickPickItem, + QuickPickItem +} from './commonQuickPicks'; + +export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPickItem { + public readonly status: GitStatusFile; + private readonly commit: GitLogCommit; + + constructor(status: GitStatusFile, realIndexStatus?: GitStatusFileStatus, item?: QuickPickItem) { + const octicon = status.getOcticon(); + const description = status.getFormattedDirectory(true); + + super( + status.uri, + item || { + label: `${status.staged ? '$(check)' : GlyphChars.Space.repeat(3)}${Strings.pad( + octicon, + 2, + 2 + )} ${path.basename(status.fileName)}`, + description: description + } + ); + + this.status = status; + if (status.indexStatus !== undefined) { + this.commit = new GitLogCommit( + GitCommitType.File, + status.repoPath, + GitService.stagedUncommittedSha, + 'You', + undefined, + new Date(), + '', + status.fileName, + [status], + status.status, + status.originalFileName, + 'HEAD', + status.fileName + ); + } + else { + this.commit = new GitLogCommit( + GitCommitType.File, + status.repoPath, + GitService.uncommittedSha, + 'You', + undefined, + new Date(), + '', + status.fileName, + [status], + status.status, + status.originalFileName, + realIndexStatus !== undefined ? GitService.stagedUncommittedSha : 'HEAD', + status.fileName + ); + } + } + + onDidPressKey(key: Keys): Promise<{} | undefined> { + return commands.executeCommand( + Commands.DiffWithPrevious, + GitUri.fromFileStatus(this.status, this.status.repoPath), + { + commit: this.commit, + line: 0, + showOptions: { + preserveFocus: true, + preview: false + } as TextDocumentShowOptions + } as DiffWithPreviousCommandArgs + ) as Promise<{} | undefined>; + } +} + +export class OpenStatusFilesCommandQuickPickItem extends CommandQuickPickItem { + constructor(statuses: GitStatusFile[], item?: QuickPickItem) { + const uris = statuses.map(f => f.uri); + + super( + item || { + label: `$(file-symlink-file) Open Changed Files`, + description: '' + // detail: `Opens all of the changed files in the repository` + }, + Commands.OpenChangedFiles, + [ + undefined, + { + uris + } as OpenChangedFilesCommandArgs + ] + ); + } +} + +interface ComputedStatus { + staged: number; + stagedAddsAndChanges: GitStatusFile[]; + stagedStatus: string; + + unstaged: number; + unstagedAddsAndChanges: GitStatusFile[]; + unstagedStatus: string; +} + +export class RepoStatusQuickPick { + private static computeStatus(files: GitStatusFile[]): ComputedStatus { + let stagedAdds = 0; + let unstagedAdds = 0; + let stagedChanges = 0; + let unstagedChanges = 0; + let stagedDeletes = 0; + let unstagedDeletes = 0; + + const stagedAddsAndChanges: GitStatusFile[] = []; + const unstagedAddsAndChanges: GitStatusFile[] = []; + + for (const f of files) { + switch (f.indexStatus) { + case 'A': + case '?': + stagedAdds++; + stagedAddsAndChanges.push(f); + break; + + case 'D': + stagedDeletes++; + break; + + case undefined: + break; + + default: + stagedChanges++; + stagedAddsAndChanges.push(f); + break; + } + + switch (f.workTreeStatus) { + case 'A': + case '?': + unstagedAdds++; + unstagedAddsAndChanges.push(f); + break; + + case 'D': + unstagedDeletes++; + break; + + case undefined: + break; + + default: + unstagedChanges++; + unstagedAddsAndChanges.push(f); + break; + } + } + + const staged = stagedAdds + stagedChanges + stagedDeletes; + const unstaged = unstagedAdds + unstagedChanges + unstagedDeletes; + + return { + staged: staged, + stagedStatus: staged > 0 ? `+${stagedAdds} ~${stagedChanges} -${stagedDeletes}` : '', + stagedAddsAndChanges: stagedAddsAndChanges, + unstaged: unstaged, + unstagedStatus: unstaged > 0 ? `+${unstagedAdds} ~${unstagedChanges} -${unstagedDeletes}` : '', + unstagedAddsAndChanges: unstagedAddsAndChanges + }; + } + + static async show( + status: GitStatus, + goBackCommand?: CommandQuickPickItem + ): Promise< + OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem | undefined + > { + const items = [ + ...Iterables.flatMap(status.files, s => { + if (s.workTreeStatus !== undefined && s.indexStatus !== undefined) { + return [ + new OpenStatusFileCommandQuickPickItem(s.with({ indexStatus: null }), s.indexStatus), + new OpenStatusFileCommandQuickPickItem(s.with({ workTreeStatus: null })) + ]; + } + else { + return [new OpenStatusFileCommandQuickPickItem(s)]; + } + }) + ] as (OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem)[]; + + // Sort the status by staged and then filename + items.sort( + (a, b) => + ((a as OpenStatusFileCommandQuickPickItem).status.staged ? -1 : 1) - + ((b as OpenStatusFileCommandQuickPickItem).status.staged ? -1 : 1) || + (a as OpenStatusFileCommandQuickPickItem).status.fileName.localeCompare( + (b as OpenStatusFileCommandQuickPickItem).status.fileName + ) + ); + + const currentCommand = new CommandQuickPickItem( + { + label: `go back ${GlyphChars.ArrowBack}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to ${GlyphChars.Space}$(git-branch) ${ + status.branch + } status` + }, + Commands.ShowQuickRepoStatus, + [ + undefined, + { + goBackCommand + } as ShowQuickRepoStatusCommandArgs + ] + ); + + const computed = this.computeStatus(status.files); + if (computed.staged > 0) { + let index = 0; + const unstagedIndex = computed.unstaged > 0 ? status.files.findIndex(f => !f.staged) : -1; + if (unstagedIndex > -1) { + items.splice( + unstagedIndex, + 0, + new CommandQuickPickItem( + { + label: `Unstaged Files`, + description: computed.unstagedStatus + }, + Commands.ShowQuickRepoStatus, + [ + undefined, + { + goBackCommand + } as ShowQuickRepoStatusCommandArgs + ] + ) + ); + + items.splice( + unstagedIndex, + 0, + new OpenStatusFilesCommandQuickPickItem(computed.stagedAddsAndChanges, { + label: `${GlyphChars.Space.repeat(4)} $(file-symlink-file) Open Staged Files`, + description: '' + }) + ); + + items.push( + new OpenStatusFilesCommandQuickPickItem(computed.unstagedAddsAndChanges, { + label: `${GlyphChars.Space.repeat(4)} $(file-symlink-file) Open Unstaged Files`, + description: '' + }) + ); + } + + items.splice( + index++, + 0, + new CommandQuickPickItem( + { + label: `Staged Files`, + description: computed.stagedStatus + }, + Commands.ShowQuickRepoStatus, + [ + undefined, + { + goBackCommand + } as ShowQuickRepoStatusCommandArgs + ] + ) + ); + } + else if (status.files.some(f => !f.staged)) { + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `Unstaged Files`, + description: computed.unstagedStatus + }, + Commands.ShowQuickRepoStatus, + [ + undefined, + { + goBackCommand + } as ShowQuickRepoStatusCommandArgs + ] + ) + ); + } + + if (status.files.length) { + items.push( + new OpenStatusFilesCommandQuickPickItem( + computed.stagedAddsAndChanges.concat(computed.unstagedAddsAndChanges) + ) + ); + items.push( + new CommandQuickPickItem( + { + label: '$(x) Close Unchanged Files', + description: '' + }, + Commands.CloseUnchangedFiles + ) + ); + } + else { + items.push( + new CommandQuickPickItem( + { + label: `No changes in the working tree`, + description: '' + }, + Commands.ShowQuickRepoStatus, + [ + undefined, + { + goBackCommand + } as ShowQuickRepoStatusCommandArgs + ] + ) + ); + } + + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `$(inbox) Show Stashed Changes`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows stashed changes in the repository` + }, + Commands.ShowQuickStashList, + [ + GitUri.fromRepoPath(status.repoPath), + { + goBackCommand: currentCommand + } as ShowQuickStashListCommandArgs + ] + ) + ); + + if (status.upstream && status.state.ahead) { + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `$(cloud-upload)${GlyphChars.Space} ${status.state.ahead} Commit${ + status.state.ahead > 1 ? 's' : '' + } ahead of ${GlyphChars.Space}$(git-branch) ${status.upstream}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows commits in ${ + GlyphChars.Space + }$(git-branch) ${status.branch} but not ${GlyphChars.Space}$(git-branch) ${status.upstream}` + }, + Commands.ShowQuickBranchHistory, + [ + GitUri.fromRepoPath(status.repoPath, `${status.upstream}..${status.branch}`), + { + branch: status.branch, + maxCount: 0, + goBackCommand: currentCommand + } as ShowQuickBranchHistoryCommandArgs + ] + ) + ); + } + + if (status.upstream && status.state.behind) { + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `$(cloud-download)${GlyphChars.Space} ${status.state.behind} Commit${ + status.state.behind > 1 ? 's' : '' + } behind ${GlyphChars.Space}$(git-branch) ${status.upstream}`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows commits in ${ + GlyphChars.Space + }$(git-branch) ${status.upstream} but not ${GlyphChars.Space}$(git-branch) ${status.branch}${ + status.sha + ? ` (since ${GlyphChars.Space}$(git-commit) ${GitService.shortenSha(status.sha)})` + : '' + }` + }, + Commands.ShowQuickBranchHistory, + [ + GitUri.fromRepoPath(status.repoPath, `${status.branch}..${status.upstream}`), + { + branch: status.upstream, + maxCount: 0, + goBackCommand: currentCommand + } as ShowQuickBranchHistoryCommandArgs + ] + ) + ); + } + + if (status.upstream && !status.state.ahead && !status.state.behind) { + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `$(git-branch) ${status.branch} is up-to-date with ${GlyphChars.Space}$(git-branch) ${ + status.upstream + }`, + description: '' + }, + Commands.ShowQuickRepoStatus, + [ + undefined, + { + goBackCommand + } as ShowQuickRepoStatusCommandArgs + ] + ) + ); + } + + if (goBackCommand) { + items.splice(0, 0, goBackCommand); + } + + const scope = await Container.keyboard.beginScope({ left: goBackCommand }); + + const pick = await window.showQuickPick(items, { + matchOnDescription: true, + placeHolder: `status of ${status.branch}${ + status.upstream ? ` ${Strings.pad(GlyphChars.ArrowLeftRightLong, 1, 1)} ${status.upstream}` : '' + }`, + ignoreFocusOut: getQuickPickIgnoreFocusOut(), + onDidSelectItem: (item: QuickPickItem) => { + scope.setKeyCommand('right', item); + } + } as QuickPickOptions); + + await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/repositoriesQuickPick.ts b/src/quickpicks/repositoriesQuickPick.ts new file mode 100644 index 0000000..e4077f0 --- /dev/null +++ b/src/quickpicks/repositoriesQuickPick.ts @@ -0,0 +1,49 @@ +'use strict'; +import { QuickPickItem, QuickPickOptions, window } from 'vscode'; +import { Container } from '../container'; +import { Repository } from '../gitService'; +import { Iterables } from '../system'; +import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; + +export class RepositoryQuickPickItem implements QuickPickItem { + label: string; + description: string; + detail: string | undefined; + + constructor( + public readonly repository: Repository + ) { + this.label = repository.name; + this.description = repository.path; + } + + get repoPath(): string { + return this.repository.path; + } +} + +export class RepositoriesQuickPick { + static async show( + placeHolder: string, + goBackCommand?: CommandQuickPickItem + ): Promise { + const items = [ + ...Iterables.map(await Container.git.getRepositories(), r => new RepositoryQuickPickItem(r)) + ] as (RepositoryQuickPickItem | CommandQuickPickItem)[]; + + if (goBackCommand !== undefined) { + items.splice(0, 0, goBackCommand); + } + + // const scope = await Container.keyboard.beginScope({ left: goBackCommand }); + + const pick = await window.showQuickPick(items, { + placeHolder: placeHolder, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + } as QuickPickOptions); + + // await scope.dispose(); + + return pick; + } +} diff --git a/src/quickpicks/stashListQuickPick.ts b/src/quickpicks/stashListQuickPick.ts new file mode 100644 index 0000000..3c40d13 --- /dev/null +++ b/src/quickpicks/stashListQuickPick.ts @@ -0,0 +1,84 @@ +'use strict'; +import { CancellationTokenSource, QuickPickOptions, window } from 'vscode'; +import { Commands, StashSaveCommandArgs } from '../commands'; +import { GlyphChars } from '../constants'; +import { Container } from '../container'; +import { GitStash } from '../gitService'; +import { KeyNoopCommand } from '../keyboard'; +import { Iterables, Strings } from '../system'; +import { + CommandQuickPickItem, + CommitQuickPickItem, + getQuickPickIgnoreFocusOut, + showQuickPickProgress +} from './commonQuickPicks'; + +export class StashListQuickPick { + static showProgress(mode: 'list' | 'apply') { + const message = + mode === 'apply' + ? `Apply stashed changes to your working tree${GlyphChars.Ellipsis}` + : `stashed changes ${GlyphChars.Dash} search by message, filename, or commit id`; + return showQuickPickProgress(message, { + left: KeyNoopCommand, + ',': KeyNoopCommand, + '.': KeyNoopCommand + }); + } + + static async show( + stash: GitStash, + mode: 'list' | 'apply', + progressCancellation: CancellationTokenSource, + goBackCommand?: CommandQuickPickItem, + currentCommand?: CommandQuickPickItem + ): Promise { + const items = ((stash && Array.from(Iterables.map(stash.commits.values(), c => new CommitQuickPickItem(c)))) || + []) as (CommitQuickPickItem | CommandQuickPickItem)[]; + + if (mode === 'list') { + items.splice( + 0, + 0, + new CommandQuickPickItem( + { + label: `$(plus) Stash Changes`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} stashes all changes` + }, + Commands.StashSave, + [ + { + goBackCommand: currentCommand + } as StashSaveCommandArgs + ] + ) + ); + } + + if (goBackCommand) { + items.splice(0, 0, goBackCommand); + } + + if (progressCancellation.token.isCancellationRequested) return undefined; + + const scope = await Container.keyboard.beginScope({ left: goBackCommand }); + + progressCancellation.cancel(); + + const pick = await window.showQuickPick(items, { + matchOnDescription: true, + placeHolder: + mode === 'apply' + ? `Apply stashed changes to your working tree${GlyphChars.Ellipsis}` + : `stashed changes ${GlyphChars.Dash} search by message, filename, or commit id`, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + // onDidSelectItem: (item: QuickPickItem) => { + // scope.setKeyCommand('right', item); + // } + } as QuickPickOptions); + + await scope.dispose(); + + return pick; + } +}