From 87e214c51bc34e2d1ac7dd8e570b304d1ffb891d Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sat, 3 Nov 2018 01:21:18 -0400 Subject: [PATCH] Reworks branches/tags quickpick --- src/commands/diffWithRevision.ts | 2 +- src/commands/openBranchInRemote.ts | 28 ++-- src/commands/openFileInRemote.ts | 35 ++--- src/commands/openFileRevision.ts | 2 +- src/commands/showQuickBranchHistory.ts | 17 +-- src/commands/showQuickFileHistory.ts | 2 +- src/quickpicks.ts | 1 - src/quickpicks/branchesAndTagsQuickPick.ts | 219 +++++++++++++++++++---------- src/quickpicks/branchesQuickPick.ts | 43 ------ src/quickpicks/commonQuickPicks.ts | 4 +- src/views/nodes/fileHistoryTrackerNode.ts | 14 +- src/views/nodes/lineHistoryTrackerNode.ts | 14 +- src/views/nodes/statusUpstreamNode.ts | 2 +- src/views/nodes/tagsNode.ts | 2 +- 14 files changed, 210 insertions(+), 175 deletions(-) delete mode 100644 src/quickpicks/branchesQuickPick.ts diff --git a/src/commands/diffWithRevision.ts b/src/commands/diffWithRevision.ts index e2476e9..0773655 100644 --- a/src/commands/diffWithRevision.ts +++ b/src/commands/diffWithRevision.ts @@ -129,7 +129,7 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand { return commands.executeCommand(Commands.DiffWithRevision, gitUri, { ...args, - branchOrTag: branchOrTag.branchOrTag, + branchOrTag: branchOrTag.item, goBackCommand: currentCommand } as DiffWithRevisionCommandArgs); } diff --git a/src/commands/openBranchInRemote.ts b/src/commands/openBranchInRemote.ts index 1a6b224..b4d6d63 100644 --- a/src/commands/openBranchInRemote.ts +++ b/src/commands/openBranchInRemote.ts @@ -4,7 +4,7 @@ import { GlyphChars } from '../constants'; import { Container } from '../container'; import { GitUri } from '../git/gitService'; import { Logger } from '../logger'; -import { BranchesQuickPick, CommandQuickPickItem } from '../quickpicks'; +import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../quickpicks'; import { ActiveEditorCommand, command, @@ -53,21 +53,19 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand { if (args.branch === undefined) { args = { ...args }; - const branches = (await Container.git.getBranches(repoPath)).filter(b => b.tracking !== undefined); - if (branches.length > 1) { - const pick = await BranchesQuickPick.show( - branches, - `Open which branch on remote${GlyphChars.Ellipsis}` - ); - if (pick === undefined) return undefined; + const pick = await new BranchesAndTagsQuickPick(repoPath).show( + `Open which branch on remote${GlyphChars.Ellipsis}`, + { + autoPick: true, + filters: { + branches: b => b.tracking !== undefined + }, + include: 'branches' + } + ); + if (pick === undefined || pick instanceof CommandQuickPickItem) return undefined; - if (pick instanceof CommandQuickPickItem) return undefined; - - args.branch = pick.branch.name; - } - else if (branches.length === 1) { - args.branch = branches[0].name; - } + args.branch = pick.item.name; } const remotes = await Container.git.getRemotes(repoPath); diff --git a/src/commands/openFileInRemote.ts b/src/commands/openFileInRemote.ts index 7fd9d6f..16de8da 100644 --- a/src/commands/openFileInRemote.ts +++ b/src/commands/openFileInRemote.ts @@ -1,10 +1,11 @@ 'use strict'; +import { __await } from 'tslib'; import { commands, Range, TextEditor, Uri, window } from 'vscode'; import { GlyphChars } from '../constants'; import { Container } from '../container'; import { GitUri } from '../git/gitService'; import { Logger } from '../logger'; -import { BranchesQuickPick, CommandQuickPickItem } from '../quickpicks'; +import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../quickpicks'; import { ActiveEditorCommand, command, @@ -54,27 +55,21 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand { if (args.branch === undefined) { const branch = await Container.git.getBranch(gitUri.repoPath); if (branch === undefined || branch.tracking === undefined) { - const branches = (await Container.git.getBranches(gitUri.repoPath)).filter( - b => b.tracking !== undefined + const pick = await new BranchesAndTagsQuickPick(gitUri.repoPath).show( + args.clipboard + ? `Copy url for ${gitUri.getRelativePath()} to clipboard for which branch${GlyphChars.Ellipsis}` + : `Open ${gitUri.getRelativePath()} on remote for which branch${GlyphChars.Ellipsis}`, + { + autoPick: true, + filters: { + branches: b => b.tracking !== undefined + }, + include: 'branches' + } ); - if (branches.length > 1) { - const pick = await BranchesQuickPick.show( - branches, - args.clipboard - ? `Copy url for ${gitUri.getRelativePath()} to clipboard for which branch${ - GlyphChars.Ellipsis - }` - : `Open ${gitUri.getRelativePath()} on remote for which branch${GlyphChars.Ellipsis}` - ); - if (pick === undefined) return undefined; + if (pick === undefined || pick instanceof CommandQuickPickItem) return undefined; - if (pick instanceof CommandQuickPickItem) return undefined; - - args.branch = pick.branch.name; - } - else if (branches.length === 1) { - args.branch = branches[0].name; - } + args.branch = pick.item.name; } else { args.branch = branch.name; diff --git a/src/commands/openFileRevision.ts b/src/commands/openFileRevision.ts index a2225f2..192085f 100644 --- a/src/commands/openFileRevision.ts +++ b/src/commands/openFileRevision.ts @@ -157,7 +157,7 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand { return commands.executeCommand(Commands.OpenFileRevision, gitUri, { ...args, - branchOrTag: branchOrTag.branchOrTag, + branchOrTag: branchOrTag.item, goBackCommand: currentCommand } as OpenFileRevisionCommandArgs); } diff --git a/src/commands/showQuickBranchHistory.ts b/src/commands/showQuickBranchHistory.ts index 0cc96c8..faecd70 100644 --- a/src/commands/showQuickBranchHistory.ts +++ b/src/commands/showQuickBranchHistory.ts @@ -5,7 +5,7 @@ import { Container } from '../container'; import { GitLog, GitUri } from '../git/gitService'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { BranchesQuickPick, BranchHistoryQuickPick, CommandQuickPickItem } from '../quickpicks'; +import { BranchesAndTagsQuickPick, BranchHistoryQuickPick, CommandQuickPickItem } from '../quickpicks'; import { Strings } from '../system'; import { ActiveEditorCachedCommand, command, Commands, getCommandUri, getRepoPathOrActiveOrPrompt } from './common'; import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails'; @@ -46,8 +46,6 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand { if (!repoPath) return undefined; if (args.branch === undefined) { - const branches = await Container.git.getBranches(repoPath); - let goBackCommand; if (!(await Container.git.getRepoPathOrActive(uri, editor))) { goBackCommand = new CommandQuickPickItem( @@ -60,14 +58,17 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand { ); } - const pick = await BranchesQuickPick.show(branches, `Show history for branch${GlyphChars.Ellipsis}`, { - goBackCommand: goBackCommand - }); + const pick = await new BranchesAndTagsQuickPick(repoPath).show( + `Show history for branch${GlyphChars.Ellipsis}`, + { + goBack: goBackCommand, + include: 'branches' + } + ); if (pick === undefined) return undefined; - if (pick instanceof CommandQuickPickItem) return pick.execute(); - args.branch = pick.branch.name; + args.branch = pick.item.name; if (args.branch === undefined) return undefined; progressCancellation = BranchHistoryQuickPick.showProgress(args.branch); diff --git a/src/commands/showQuickFileHistory.ts b/src/commands/showQuickFileHistory.ts index 312500d..1452e61 100644 --- a/src/commands/showQuickFileHistory.ts +++ b/src/commands/showQuickFileHistory.ts @@ -172,7 +172,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand { return commands.executeCommand(Commands.ShowQuickFileHistory, gitUri, { ...args, log: undefined, - branchOrTag: branchOrTag.branchOrTag, + branchOrTag: branchOrTag.item, goBackCommand: currentCommand } as ShowQuickFileHistoryCommandArgs); } diff --git a/src/quickpicks.ts b/src/quickpicks.ts index 13017bc..1f66862 100644 --- a/src/quickpicks.ts +++ b/src/quickpicks.ts @@ -1,7 +1,6 @@ 'use strict'; export * from './quickpicks/branchesAndTagsQuickPick'; -export * from './quickpicks/branchesQuickPick'; export * from './quickpicks/branchHistoryQuickPick'; export * from './quickpicks/commitFileQuickPick'; export * from './quickpicks/commitQuickPick'; diff --git a/src/quickpicks/branchesAndTagsQuickPick.ts b/src/quickpicks/branchesAndTagsQuickPick.ts index 99cfe34..3b79b62 100644 --- a/src/quickpicks/branchesAndTagsQuickPick.ts +++ b/src/quickpicks/branchesAndTagsQuickPick.ts @@ -3,44 +3,81 @@ import { CancellationToken, CancellationTokenSource, QuickPickItem, QuickPickOpt import { GlyphChars } from '../constants'; import { Container } from '../container'; import { GitBranch, GitTag } from '../git/gitService'; -import { KeyNoopCommand } from '../keyboard'; -import { Functions, Iterables } from '../system'; +import { Functions } from '../system'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './commonQuickPicks'; -export class BranchOrTagQuickPickItem implements QuickPickItem { +export class BranchQuickPickItem implements QuickPickItem { label: string; description: string; detail: string | undefined; constructor( - public readonly branchOrTag: GitBranch | GitTag, + public readonly branch: GitBranch, checked?: boolean ) { - if (branchOrTag instanceof GitBranch) { - this.label = `${ - checked === true || (checked === undefined && 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`; - } + checked = checked || (checked === undefined && branch.current); + this.label = `${checked ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${branch.name}`; + this.description = branch.remote ? `${GlyphChars.Space.repeat(2)} remote branch` : ''; } get current() { - return this.branchOrTag instanceof GitBranch ? this.branchOrTag.current : false; + return this.branch.current; + } + + get item() { + return this.branch; } get name() { - return this.branchOrTag.name; + return this.branch.name; } get remote() { - return this.branchOrTag instanceof GitBranch && this.branchOrTag.remote; + return this.branch.remote; + } +} + +export class TagQuickPickItem implements QuickPickItem { + label: string; + description: string; + detail: string | undefined; + + constructor( + public readonly tag: GitBranch | GitTag, + checked?: boolean + ) { + this.label = `${checked ? `$(check)${GlyphChars.Space}` : GlyphChars.Space.repeat(4)} ${tag.name}`; + this.description = `${GlyphChars.Space.repeat(2)} tag`; } + + get current() { + return false; + } + + get item() { + return this.tag; + } + + get name() { + return this.tag.name; + } + + get remote() { + return false; + } +} + +export type BranchOrTagQuickPickItem = BranchQuickPickItem | TagQuickPickItem; + +export interface BranchesAndTagsQuickPickOptions { + autoPick?: boolean; + checked?: string; + filters?: { + branches?(branch: GitBranch): boolean; + tags?(tag: GitTag): boolean; + }; + goBack?: CommandQuickPickItem; + include?: 'branches' | 'tags' | 'all'; } export class BranchesAndTagsQuickPick { @@ -50,75 +87,115 @@ export class BranchesAndTagsQuickPick { async show( placeHolder: string, - options: { checked?: string; goBack?: CommandQuickPickItem } = {} + options: BranchesAndTagsQuickPickOptions = {} ): Promise { const cancellation = new CancellationTokenSource(); + let scope; + if (options.goBack) { + scope = await Container.keyboard.beginScope({ left: options.goBack }); + } + + let autoPick; try { - const items = this.getItems(options, cancellation.token); - const scope = await Container.keyboard.beginScope({ left: options.goBack || KeyNoopCommand }); + let items = this.getItems(options, cancellation.token); + if (options.autoPick) { + items = items.then(itms => { + if (itms.length <= 1) { + autoPick = itms[0]; + cancellation.cancel(); + } + return itms; + }); + } - const pick = await window.showQuickPick(items, { - placeHolder: placeHolder, - ignoreFocusOut: getQuickPickIgnoreFocusOut() - } as QuickPickOptions); + let pick = await window.showQuickPick( + items, + { + placeHolder: placeHolder, + ignoreFocusOut: getQuickPickIgnoreFocusOut() + } as QuickPickOptions, + cancellation.token + ); + + if (pick === undefined && autoPick !== undefined) { + pick = autoPick; + } if (pick === undefined) { cancellation.cancel(); } - await scope.dispose(); return pick; } finally { + if (scope !== undefined) { + await scope.dispose(); + } + cancellation.dispose(); } } - private async getItems( - options: { checked?: string; goBack?: CommandQuickPickItem } = {}, - token: CancellationToken - ) { - const result = await Functions.cancellable( - Promise.all([Container.git.getBranches(this.repoPath), Container.git.getTags(this.repoPath)]), - token - ); - if (result === undefined || token.isCancellationRequested) return []; - - const [branches, tags] = result; - - const items = [ - ...Iterables.filterMap( - branches, - b => - !b.remote - ? new BranchOrTagQuickPickItem( - b, - options.checked !== undefined ? b.name === options.checked : undefined - ) - : undefined - ), - ...Iterables.filterMap( - branches, - b => - b.remote - ? new BranchOrTagQuickPickItem( - b, - options.checked !== undefined ? b.name === options.checked : undefined - ) - : undefined - ), - ...tags.map( - t => - new BranchOrTagQuickPickItem( - t, - options.checked !== undefined ? t.name === options.checked : undefined - ) - ) - ] as (BranchOrTagQuickPickItem | CommandQuickPickItem)[]; - - if (options.goBack !== undefined) { - items.splice(0, 0, options.goBack); + private async getItems(options: BranchesAndTagsQuickPickOptions, token: CancellationToken) { + const { checked, filters, goBack, include } = { include: 'all', ...options }; + + let branches; + let tags; + switch (include) { + case 'branches': { + const result = await Functions.cancellable(Container.git.getBranches(this.repoPath), token); + if (result === undefined || token.isCancellationRequested) return []; + + branches = result; + break; + } + case 'tags': { + const result = await Functions.cancellable(Container.git.getTags(this.repoPath), token); + if (result === undefined || token.isCancellationRequested) return []; + + tags = result; + break; + } + default: { + const result = await Functions.cancellable( + Promise.all([Container.git.getBranches(this.repoPath), Container.git.getTags(this.repoPath)]), + token + ); + if (result === undefined || token.isCancellationRequested) return []; + + [branches, tags] = result; + break; + } + } + + const items: (BranchOrTagQuickPickItem | CommandQuickPickItem)[] = []; + + if (branches !== undefined) { + const filter = + filters !== undefined && typeof filters.branches === 'function' ? filters.branches : undefined; + + branches.sort((a, b) => (b.remote ? -1 : 1) - (a.remote ? -1 : 1) || a.name.localeCompare(b.name)); + for (const b of branches) { + if (filter !== undefined && !filter(b)) continue; + + items.push(new BranchQuickPickItem(b, checked !== undefined ? b.name === checked : undefined)); + } + } + + if (tags !== undefined) { + const filter = filters !== undefined && typeof filters.tags === 'function' ? filters.tags : undefined; + + tags.sort((a, b) => a.name.localeCompare(b.name)); + for (const t of tags) { + if (filter !== undefined && !filter(t)) continue; + + items.push(new TagQuickPickItem(t, checked !== undefined ? t.name === checked : undefined)); + } + } + + if (goBack !== undefined) { + items.splice(0, 0, goBack); } return items; diff --git a/src/quickpicks/branchesQuickPick.ts b/src/quickpicks/branchesQuickPick.ts deleted file mode 100644 index 1ba16ff..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 '../git/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/commonQuickPicks.ts b/src/quickpicks/commonQuickPicks.ts index 00ebfd8..99293fa 100644 --- a/src/quickpicks/commonQuickPicks.ts +++ b/src/quickpicks/commonQuickPicks.ts @@ -16,7 +16,7 @@ import { Container } from '../container'; import { GitLog, GitLogCommit, GitStashCommit } from '../git/gitService'; import { KeyMapping, Keys } from '../keyboard'; import { Strings } from '../system'; -import { BranchesAndTagsQuickPick, BranchOrTagQuickPickItem } from './branchesAndTagsQuickPick'; +import { BranchesAndTagsQuickPick, BranchQuickPickItem, TagQuickPickItem } from './branchesAndTagsQuickPick'; export function getQuickPickIgnoreFocusOut() { return !configuration.get(configuration.name('advanced')('quickPick')('closeOnFocusOut').value); @@ -145,7 +145,7 @@ export class ChooseFromBranchesAndTagsQuickPickItem extends CommandQuickPickItem super(item, undefined, undefined); } - execute(): Promise { + execute(): Promise { return new BranchesAndTagsQuickPick(this.repoPath).show(this.placeHolder, { goBack: this._goBack }); } } diff --git a/src/views/nodes/fileHistoryTrackerNode.ts b/src/views/nodes/fileHistoryTrackerNode.ts index 76d2fa6..6e976a6 100644 --- a/src/views/nodes/fileHistoryTrackerNode.ts +++ b/src/views/nodes/fileHistoryTrackerNode.ts @@ -2,9 +2,10 @@ import * as paths from 'path'; import { Disposable, TextEditor, TreeItem, TreeItemCollapsibleState, Uri, window } from 'vscode'; import { UriComparer } from '../../comparers'; +import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitCommitish, GitUri } from '../../git/gitService'; -import { BranchesAndTagsQuickPick, BranchOrTagQuickPickItem } from '../../quickpicks'; +import { BranchesAndTagsQuickPick, CommandQuickPickItem } from '../../quickpicks'; import { debug, Functions, gate, log } from '../../system'; import { FileHistoryView } from '../fileHistoryView'; import { MessageNode } from './common'; @@ -58,10 +59,13 @@ export class FileHistoryTrackerNode extends SubscribeableViewNode new CommitNode(c, this, this.view)), this, 1)]; + children = [...insertDateMarkers(Iterables.map(commits, c => new CommitNode(c, this, this.view)), this, 1)]; } else { children = [ diff --git a/src/views/nodes/tagsNode.ts b/src/views/nodes/tagsNode.ts index 5911a96..f2a807c 100644 --- a/src/views/nodes/tagsNode.ts +++ b/src/views/nodes/tagsNode.ts @@ -29,7 +29,7 @@ export class TagsNode extends ViewNode { if (tags.length === 0) return [new MessageNode(this, 'No tags could be found.')]; tags.sort((a, b) => a.name.localeCompare(b.name)); - const tagNodes = [...tags.map(t => new TagNode(t, this.uri, this, this.view))]; + const tagNodes = tags.map(t => new TagNode(t, this.uri, this, this.view)); if (this.view.config.branches.layout === ViewBranchesLayout.List) return tagNodes; const hierarchy = Arrays.makeHierarchical(