diff --git a/README.md b/README.md index f8852e2..329c70b 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,10 @@ While GitLens is generously offered to everyone free of charge, if you find it useful, please consider [**sponsoring**](https://gitlens.amod.io/#sponsor) it. Also please [write a review](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens#review-details 'Write a review'), [star me on GitHub](https://github.com/eamodio/vscode-gitlens 'Star me on GitHub'), and [follow me on Twitter](https://twitter.com/eamodio 'Follow me on Twitter') -# What's new in GitLens 10 - -- Adds all-new iconography to better match VS Code's new visual style -- Adds an all-new Welcome experience with a simple quick setup of common GitLens features -- Adds a new and improved interactive Settings editor experience -- Adds a new and improved _Git Commands_ experience -- Adds an all-new commit search experience — complete with ability to match on more than one search pattern -- Adds a _Reveal Commit in Repositories View_ command to reveal the current commit in the _Repositories_ view -- Adds a _Show Commits within Selection in Search Commits View_ command to show all the commits within the current selection in the _Search Commits_ view -- Adds new actions options to the Git Code Lens -- Adds ability to sort branches and tags in quick pick menus and views +# What's new in GitLens 11 + +TBD + - And much more See the [release notes](https://github.com/eamodio/vscode-gitlens/blob/master/CHANGELOG.md 'Open Release Notes') for the full set of changes @@ -767,19 +760,19 @@ See also [View Settings](#view-settings- 'Jump to the View settings') See also [View Settings](#view-settings- 'Jump to the View settings') -| Name | Description | -| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `gitlens.views.fileHistory.avatars` | Specifies whether to show avatar images instead of status icons in the _File History_ view | -| `gitlens.views.fileHistory.enabled` | Specifies whether to show the _File History_ view | +| Name | Description | +| ----------------------------------- | ------------------------------------------------------------------------------------------ | +| `gitlens.views.fileHistory.avatars` | Specifies whether to show avatar images instead of status icons in the _File History_ view | +| `gitlens.views.fileHistory.enabled` | Specifies whether to show the _File History_ view | ### Line History View Settings [#](#line-history-view-settings- 'Line History View Settings') See also [View Settings](#view-settings- 'Jump to the View settings') -| Name | Description | -| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `gitlens.views.lineHistory.avatars` | Specifies whether to show avatar images instead of status icons in the _Line History_ view | -| `gitlens.views.lineHistory.enabled` | Specifies whether to show the _Line History_ view | +| Name | Description | +| ----------------------------------- | ------------------------------------------------------------------------------------------ | +| `gitlens.views.lineHistory.avatars` | Specifies whether to show avatar images instead of status icons in the _Line History_ view | +| `gitlens.views.lineHistory.enabled` | Specifies whether to show the _Line History_ view | ### Search View Settings [#](#search-view-settings- 'Search View Settings') diff --git a/package.json b/package.json index 5f6ef45..459e61b 100644 --- a/package.json +++ b/package.json @@ -264,7 +264,7 @@ "enumDescriptions": [ "Toggles file blame annotations", "Compares the current committed file with the previous commit", - "Reveals the commit in the Repositories view", + "Reveals the commit in the side bar", "Shows the commits within the range in the Search Commits view", "Shows a commit details quick pick", "Shows a commit file details quick pick", @@ -317,7 +317,7 @@ "enumDescriptions": [ "Toggles file blame annotations", "Compares the current committed file with the previous commit", - "Reveals the commit in the Repositories view", + "Reveals the commit in the side bar", "Shows the commit in the Search Commits view", "Shows a commit details quick pick", "Shows a commit file details quick pick", @@ -1423,7 +1423,7 @@ "Compares the current line commit with the previous", "Compares the current line commit with the working tree", "Toggles Git code lens", - "Reveals the commit in the Repositories view", + "Reveals the commit in the side bar", "Shows the commit in the Search Commits view", "Shows a commit details quick pick", "Shows a commit file details quick pick", @@ -2585,7 +2585,7 @@ }, { "command": "gitlens.revealCommitInView", - "title": "Reveal Commit in Repositories View", + "title": "Reveal Commit in Side BSar", "category": "GitLens" }, { diff --git a/src/commands/git/stash.ts b/src/commands/git/stash.ts index dd792e5..de6aff6 100644 --- a/src/commands/git/stash.ts +++ b/src/commands/git/stash.ts @@ -4,7 +4,7 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitReference, GitStashCommit, GitStashReference, Repository } from '../../git/git'; import { GitUri } from '../../git/gitUri'; -import { GitCommandsCommand } from '../gitCommands'; +import { GitActions, GitCommandsCommand } from '../gitCommands'; import { appendReposToTitle, PartialStepState, @@ -353,7 +353,7 @@ export class StashGitCommand extends QuickCommand { additionalButtons: [QuickCommandButtons.RevealInView], onDidClickButton: (quickpick, button) => { if (button === QuickCommandButtons.RevealInView) { - void Container.repositoriesView.revealStash(state.reference, { + void GitActions.Stash.reveal(state.reference, { select: true, expand: true, }); @@ -412,7 +412,7 @@ export class StashGitCommand extends QuickCommand { additionalButtons: [QuickCommandButtons.RevealInView], onDidClickButton: (quickpick, button) => { if (button === QuickCommandButtons.RevealInView) { - void Container.repositoriesView.revealStash(state.reference, { + void GitActions.Stash.reveal(state.reference, { select: true, expand: true, }); diff --git a/src/commands/gitCommands.actions.ts b/src/commands/gitCommands.actions.ts index 5758836..fabd25d 100644 --- a/src/commands/gitCommands.actions.ts +++ b/src/commands/gitCommands.actions.ts @@ -11,7 +11,7 @@ import { GitCommandsCommandArgs, OpenWorkingFileCommandArgs, } from '../commands'; -import { FileAnnotationType } from '../configuration'; +import { configuration, FileAnnotationType } from '../configuration'; import { Container } from '../container'; import { GitBranchReference, @@ -19,6 +19,7 @@ import { GitFile, GitLogCommit, GitReference, + GitRemote, GitRevision, GitRevisionReference, GitStashReference, @@ -132,6 +133,28 @@ export namespace GitActions { }, }); } + + export async function reveal( + branch: GitBranchReference, + options?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }, + ) { + if (configuration.get('views', 'repositories', 'enabled')) { + return Container.repositoriesView.revealBranch(branch, options); + } + + let node; + if (!branch.remote) { + node = await Container.branchesView.revealBranch(branch, options); + } else { + node = await Container.remotesView.revealBranch(branch, options); + } + + return node; + } } export namespace Commit { @@ -641,6 +664,32 @@ export namespace GitActions { fileName: typeof file === 'string' ? file : file.fileName, })); } + + export async function reveal( + commit: GitRevisionReference, + options?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }, + ) { + if (configuration.get('views', 'repositories', 'enabled')) { + return Container.repositoriesView.revealCommit(commit, options); + } + + // TODO@eamodio stop duplicate notifications + + let node = await Container.commitsView.revealCommit(commit, options); + if (node != null) return node; + + node = await Container.branchesView.revealCommit(commit, options); + if (node != null) return node; + + node = await Container.remotesView.revealCommit(commit, options); + if (node != null) return node; + + return undefined; + } } export namespace Contributor { @@ -679,6 +728,22 @@ export namespace GitActions { }, }); } + + export async function reveal( + tag: GitTagReference, + options?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }, + ) { + if (configuration.get('views', 'repositories', 'enabled')) { + return Container.repositoriesView.revealTag(tag, options); + } + + const node = await Container.tagsView.revealTag(tag, options); + return node; + } } export namespace Remote { @@ -720,6 +785,22 @@ export namespace GitActions { export async function prune(repo: string | Repository, remote: string) { void (await Container.git.pruneRemote(typeof repo === 'string' ? repo : repo.path, remote)); } + + export async function reveal( + remote: GitRemote, + options?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }, + ) { + // if (configuration.get('views', 'repositories', 'enabled')) { + // return Container.repositoriesView.revealRemote(remote, options); + // } + + const node = await Container.remotesView.revealRemote(remote, options); + return node; + } } export namespace Stash { @@ -756,5 +837,21 @@ export namespace GitActions { }, }); } + + export async function reveal( + stash: GitStashReference, + options?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }, + ) { + if (configuration.get('views', 'repositories', 'enabled')) { + return Container.repositoriesView.revealStash(stash, options); + } + + const node = await Container.stashesView.revealStash(stash, options); + return node; + } } } diff --git a/src/commands/quickCommand.buttons.ts b/src/commands/quickCommand.buttons.ts index 876ad49..5aa7eb9 100644 --- a/src/commands/quickCommand.buttons.ts +++ b/src/commands/quickCommand.buttons.ts @@ -99,7 +99,7 @@ export namespace QuickCommandButtons { export const RevealInView: QuickInputButton = { iconPath: new ThemeIcon('eye'), - tooltip: 'Reveal in Repositories View', + tooltip: 'Reveal in Side Bar', }; export const ShowInView: QuickInputButton = { diff --git a/src/commands/quickCommand.steps.ts b/src/commands/quickCommand.steps.ts index 524c89a..af724c5 100644 --- a/src/commands/quickCommand.steps.ts +++ b/src/commands/quickCommand.steps.ts @@ -73,6 +73,7 @@ import { } from '../quickpicks'; import { Arrays, Iterables, Strings } from '../system'; import { GitUri } from '../git/gitUri'; +import { GitActions } from './gitCommands.actions'; export function appendReposToTitle< State extends { repo: Repository } | { repos: Repository[] }, @@ -382,7 +383,7 @@ export async function* pickBranchStep< return; } - void Container.repositoriesView.revealBranch(quickpick.activeItems[0].item, { + void GitActions.Branch.reveal(quickpick.activeItems[0].item, { select: true, expand: true, }); @@ -392,7 +393,7 @@ export async function* pickBranchStep< onDidPressKey: async quickpick => { if (quickpick.activeItems.length === 0) return; - await Container.repositoriesView.revealBranch(quickpick.activeItems[0].item, { + await GitActions.Branch.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -447,7 +448,7 @@ export async function* pickBranchesStep< return; } - void Container.repositoriesView.revealBranch(quickpick.activeItems[0].item, { + void GitActions.Branch.reveal(quickpick.activeItems[0].item, { select: true, expand: true, }); @@ -457,7 +458,7 @@ export async function* pickBranchesStep< onDidPressKey: async quickpick => { if (quickpick.activeItems.length === 0) return; - await Container.repositoriesView.revealBranch(quickpick.activeItems[0].item, { + await GitActions.Branch.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -558,11 +559,11 @@ export async function* pickBranchOrTagStep< const item = quickpick.activeItems[0].item; if (GitReference.isBranch(item)) { - void Container.repositoriesView.revealBranch(item, { select: true, expand: true }); + void GitActions.Branch.reveal(item, { select: true, expand: true }); } else if (GitReference.isTag(item)) { - void Container.repositoriesView.revealTag(item, { select: true, expand: true }); + void GitActions.Tag.reveal(item, { select: true, expand: true }); } else if (GitReference.isRevision(item)) { - void Container.repositoriesView.revealCommit(item, { select: true, expand: true }); + void GitActions.Commit.reveal(item, { select: true, expand: true }); } } }, @@ -572,11 +573,11 @@ export async function* pickBranchOrTagStep< const item = quickpick.activeItems[0].item; if (GitReference.isBranch(item)) { - void Container.repositoriesView.revealBranch(item, { select: true, focus: false, expand: true }); + void GitActions.Branch.reveal(item, { select: true, focus: false, expand: true }); } else if (GitReference.isTag(item)) { - void Container.repositoriesView.revealTag(item, { select: true, focus: false, expand: true }); + void GitActions.Tag.reveal(item, { select: true, focus: false, expand: true }); } else if (GitReference.isRevision(item)) { - void Container.repositoriesView.revealCommit(item, { select: true, focus: false, expand: true }); + void GitActions.Commit.reveal(item, { select: true, focus: false, expand: true }); } }, onValidateValue: getValidateGitReferenceFn(state.repo), @@ -674,11 +675,11 @@ export async function* pickBranchOrTagStepMultiRepo< const item = quickpick.activeItems[0].item; if (GitReference.isBranch(item)) { - void Container.repositoriesView.revealBranch(item, { select: true, expand: true }); + void GitActions.Branch.reveal(item, { select: true, expand: true }); } else if (GitReference.isTag(item)) { - void Container.repositoriesView.revealTag(item, { select: true, expand: true }); + void GitActions.Tag.reveal(item, { select: true, expand: true }); } else if (GitReference.isRevision(item)) { - void Container.repositoriesView.revealCommit(item, { select: true, expand: true }); + void GitActions.Commit.reveal(item, { select: true, expand: true }); } } }, @@ -688,11 +689,11 @@ export async function* pickBranchOrTagStepMultiRepo< const item = quickpick.activeItems[0].item; if (GitReference.isBranch(item)) { - void Container.repositoriesView.revealBranch(item, { select: true, focus: false, expand: true }); + void GitActions.Branch.reveal(item, { select: true, focus: false, expand: true }); } else if (GitReference.isTag(item)) { - void Container.repositoriesView.revealTag(item, { select: true, focus: false, expand: true }); + void GitActions.Tag.reveal(item, { select: true, focus: false, expand: true }); } else if (GitReference.isRevision(item)) { - void Container.repositoriesView.revealCommit(item, { select: true, focus: false, expand: true }); + void GitActions.Commit.reveal(item, { select: true, focus: false, expand: true }); } }, onValidateValue: getValidateGitReferenceFn(state.repos), @@ -778,7 +779,7 @@ export function* pickCommitStep< if (quickpick.activeItems.length === 0 || log == null) return; if (button === QuickCommandButtons.RevealInView) { - void Container.repositoriesView.revealCommit(quickpick.activeItems[0].item, { + void GitActions.Commit.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -809,7 +810,7 @@ export function* pickCommitStep< if (quickpick.activeItems.length === 0) return; if (key === 'ctrl+right') { - await Container.repositoriesView.revealCommit(quickpick.activeItems[0].item, { + await GitActions.Commit.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -897,7 +898,7 @@ export function* pickCommitsStep< if (quickpick.activeItems.length === 0 || log == null) return; if (button === QuickCommandButtons.RevealInView) { - void Container.repositoriesView.revealCommit(quickpick.activeItems[0].item, { + void GitActions.Commit.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -928,7 +929,7 @@ export function* pickCommitsStep< if (quickpick.activeItems.length === 0) return; if (key === 'ctrl+right') { - await Container.repositoriesView.revealCommit(quickpick.activeItems[0].item, { + await GitActions.Commit.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -1146,7 +1147,7 @@ export function* pickStashStep< expand: true, }); } else { - void Container.repositoriesView.revealStash(quickpick.activeItems[0].item, { + void GitActions.Stash.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -1179,7 +1180,7 @@ export function* pickStashStep< onDidPressKey: async quickpick => { if (quickpick.activeItems.length === 0) return; - await Container.repositoriesView.revealStash(quickpick.activeItems[0].item, { + await GitActions.Stash.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -1234,7 +1235,7 @@ export async function* pickTagsStep< return; } - void Container.repositoriesView.revealTag(quickpick.activeItems[0].item, { + void GitActions.Tag.reveal(quickpick.activeItems[0].item, { select: true, expand: true, }); @@ -1244,7 +1245,7 @@ export async function* pickTagsStep< onDidPressKey: async quickpick => { if (quickpick.activeItems.length === 0) return; - await Container.repositoriesView.revealTag(quickpick.activeItems[0].item, { + await GitActions.Tag.reveal(quickpick.activeItems[0].item, { select: true, focus: false, expand: true, @@ -1299,13 +1300,13 @@ export async function* showCommitOrStashStep< if (button === QuickCommandButtons.RevealInView) { if (GitReference.isStash(state.reference)) { - void Container.repositoriesView.revealStash(state.reference, { + void GitActions.Stash.reveal(state.reference, { select: true, focus: false, expand: true, }); } else { - void Container.repositoriesView.revealCommit(state.reference, { + void GitActions.Commit.reveal(state.reference, { select: true, focus: false, expand: true, @@ -1506,13 +1507,13 @@ export async function* showCommitOrStashFilesStep< if (button === QuickCommandButtons.RevealInView) { if (GitReference.isStash(state.reference)) { - void Container.repositoriesView.revealStash(state.reference, { + void GitActions.Stash.reveal(state.reference, { select: true, focus: false, expand: true, }); } else { - void Container.repositoriesView.revealCommit(state.reference, { + void GitActions.Commit.reveal(state.reference, { select: true, focus: false, expand: true, @@ -1578,13 +1579,13 @@ export async function* showCommitOrStashFileStep< if (button === QuickCommandButtons.RevealInView) { if (GitReference.isStash(state.reference)) { - void Container.repositoriesView.revealStash(state.reference, { + void GitActions.Stash.reveal(state.reference, { select: true, focus: false, expand: true, }); } else { - void Container.repositoriesView.revealCommit(state.reference, { + void GitActions.Commit.reveal(state.reference, { select: true, focus: false, expand: true, diff --git a/src/commands/showQuickCommit.ts b/src/commands/showQuickCommit.ts index 0d39018..d9ce81d 100644 --- a/src/commands/showQuickCommit.ts +++ b/src/commands/showQuickCommit.ts @@ -10,7 +10,7 @@ import { } from './common'; import { Container } from '../container'; import { GitCommit, GitLog, GitLogCommit } from '../git/git'; -import { executeGitCommand } from './gitCommands'; +import { executeGitCommand, GitActions } from './gitCommands'; import { GitUri } from '../git/gitUri'; import { Logger } from '../logger'; import { Messages } from '../messages'; @@ -137,7 +137,7 @@ export class ShowQuickCommitCommand extends ActiveEditorCachedCommand { } if (args.revealInView) { - void (await Container.repositoriesView.revealCommit(args.commit, { + void (await GitActions.Commit.reveal(args.commit, { select: true, focus: true, expand: true, diff --git a/src/quickpicks/quickPicksItems.ts b/src/quickpicks/quickPicksItems.ts index 73e6d1b..6c68c4b 100644 --- a/src/quickpicks/quickPicksItems.ts +++ b/src/quickpicks/quickPicksItems.ts @@ -1,10 +1,9 @@ 'use strict'; import { commands, QuickPickItem } from 'vscode'; -import { Commands } from '../commands'; +import { Commands, GitActions } from '../commands'; import { Container } from '../container'; import { GitReference, GitRevisionReference, GitStashCommit, SearchPattern } from '../git/git'; import { Keys } from '../keyboard'; -import { GlyphChars } from '../constants'; declare module 'vscode' { interface QuickPickItem { @@ -207,9 +206,7 @@ export class RevealInRepositoriesViewQuickPickItem extends CommandQuickPickItem private readonly reference: GitRevisionReference, item: QuickPickItem = { label: `$(eye) Reveal ${GitReference.isStash(reference) ? 'Stash' : 'Commit'}`, - description: `in Repositories view ${ - GitReference.isStash(reference) ? '' : `${GlyphChars.Dash} this can take a while` - }`, + description: GitReference.isStash(reference) ? '' : 'can take a while', }, ) { super(item, undefined, undefined); @@ -217,13 +214,13 @@ export class RevealInRepositoriesViewQuickPickItem extends CommandQuickPickItem async execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { if (GitStashCommit.is(this.reference)) { - void (await Container.repositoriesView.revealStash(this.reference, { + void (await GitActions.Stash.reveal(this.reference, { select: true, focus: !(options?.preserveFocus ?? false), expand: true, })); } else { - void (await Container.repositoriesView.revealCommit(this.reference, { + void (await GitActions.Commit.reveal(this.reference, { select: true, focus: !(options?.preserveFocus ?? false), expand: true, diff --git a/src/statusbar/statusBarController.ts b/src/statusbar/statusBarController.ts index 2de8e8f..32a1b33 100644 --- a/src/statusbar/statusBarController.ts +++ b/src/statusbar/statusBarController.ts @@ -152,10 +152,10 @@ export class StatusBarController implements Disposable { this._blameStatusBarItem.tooltip = 'Toggle Git CodeLens'; break; case StatusBarCommand.RevealCommitInView: - this._blameStatusBarItem.tooltip = 'Reveal Commit in Repositories View'; + this._blameStatusBarItem.tooltip = 'Reveal Commit in the Side Bar'; break; case StatusBarCommand.ShowCommitsInView: - this._blameStatusBarItem.tooltip = 'Show Commit in Repositories View'; + this._blameStatusBarItem.tooltip = 'Show Commit in Search Commits View'; break; case StatusBarCommand.ShowQuickCommitDetails: this._blameStatusBarItem.tooltip = 'Show Commit'; diff --git a/src/views/branchesView.ts b/src/views/branchesView.ts index d377bee..63a7a61 100644 --- a/src/views/branchesView.ts +++ b/src/views/branchesView.ts @@ -11,7 +11,6 @@ import { import { BranchesViewConfig, configuration, ViewBranchesLayout, ViewFilesLayout } from '../configuration'; import { Container } from '../container'; import { - GitBranch, GitBranchReference, GitLogCommit, GitReference, @@ -27,9 +26,6 @@ import { BranchOrTagFolderNode, ContextValues, MessageNode, - RemoteNode, - RemotesNode, - RepositoriesNode, RepositoryNode, SubscribeableViewNode, unknownGitUri, @@ -39,6 +35,7 @@ import { debug, gate } from '../system'; import { ViewBase } from './viewBase'; export class BranchesRepositoryNode extends SubscribeableViewNode { + protected splatted = true; private child: BranchesNode | undefined; constructor( @@ -46,32 +43,48 @@ export class BranchesRepositoryNode extends SubscribeableViewNode view: BranchesView, parent: ViewNode, public readonly repo: Repository, - private readonly root: boolean, + splatted: boolean, ) { super(uri, view, parent); + + this.splatted = splatted; + } + + get id(): string { + return RepositoryNode.getId(this.repo.path); } async getChildren(): Promise { + void this.ensureSubscription(); + if (this.child == null) { this.child = new BranchesNode(this.uri, this.view, this, this.repo); - - void this.ensureSubscription(); } + return this.child.getChildren(); } getTreeItem(): TreeItem { + this.splatted = false; + void this.ensureSubscription(); + const item = new TreeItem( this.repo.formattedName ?? this.uri.repoPath ?? '', TreeItemCollapsibleState.Expanded, ); item.contextValue = ContextValues.RepositoryFolder; - void this.ensureSubscription(); - return item; } + async getSplattedChild() { + if (this.child == null) { + await this.getChildren(); + } + + return this.child; + } + @gate() @debug() async refresh(reset: boolean = false) { @@ -105,14 +118,15 @@ export class BranchesRepositoryNode extends SubscribeableViewNode e.changed(RepositoryChange.Remotes) ) { void this.triggerChange(true); - if (this.root) { - void this.parent?.triggerChange(true); - } + // if (this.root) { + // void this.parent?.triggerChange(true); + // } } } } export class BranchesViewNode extends ViewNode { + protected splatted = true; private children: BranchesRepositoryNode[] | undefined; constructor(view: BranchesView) { @@ -120,22 +134,17 @@ export class BranchesViewNode extends ViewNode { } async getChildren(): Promise { - if (this.children != null) { - for (const child of this.children) { - child.dispose?.(); - } - this.children = undefined; + if (this.children == null) { + const repositories = await Container.git.getOrderedRepositories(); + if (repositories.length === 0) return [new MessageNode(this.view, this, 'No branches could be found.')]; + + const splat = repositories.length === 1; + this.children = repositories.map( + r => new BranchesRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, splat), + ); } - const repositories = await Container.git.getOrderedRepositories(); - if (repositories.length === 0) return [new MessageNode(this.view, this, 'No branches could be found.')]; - - const root = repositories.length === 1; - this.children = repositories.map( - r => new BranchesRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, root), - ); - - if (root) { + if (this.children.length === 1) { const [child] = this.children; const branches = await child.repo.getBranches({ filter: b => !b.remote }); @@ -143,6 +152,7 @@ export class BranchesViewNode extends ViewNode { return child.getChildren(); } + return this.children; } @@ -150,6 +160,25 @@ export class BranchesViewNode extends ViewNode { const item = new TreeItem('Branches', TreeItemCollapsibleState.Expanded); return item; } + + async getSplattedChild() { + if (this.children == null) { + await this.getChildren(); + } + + return this.children?.length === 1 ? this.children[0] : undefined; + } + + @gate() + @debug() + refresh(reset: boolean = false) { + if (reset && this.children != null) { + for (const child of this.children) { + child.dispose(); + } + this.children = undefined; + } + } } export class BranchesView extends ViewBase { @@ -218,45 +247,17 @@ export class BranchesView extends ViewBase } findBranch(branch: GitBranchReference, token?: CancellationToken) { - const repoNodeId = RepositoryNode.getId(branch.repoPath); + if (branch.remote) return undefined; - if (branch.remote) { - return this.findNode((n: any) => n.branch !== undefined && n.branch.ref === branch.ref, { - allowPaging: true, - maxDepth: 6, - canTraverse: n => { - // Only search for branch nodes in the same repo within BranchesNode - if (n instanceof RepositoriesNode) return true; - - if (n instanceof RemoteNode) { - if (!n.id.startsWith(repoNodeId)) return false; - - return branch.remote && n.remote.name === GitBranch.getRemote(branch.name); //branch.getRemoteName(); - } - - if ( - n instanceof RepositoryNode || - n instanceof BranchesNode || - n instanceof RemotesNode || - n instanceof BranchOrTagFolderNode - ) { - return n.id.startsWith(repoNodeId); - } - - return false; - }, - token: token, - }); - } + const repoNodeId = RepositoryNode.getId(branch.repoPath); - return this.findNode((n: any) => n.branch !== undefined && n.branch.ref === branch.ref, { + return this.findNode((n: any) => n.branch?.ref === branch.ref, { allowPaging: true, - maxDepth: 5, + maxDepth: 4, canTraverse: n => { - // Only search for branch nodes in the same repo within BranchesNode - if (n instanceof RepositoriesNode) return true; + if (n instanceof BranchesViewNode) return true; - if (n instanceof RepositoryNode || n instanceof BranchesNode || n instanceof BranchOrTagFolderNode) { + if (n instanceof BranchesRepositoryNode || n instanceof BranchOrTagFolderNode) { return n.id.startsWith(repoNodeId); } @@ -270,59 +271,22 @@ export class BranchesView extends ViewBase const repoNodeId = RepositoryNode.getId(commit.repoPath); // Get all the branches the commit is on - let branches = await Container.git.getCommitBranches(commit.repoPath, commit.ref); - if (branches.length !== 0) { - return this.findNode((n: any) => n.commit !== undefined && n.commit.ref === commit.ref, { - allowPaging: true, - maxDepth: 6, - canTraverse: async n => { - // Only search for commit nodes in the same repo within BranchNodes - if (n instanceof RepositoriesNode) return true; - - if (n instanceof BranchNode) { - if (n.id.startsWith(repoNodeId) && branches.includes(n.branch.name)) { - await n.showMore({ until: commit.ref }); - return true; - } - } - - if ( - n instanceof RepositoryNode || - n instanceof BranchesNode || - n instanceof BranchOrTagFolderNode - ) { - return n.id.startsWith(repoNodeId); - } - - return false; - }, - token: token, - }); - } - - // If we didn't find the commit on any local branches, check remote branches - branches = await Container.git.getCommitBranches(commit.repoPath, commit.ref, { remotes: true }); + const branches = await Container.git.getCommitBranches(commit.repoPath, commit.ref); if (branches.length === 0) return undefined; - const remotes = branches.map(b => b.split('/', 1)[0]); - - return this.findNode((n: any) => n.commit !== undefined && n.commit.ref === commit.ref, { + return this.findNode((n: any) => n.commit?.ref === commit.ref, { allowPaging: true, - maxDepth: 8, - canTraverse: n => { - // Only search for commit nodes in the same repo within BranchNodes - if (n instanceof RepositoriesNode) return true; - - if (n instanceof RemoteNode) { - return n.id.startsWith(repoNodeId) && remotes.includes(n.remote.name); - } + maxDepth: 5, + canTraverse: async n => { + if (n instanceof BranchesViewNode) return true; - if (n instanceof BranchNode) { - return n.id.startsWith(repoNodeId) && branches.includes(n.branch.name); + if (n instanceof BranchesRepositoryNode || n instanceof BranchOrTagFolderNode) { + return n.id.startsWith(repoNodeId); } - if (n instanceof RepositoryNode || n instanceof RemotesNode || n instanceof BranchOrTagFolderNode) { - return n.id.startsWith(repoNodeId); + if (n instanceof BranchNode && branches.includes(n.branch.name)) { + await n.showMore({ until: commit.ref }); + return true; } return false; @@ -343,12 +307,12 @@ export class BranchesView extends ViewBase return window.withProgress( { location: ProgressLocation.Notification, - title: `Revealing ${GitReference.toString(branch, { icon: false })} in the Repositories view...`, + title: `Revealing ${GitReference.toString(branch, { icon: false })} in the side bar...`, cancellable: true, }, async (progress, token) => { const node = await this.findBranch(branch, token); - if (node === undefined) return node; + if (node == null) return undefined; await this.ensureRevealNode(node, options); @@ -369,12 +333,12 @@ export class BranchesView extends ViewBase return window.withProgress( { location: ProgressLocation.Notification, - title: `Revealing ${GitReference.toString(commit, { icon: false })} in the Repositories view...`, + title: `Revealing ${GitReference.toString(commit, { icon: false })} in the side bar...`, cancellable: true, }, async (progress, token) => { const node = await this.findCommit(commit, token); - if (node === undefined) return node; + if (node == null) return undefined; await this.ensureRevealNode(node, options); diff --git a/src/views/commitsView.ts b/src/views/commitsView.ts index 730d5f0..ab25e38 100644 --- a/src/views/commitsView.ts +++ b/src/views/commitsView.ts @@ -21,15 +21,10 @@ import { } from '../git/git'; import { GitUri } from '../git/gitUri'; import { - BranchesNode, BranchNode, - BranchOrTagFolderNode, CompareBranchNode, ContextValues, MessageNode, - RemoteNode, - RemotesNode, - RepositoriesNode, RepositoryNode, SubscribeableViewNode, unknownGitUri, @@ -37,13 +32,20 @@ import { } from './nodes'; import { debug, gate } from '../system'; import { ViewBase } from './viewBase'; +import { BranchTrackingStatusNode } from './nodes/branchTrackingStatusNode'; export class CommitsRepositoryNode extends SubscribeableViewNode { - private children: (BranchNode | CompareBranchNode)[] | undefined; protected splatted = true; + private children: (BranchNode | CompareBranchNode)[] | undefined; - constructor(uri: GitUri, view: CommitsView, parent: ViewNode, public readonly repo: Repository) { + constructor(uri: GitUri, view: CommitsView, parent: ViewNode, public readonly repo: Repository, splatted: boolean) { super(uri, view, parent); + + this.splatted = splatted; + } + + get id(): string { + return RepositoryNode.getId(this.repo.path); } async getChildren(): Promise { @@ -92,6 +94,14 @@ export class CommitsRepositoryNode extends SubscribeableViewNode { return item; } + async getSplattedChild() { + if (this.children == null) { + await this.getChildren(); + } + + return this.children?.[0]; + } + @gate() @debug() async refresh(reset: boolean = false) { @@ -135,6 +145,7 @@ export class CommitsRepositoryNode extends SubscribeableViewNode { } export class CommitsViewNode extends ViewNode { + protected splatted = true; private children: CommitsRepositoryNode[] | undefined; constructor(view: CommitsView) { @@ -142,19 +153,15 @@ export class CommitsViewNode extends ViewNode { } async getChildren(): Promise { - if (this.children != null) { - for (const child of this.children) { - child.dispose(); - } - this.children = undefined; - } - - const repositories = await Container.git.getOrderedRepositories(); - if (repositories.length === 0) return [new MessageNode(this.view, this, 'No commits could be found.')]; + if (this.children == null) { + const repositories = await Container.git.getOrderedRepositories(); + if (repositories.length === 0) return [new MessageNode(this.view, this, 'No commits could be found.')]; - this.children = repositories.map( - r => new CommitsRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r), - ); + const splat = repositories.length === 1; + this.children = repositories.map( + r => new CommitsRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, splat), + ); + } if (this.children.length === 1) { const [child] = this.children; @@ -169,6 +176,7 @@ export class CommitsViewNode extends ViewNode { return child.getChildren(); } + return this.children; } @@ -176,6 +184,25 @@ export class CommitsViewNode extends ViewNode { const item = new TreeItem('Commits', TreeItemCollapsibleState.Expanded); return item; } + + async getSplattedChild() { + if (this.children == null) { + await this.getChildren(); + } + + return this.children?.length === 1 ? this.children[0] : undefined; + } + + @gate() + @debug() + refresh(reset: boolean = false) { + if (reset && this.children != null) { + for (const child of this.children) { + child.dispose(); + } + this.children = undefined; + } + } } export class CommitsView extends ViewBase { @@ -235,59 +262,40 @@ export class CommitsView extends ViewBase { async findCommit(commit: GitLogCommit | { repoPath: string; ref: string }, token?: CancellationToken) { const repoNodeId = RepositoryNode.getId(commit.repoPath); - // Get all the branches the commit is on - let branches = await Container.git.getCommitBranches(commit.repoPath, commit.ref); - if (branches.length !== 0) { - return this.findNode((n: any) => n.commit !== undefined && n.commit.ref === commit.ref, { - allowPaging: true, - maxDepth: 6, - canTraverse: async n => { - // Only search for commit nodes in the same repo within BranchNodes - if (n instanceof RepositoriesNode) return true; - - if (n instanceof BranchNode) { - if (n.id.startsWith(repoNodeId) && branches.includes(n.branch.name)) { - await n.showMore({ until: commit.ref }); - return true; - } - } - - if ( - n instanceof RepositoryNode || - n instanceof BranchesNode || - n instanceof BranchOrTagFolderNode - ) { - return n.id.startsWith(repoNodeId); - } - - return false; - }, - token: token, - }); - } - - // If we didn't find the commit on any local branches, check remote branches - branches = await Container.git.getCommitBranches(commit.repoPath, commit.ref, { remotes: true }); - if (branches.length === 0) return undefined; + const branch = await Container.git.getBranch(commit.repoPath); + if (branch == null) return undefined; - const remotes = branches.map(b => b.split('/', 1)[0]); + // Get all the branches the commit is on + const branches = await Container.git.getCommitBranches(commit.repoPath, commit.ref); + if (branches.length === 0 || !branches.includes(branch.name)) return undefined; - return this.findNode((n: any) => n.commit !== undefined && n.commit.ref === commit.ref, { + return this.findNode((n: any) => n.commit?.ref === commit.ref, { allowPaging: true, - maxDepth: 8, - canTraverse: n => { - // Only search for commit nodes in the same repo within BranchNodes - if (n instanceof RepositoriesNode) return true; + maxDepth: 2, + canTraverse: async n => { + if (n instanceof CommitsViewNode) { + let node: ViewNode | undefined = await n.getSplattedChild?.(); + if (node instanceof CommitsRepositoryNode) { + node = await node.getSplattedChild?.(); + if (node instanceof BranchNode) { + await node.showMore({ until: commit.ref }); + } + } - if (n instanceof RemoteNode) { - return n.id.startsWith(repoNodeId) && remotes.includes(n.remote.name); + return true; } - if (n instanceof BranchNode) { - return n.id.startsWith(repoNodeId) && branches.includes(n.branch.name); + if (n instanceof CommitsRepositoryNode) { + if (n.id.startsWith(repoNodeId)) { + const node = await n.getSplattedChild?.(); + if (node instanceof BranchNode) { + await node.showMore({ until: commit.ref }); + return true; + } + } } - if (n instanceof RepositoryNode || n instanceof RemotesNode || n instanceof BranchOrTagFolderNode) { + if (n instanceof BranchTrackingStatusNode) { return n.id.startsWith(repoNodeId); } @@ -309,12 +317,12 @@ export class CommitsView extends ViewBase { return window.withProgress( { location: ProgressLocation.Notification, - title: `Revealing ${GitReference.toString(commit, { icon: false })} in the Repositories view...`, + title: `Revealing ${GitReference.toString(commit, { icon: false })} in the side bar...`, cancellable: true, }, async (progress, token) => { const node = await this.findCommit(commit, token); - if (node === undefined) return node; + if (node == null) return undefined; await this.ensureRevealNode(node, options); diff --git a/src/views/contributorsView.ts b/src/views/contributorsView.ts index 4b15e1b..cb3596a 100644 --- a/src/views/contributorsView.ts +++ b/src/views/contributorsView.ts @@ -4,11 +4,20 @@ import { configuration, ContributorsViewConfig, ViewFilesLayout } from '../confi import { Container } from '../container'; import { Repository, RepositoryChange, RepositoryChangeEvent } from '../git/git'; import { GitUri } from '../git/gitUri'; -import { ContextValues, ContributorsNode, MessageNode, SubscribeableViewNode, unknownGitUri, ViewNode } from './nodes'; +import { + ContextValues, + ContributorsNode, + MessageNode, + RepositoryNode, + SubscribeableViewNode, + unknownGitUri, + ViewNode, +} from './nodes'; import { debug, gate } from '../system'; import { ViewBase } from './viewBase'; export class ContributorsRepositoryNode extends SubscribeableViewNode { + protected splatted = true; private child: ContributorsNode | undefined; constructor( @@ -16,32 +25,47 @@ export class ContributorsRepositoryNode extends SubscribeableViewNode { + void this.ensureSubscription(); + if (this.child == null) { this.child = new ContributorsNode(this.uri, this.view, this, this.repo); - - void this.ensureSubscription(); } + return this.child.getChildren(); } getTreeItem(): TreeItem { + void this.ensureSubscription(); + const item = new TreeItem( this.repo.formattedName ?? this.uri.repoPath ?? '', TreeItemCollapsibleState.Expanded, ); item.contextValue = ContextValues.RepositoryFolder; - void this.ensureSubscription(); - return item; } + async getSplattedChild() { + if (this.child == null) { + await this.getChildren(); + } + + return this.child; + } + @gate() @debug() async refresh(reset: boolean = false) { @@ -71,14 +95,15 @@ export class ContributorsRepositoryNode extends SubscribeableViewNode { + protected splatted = true; private children: ContributorsRepositoryNode[] | undefined; constructor(view: ContributorsView) { @@ -86,22 +111,17 @@ export class ContributorsViewNode extends ViewNode { } async getChildren(): Promise { - if (this.children != null) { - for (const child of this.children) { - child.dispose?.(); - } - this.children = undefined; + if (this.children == null) { + const repositories = await Container.git.getOrderedRepositories(); + if (repositories.length === 0) return [new MessageNode(this.view, this, 'No contributors could be found.')]; + + const splat = repositories.length === 1; + this.children = repositories.map( + r => new ContributorsRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, splat), + ); } - const repositories = await Container.git.getOrderedRepositories(); - if (repositories.length === 0) return [new MessageNode(this.view, this, 'No contributors could be found.')]; - - const root = repositories.length === 1; - this.children = repositories.map( - r => new ContributorsRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, root), - ); - - if (root) { + if (this.children.length === 1) { const [child] = this.children; const contributors = await child.repo.getContributors(); @@ -109,6 +129,7 @@ export class ContributorsViewNode extends ViewNode { return child.getChildren(); } + return this.children; } @@ -116,6 +137,25 @@ export class ContributorsViewNode extends ViewNode { const item = new TreeItem('Contributors', TreeItemCollapsibleState.Expanded); return item; } + + async getSplattedChild() { + if (this.children == null) { + await this.getChildren(); + } + + return this.children?.length === 1 ? this.children[0] : undefined; + } + + @gate() + @debug() + refresh(reset: boolean = false) { + if (reset && this.children != null) { + for (const child of this.children) { + child.dispose(); + } + this.children = undefined; + } + } } export class ContributorsView extends ViewBase { diff --git a/src/views/nodes/branchNode.ts b/src/views/nodes/branchNode.ts index 048abb9..2857282 100644 --- a/src/views/nodes/branchNode.ts +++ b/src/views/nodes/branchNode.ts @@ -96,7 +96,7 @@ export class BranchNode } async getChildren(): Promise { - if (this._children === undefined) { + if (this._children == null) { const children = []; if (this.options.showTracking && this.branch.tracking) { const status = { @@ -120,7 +120,7 @@ export class BranchNode } const log = await this.getLog(); - if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')]; + if (log == null) return [new MessageNode(this.view, this, 'No commits could be found.')]; const getBranchAndTagTips = await Container.git.getBranchesAndTagsTipsFn( this.uri.repoPath, @@ -267,7 +267,7 @@ export class BranchNode private _log: GitLog | undefined; private async getLog() { - if (this._log === undefined) { + if (this._log == null) { this._log = await Container.git.getLog(this.uri.repoPath!, { limit: this.limit ?? this.view.config.defaultItemLimit, ref: this.ref.ref, @@ -284,13 +284,15 @@ export class BranchNode limit: number | undefined = this.view.getNodeLastKnownLimit(this); async showMore(limit?: number | { until?: any }) { let log = await this.getLog(); - if (log === undefined || !log.hasMore) return; + if (log == null || !log.hasMore) return; log = await log.more?.(limit ?? this.view.config.pageItemLimit); if (this._log === log) return; this._log = log; this.limit = log?.count; + + this._children = undefined; void this.triggerChange(false); } } diff --git a/src/views/nodes/branchTrackingStatusNode.ts b/src/views/nodes/branchTrackingStatusNode.ts index 923931c..2b58ef2 100644 --- a/src/views/nodes/branchTrackingStatusNode.ts +++ b/src/views/nodes/branchTrackingStatusNode.ts @@ -62,16 +62,16 @@ export class BranchTrackingStatusNode extends ViewNode implement async getChildren(): Promise { const log = await this.getLog(); - if (log === undefined) return []; + if (log == null) return []; let children; if (this.ahead) { // Since the last commit when we are looking 'ahead' can have no previous (because of the range given) -- look it up const commits = [...log.commits.values()]; const commit = commits[commits.length - 1]; - if (commit.previousSha === undefined) { + if (commit.previousSha == null) { const previousLog = await Container.git.getLog(this.uri.repoPath!, { limit: 2, ref: commit.sha }); - if (previousLog !== undefined) { + if (previousLog != null) { commits[commits.length - 1] = Iterables.first(previousLog.commits.values()); } } @@ -154,7 +154,7 @@ export class BranchTrackingStatusNode extends ViewNode implement private _log: GitLog | undefined; private async getLog() { - if (this._log === undefined) { + if (this._log == null) { const range = this.ahead ? GitRevision.createRange(this.status.upstream, this.status.ref) : GitRevision.createRange(this.status.ref, this.status.upstream); @@ -175,13 +175,14 @@ export class BranchTrackingStatusNode extends ViewNode implement limit: number | undefined = this.view.getNodeLastKnownLimit(this); async showMore(limit?: number | { until?: any }) { let log = await this.getLog(); - if (log === undefined || !log.hasMore) return; + if (log == null || !log.hasMore) return; log = await log.more?.(limit ?? this.view.config.pageItemLimit); if (this._log === log) return; this._log = log; this.limit = log?.count; + void this.triggerChange(false); } } diff --git a/src/views/nodes/branchesNode.ts b/src/views/nodes/branchesNode.ts index 3e32c20..ab5d6e2 100644 --- a/src/views/nodes/branchesNode.ts +++ b/src/views/nodes/branchesNode.ts @@ -35,7 +35,7 @@ export class BranchesNode extends ViewNode { } async getChildren(): Promise { - if (this._children === undefined) { + if (this._children == null) { const branches = await this.repo.getBranches({ // only show local branches filter: b => !b.remote, @@ -71,6 +71,7 @@ export class BranchesNode extends ViewNode { ); this._children = root.getChildren(); } + return this._children; } diff --git a/src/views/nodes/contributorNode.ts b/src/views/nodes/contributorNode.ts index 52c86fb..41c8652 100644 --- a/src/views/nodes/contributorNode.ts +++ b/src/views/nodes/contributorNode.ts @@ -40,7 +40,7 @@ export class ContributorNode extends ViewNode { const log = await this.getLog(); - if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')]; + if (log == null) return [new MessageNode(this.view, this, 'No commits could be found.')]; const getBranchAndTagTips = await Container.git.getBranchesAndTagsTipsFn(this.uri.repoPath); const children = [ @@ -96,7 +96,7 @@ export class ContributorNode extends ViewNode$`], @@ -113,13 +113,14 @@ export class ContributorNode extends ViewNode { @@ -17,6 +17,8 @@ export class ContributorsNode extends ViewNode { - const contributors = await this.repo.getContributors(); - if (contributors.length === 0) return [new MessageNode(this.view, this, 'No contributors could be found.')]; + if (this._children == null) { + const contributors = await this.repo.getContributors(); + if (contributors.length === 0) return [new MessageNode(this.view, this, 'No contributors could be found.')]; + + GitContributor.sort(contributors); + const presenceMap = await this.maybeGetPresenceMap(contributors).catch(() => undefined); - GitContributor.sort(contributors); - const presenceMap = await this.maybeGetPresenceMap(contributors).catch(() => undefined); + this._children = contributors.map(c => new ContributorNode(this.uri, this.view, this, c, presenceMap)); + } - const children = contributors.map(c => new ContributorNode(this.uri, this.view, this, c, presenceMap)); - return children; + return this._children; } getTreeItem(): TreeItem { @@ -49,6 +54,12 @@ export class ContributorsNode extends ViewNode { static key = ':remotes'; @@ -16,6 +17,8 @@ export class RemotesNode extends ViewNode { return `${RepositoryNode.getId(repoPath)}${this.key}`; } + private _children: ViewNode[] | undefined; + constructor(uri: GitUri, view: RemotesView | RepositoriesView, parent: ViewNode, public readonly repo: Repository) { super(uri, view, parent); } @@ -25,12 +28,16 @@ export class RemotesNode extends ViewNode { } async getChildren(): Promise { - const remotes = await this.repo.getRemotes({ sort: true }); - if (remotes === undefined || remotes.length === 0) { - return [new MessageNode(this.view, this, 'No remotes could be found')]; + if (this._children == null) { + const remotes = await this.repo.getRemotes({ sort: true }); + if (remotes == null || remotes.length === 0) { + return [new MessageNode(this.view, this, 'No remotes could be found')]; + } + + this._children = remotes.map(r => new RemoteNode(this.uri, this.view, this, r, this.repo)); } - return remotes.map(r => new RemoteNode(this.uri, this.view, this, r, this.repo)); + return this._children; } getTreeItem(): TreeItem { @@ -45,4 +52,10 @@ export class RemotesNode extends ViewNode { return item; } + + @gate() + @debug() + refresh() { + this._children = undefined; + } } diff --git a/src/views/nodes/stashesNode.ts b/src/views/nodes/stashesNode.ts index 3e678f7..9729051 100644 --- a/src/views/nodes/stashesNode.ts +++ b/src/views/nodes/stashesNode.ts @@ -8,7 +8,7 @@ import { RepositoriesView } from '../repositoriesView'; import { RepositoryNode } from './repositoryNode'; import { StashesView } from '../stashesView'; import { StashNode } from './stashNode'; -import { Iterables } from '../../system'; +import { debug, gate, Iterables } from '../../system'; import { ContextValues, ViewNode } from './viewNode'; export class StashesNode extends ViewNode { @@ -17,6 +17,8 @@ export class StashesNode extends ViewNode { return `${RepositoryNode.getId(repoPath)}${this.key}`; } + private _children: ViewNode[] | undefined; + constructor(uri: GitUri, view: StashesView | RepositoriesView, parent: ViewNode, public readonly repo: Repository) { super(uri, view, parent); } @@ -26,10 +28,14 @@ export class StashesNode extends ViewNode { } async getChildren(): Promise { - const stash = await this.repo.getStash(); - if (stash === undefined) return [new MessageNode(this.view, this, 'No stashes could be found.')]; + if (this._children == null) { + const stash = await this.repo.getStash(); + if (stash === undefined) return [new MessageNode(this.view, this, 'No stashes could be found.')]; + + this._children = [...Iterables.map(stash.commits.values(), c => new StashNode(this.view, this, c))]; + } - return [...Iterables.map(stash.commits.values(), c => new StashNode(this.view, this, c))]; + return this._children; } getTreeItem(): TreeItem { @@ -44,4 +50,10 @@ export class StashesNode extends ViewNode { return item; } + + @gate() + @debug() + refresh() { + this._children = undefined; + } } diff --git a/src/views/nodes/tagNode.ts b/src/views/nodes/tagNode.ts index 8fcbb8a..d5426c1 100644 --- a/src/views/nodes/tagNode.ts +++ b/src/views/nodes/tagNode.ts @@ -43,7 +43,7 @@ export class TagNode extends ViewRefNode { const log = await this.getLog(); - if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')]; + if (log == null) return [new MessageNode(this.view, this, 'No commits could be found.')]; const getBranchAndTagTips = await Container.git.getBranchesAndTagsTipsFn(this.uri.repoPath, this.tag.name); const children = [ @@ -94,7 +94,7 @@ export class TagNode extends ViewRefNode { return `${RepositoryNode.getId(repoPath)}${this.key}`; } + private _children: ViewNode[] | undefined; + constructor(uri: GitUri, view: TagsView | RepositoriesView, parent: ViewNode, public readonly repo: Repository) { super(uri, view, parent); } @@ -27,31 +29,36 @@ export class TagsNode extends ViewNode { } async getChildren(): Promise { - const tags = await this.repo.getTags({ sort: true }); - if (tags.length === 0) return [new MessageNode(this.view, this, 'No tags could be found.')]; + if (this._children == null) { + const tags = await this.repo.getTags({ sort: true }); + if (tags.length === 0) return [new MessageNode(this.view, this, 'No tags could be found.')]; + + const tagNodes = tags.map( + t => new TagNode(GitUri.fromRepoPath(this.uri.repoPath!, t.ref), this.view, this, t), + ); + if (this.view.config.branches.layout === ViewBranchesLayout.List) return tagNodes; - const tagNodes = tags.map(t => new TagNode(GitUri.fromRepoPath(this.uri.repoPath!, t.ref), this.view, this, t)); - if (this.view.config.branches.layout === ViewBranchesLayout.List) return tagNodes; + const hierarchy = Arrays.makeHierarchical( + tagNodes, + n => n.tag.name.split('/'), + (...paths) => paths.join('/'), + this.view.config.files.compact, + ); - const hierarchy = Arrays.makeHierarchical( - tagNodes, - n => n.tag.name.split('/'), - (...paths) => paths.join('/'), - this.view.config.files.compact, - ); + const root = new BranchOrTagFolderNode( + this.view, + this, + 'tag', + this.repo.path, + '', + undefined, + hierarchy, + 'tags', + ); + this._children = root.getChildren(); + } - const root = new BranchOrTagFolderNode( - this.view, - this, - 'tag', - this.repo.path, - '', - undefined, - hierarchy, - 'tags', - ); - const children = root.getChildren(); - return children; + return this._children; } getTreeItem(): TreeItem { @@ -61,4 +68,10 @@ export class TagsNode extends ViewNode { item.iconPath = new ThemeIcon('tag'); return item; } + + @gate() + @debug() + refresh() { + this._children = undefined; + } } diff --git a/src/views/nodes/viewNode.ts b/src/views/nodes/viewNode.ts index bc09e77..66d7bc9 100644 --- a/src/views/nodes/viewNode.ts +++ b/src/views/nodes/viewNode.ts @@ -84,12 +84,8 @@ export abstract class ViewNode { abstract getChildren(): ViewNode[] | Promise; getParent(): ViewNode | undefined { - // If this node has been splatted (e.g. not shown itself, but its children are), then return its grandparent - if (this.splatted) { - return this.parent?.getParent() ?? this.parent; - } - - return this.parent; + // If this node's parent has been splatted (e.g. not shown itself, but its children are), then return its grandparent + return this.parent?.splatted ? this.parent?.getParent() : this.parent; } abstract getTreeItem(): TreeItem | Promise; @@ -110,6 +106,8 @@ export abstract class ViewNode { return this.view.refreshNode(this, reset, force); } + + getSplattedChild?(): Promise; } export abstract class ViewRefNode< diff --git a/src/views/remotesView.ts b/src/views/remotesView.ts index 18b66c8..43c9854 100644 --- a/src/views/remotesView.ts +++ b/src/views/remotesView.ts @@ -13,20 +13,22 @@ import { Container } from '../container'; import { GitBranch, GitBranchReference, + GitLogCommit, GitReference, + GitRemote, + GitRevisionReference, Repository, RepositoryChange, RepositoryChangeEvent, } from '../git/git'; import { GitUri } from '../git/gitUri'; import { - BranchesNode, + BranchNode, BranchOrTagFolderNode, ContextValues, MessageNode, RemoteNode, RemotesNode, - RepositoriesNode, RepositoryNode, SubscribeableViewNode, unknownGitUri, @@ -36,39 +38,50 @@ import { debug, gate } from '../system'; import { ViewBase } from './viewBase'; export class RemotesRepositoryNode extends SubscribeableViewNode { + protected splatted = true; private child: RemotesNode | undefined; - constructor( - uri: GitUri, - view: RemotesView, - parent: ViewNode, - public readonly repo: Repository, - private readonly root: boolean, - ) { + constructor(uri: GitUri, view: RemotesView, parent: ViewNode, public readonly repo: Repository, splatted: boolean) { super(uri, view, parent); + + this.splatted = splatted; + } + + get id(): string { + return RepositoryNode.getId(this.repo.path); } async getChildren(): Promise { + void this.ensureSubscription(); + if (this.child == null) { this.child = new RemotesNode(this.uri, this.view, this, this.repo); - - void this.ensureSubscription(); } + return this.child.getChildren(); } getTreeItem(): TreeItem { + this.splatted = false; + void this.ensureSubscription(); + const item = new TreeItem( this.repo.formattedName ?? this.uri.repoPath ?? '', TreeItemCollapsibleState.Expanded, ); item.contextValue = ContextValues.RepositoryFolder; - void this.ensureSubscription(); - return item; } + async getSplattedChild() { + if (this.child == null) { + await this.getChildren(); + } + + return this.child; + } + @gate() @debug() async refresh(reset: boolean = false) { @@ -98,14 +111,15 @@ export class RemotesRepositoryNode extends SubscribeableViewNode { if (e.changed(RepositoryChange.Config) || e.changed(RepositoryChange.Remotes)) { void this.triggerChange(true); - if (this.root) { - void this.parent?.triggerChange(true); - } + // if (this.root) { + // void this.parent?.triggerChange(true); + // } } } } export class RemotesViewNode extends ViewNode { + protected splatted = true; private children: RemotesRepositoryNode[] | undefined; constructor(view: RemotesView) { @@ -113,22 +127,17 @@ export class RemotesViewNode extends ViewNode { } async getChildren(): Promise { - if (this.children != null) { - for (const child of this.children) { - child.dispose?.(); - } - this.children = undefined; + if (this.children == null) { + const repositories = await Container.git.getOrderedRepositories(); + if (repositories.length === 0) return [new MessageNode(this.view, this, 'No remotes could be found.')]; + + const splat = repositories.length === 1; + this.children = repositories.map( + r => new RemotesRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, splat), + ); } - const repositories = await Container.git.getOrderedRepositories(); - if (repositories.length === 0) return [new MessageNode(this.view, this, 'No remotes could be found.')]; - - const root = repositories.length === 1; - this.children = repositories.map( - r => new RemotesRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, root), - ); - - if (root) { + if (this.children.length === 1) { const [child] = this.children; const remotes = await child.repo.getRemotes(); @@ -136,6 +145,7 @@ export class RemotesViewNode extends ViewNode { return child.getChildren(); } + return this.children; } @@ -143,6 +153,25 @@ export class RemotesViewNode extends ViewNode { const item = new TreeItem('Remotes', TreeItemCollapsibleState.Expanded); return item; } + + async getSplattedChild() { + if (this.children == null) { + await this.getChildren(); + } + + return this.children?.length === 1 ? this.children[0] : undefined; + } + + @gate() + @debug() + refresh(reset: boolean = false) { + if (reset && this.children != null) { + for (const child of this.children) { + child.dispose(); + } + this.children = undefined; + } + } } export class RemotesView extends ViewBase { @@ -215,25 +244,75 @@ export class RemotesView extends ViewBase { const repoNodeId = RepositoryNode.getId(branch.repoPath); - return this.findNode((n: any) => n.branch !== undefined && n.branch.ref === branch.ref, { + return this.findNode((n: any) => n.branch?.ref === branch.ref, { allowPaging: true, - maxDepth: 6, + maxDepth: 5, canTraverse: n => { - // Only search for branch nodes in the same repo within BranchesNode - if (n instanceof RepositoriesNode) return true; + if (n instanceof RemotesViewNode) return true; + + if (n instanceof RemotesRepositoryNode || n instanceof BranchOrTagFolderNode) { + return n.id.startsWith(repoNodeId); + } if (n instanceof RemoteNode) { if (!n.id.startsWith(repoNodeId)) return false; - return branch.remote && n.remote.name === GitBranch.getRemote(branch.name); //branch.getRemoteName(); + return n.remote.name === GitBranch.getRemote(branch.name); + } + + return false; + }, + token: token, + }); + } + + async findCommit(commit: GitLogCommit | { repoPath: string; ref: string }, token?: CancellationToken) { + const repoNodeId = RepositoryNode.getId(commit.repoPath); + + // Get all the remote branches the commit is on + const branches = await Container.git.getCommitBranches(commit.repoPath, commit.ref, { remotes: true }); + if (branches.length === 0) return undefined; + + const remotes = branches.map(b => b.split('/', 1)[0]); + + return this.findNode((n: any) => n.commit !== undefined && n.commit.ref === commit.ref, { + allowPaging: true, + maxDepth: 6, + canTraverse: n => { + if (n instanceof RemotesViewNode) return true; + + if (n instanceof RemotesRepositoryNode || n instanceof BranchOrTagFolderNode) { + return n.id.startsWith(repoNodeId); + } + + if (n instanceof RemoteNode) { + return n.id.startsWith(repoNodeId) && remotes.includes(n.remote.name); + } + + if (n instanceof BranchNode) { + return n.id.startsWith(repoNodeId) && branches.includes(n.branch.name); + } + + if (n instanceof RepositoryNode || n instanceof RemotesNode || n instanceof BranchOrTagFolderNode) { + return n.id.startsWith(repoNodeId); } - if ( - n instanceof RepositoryNode || - n instanceof BranchesNode || - n instanceof RemotesNode || - n instanceof BranchOrTagFolderNode - ) { + return false; + }, + token: token, + }); + } + + findRemote(remote: GitRemote, token?: CancellationToken) { + const repoNodeId = RepositoryNode.getId(remote.repoPath); + + return this.findNode((n: any) => n.remote?.name === remote.name, { + allowPaging: true, + maxDepth: 2, + canTraverse: n => { + if (n instanceof RemotesViewNode) return true; + + if (n instanceof RemotesRepositoryNode) { return n.id.startsWith(repoNodeId); } @@ -255,12 +334,12 @@ export class RemotesView extends ViewBase { return window.withProgress( { location: ProgressLocation.Notification, - title: `Revealing ${GitReference.toString(branch, { icon: false })} in the Repositories view...`, + title: `Revealing ${GitReference.toString(branch, { icon: false })} in the side bar...`, cancellable: true, }, async (progress, token) => { const node = await this.findBranch(branch, token); - if (node === undefined) return node; + if (node == null) return undefined; await this.ensureRevealNode(node, options); @@ -268,6 +347,59 @@ export class RemotesView extends ViewBase { }, ); } + + @gate(() => '') + async revealCommit( + commit: GitRevisionReference, + options?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }, + ) { + return window.withProgress( + { + location: ProgressLocation.Notification, + title: `Revealing ${GitReference.toString(commit, { icon: false })} in the side bar...`, + cancellable: true, + }, + async (progress, token) => { + const node = await this.findCommit(commit, token); + if (node == null) return undefined; + + await this.ensureRevealNode(node, options); + + return node; + }, + ); + } + + @gate(() => '') + revealRemote( + remote: GitRemote, + options?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }, + ) { + return window.withProgress( + { + location: ProgressLocation.Notification, + title: `Revealing remote ${remote.name} in the side bar...`, + cancellable: true, + }, + async (progress, token) => { + const node = await this.findRemote(remote, token); + if (node == null) return undefined; + + await this.ensureRevealNode(node, options); + + return node; + }, + ); + } + private setLayout(layout: ViewBranchesLayout) { return configuration.updateEffective('views', this.configKey, 'branches', 'layout', layout); } diff --git a/src/views/repositoriesView.ts b/src/views/repositoriesView.ts index fd6c65c..cf1587c 100644 --- a/src/views/repositoriesView.ts +++ b/src/views/repositoriesView.ts @@ -300,7 +300,7 @@ export class RepositoriesView extends ViewBase { const node = await this.findBranch(branch, token); - if (node === undefined) return node; + if (node == null) return undefined; await this.ensureRevealNode(node, options); @@ -358,7 +358,7 @@ export class RepositoriesView extends ViewBase { const node = await this.findCommit(commit, token); - if (node === undefined) return node; + if (node == null) return undefined; await this.ensureRevealNode(node, options); @@ -474,7 +474,7 @@ export class RepositoriesView extends ViewBase { const node = await this.findTag(tag, token); - if (node === undefined) return node; + if (node == null) return undefined; await this.ensureRevealNode(node, options); diff --git a/src/views/stashesView.ts b/src/views/stashesView.ts index 77da89c..cb92583 100644 --- a/src/views/stashesView.ts +++ b/src/views/stashesView.ts @@ -1,47 +1,75 @@ 'use strict'; -import { commands, ConfigurationChangeEvent, TreeItem, TreeItemCollapsibleState } from 'vscode'; +import { + CancellationToken, + commands, + ConfigurationChangeEvent, + ProgressLocation, + TreeItem, + TreeItemCollapsibleState, + window, +} from 'vscode'; import { configuration, StashesViewConfig, ViewFilesLayout } from '../configuration'; import { Container } from '../container'; -import { Repository, RepositoryChange, RepositoryChangeEvent } from '../git/git'; +import { GitReference, GitStashReference, Repository, RepositoryChange, RepositoryChangeEvent } from '../git/git'; import { GitUri } from '../git/gitUri'; -import { ContextValues, MessageNode, StashesNode, SubscribeableViewNode, unknownGitUri, ViewNode } from './nodes'; +import { + ContextValues, + MessageNode, + RepositoryNode, + StashesNode, + StashNode, + SubscribeableViewNode, + unknownGitUri, + ViewNode, +} from './nodes'; import { debug, gate } from '../system'; import { ViewBase } from './viewBase'; export class StashesRepositoryNode extends SubscribeableViewNode { + protected splatted = true; private child: StashesNode | undefined; - constructor( - uri: GitUri, - view: StashesView, - parent: ViewNode, - public readonly repo: Repository, - private readonly root: boolean, - ) { + constructor(uri: GitUri, view: StashesView, parent: ViewNode, public readonly repo: Repository, splatted: boolean) { super(uri, view, parent); + + this.splatted = splatted; + } + + get id(): string { + return RepositoryNode.getId(this.repo.path); } async getChildren(): Promise { + void this.ensureSubscription(); + if (this.child == null) { this.child = new StashesNode(this.uri, this.view, this, this.repo); - - void this.ensureSubscription(); } + return this.child.getChildren(); } getTreeItem(): TreeItem { + this.splatted = false; + void this.ensureSubscription(); + const item = new TreeItem( this.repo.formattedName ?? this.uri.repoPath ?? '', TreeItemCollapsibleState.Expanded, ); item.contextValue = ContextValues.RepositoryFolder; - void this.ensureSubscription(); - return item; } + async getSplattedChild() { + if (this.child == null) { + await this.getChildren(); + } + + return this.child; + } + @gate() @debug() async refresh(reset: boolean = false) { @@ -71,14 +99,15 @@ export class StashesRepositoryNode extends SubscribeableViewNode { if (e.changed(RepositoryChange.Config) || e.changed(RepositoryChange.Heads)) { void this.triggerChange(true); - if (this.root) { - void this.parent?.triggerChange(true); - } + // if (this.root) { + // void this.parent?.triggerChange(true); + // } } } } export class StashesViewNode extends ViewNode { + protected splatted = true; private children: StashesRepositoryNode[] | undefined; constructor(view: StashesView) { @@ -86,22 +115,17 @@ export class StashesViewNode extends ViewNode { } async getChildren(): Promise { - if (this.children != null) { - for (const child of this.children) { - child.dispose?.(); - } - this.children = undefined; - } + if (this.children == null) { + const repositories = await Container.git.getOrderedRepositories(); + if (repositories.length === 0) return [new MessageNode(this.view, this, 'No stashes could be found.')]; - const repositories = await Container.git.getOrderedRepositories(); - if (repositories.length === 0) return [new MessageNode(this.view, this, 'No stashes could be found.')]; - - const root = repositories.length === 1; - this.children = repositories.map( - r => new StashesRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, root), - ); + const splat = repositories.length === 1; + this.children = repositories.map( + r => new StashesRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, splat), + ); + } - if (root) { + if (this.children.length === 1) { const [child] = this.children; const stash = await child.repo.getStash(); @@ -109,6 +133,7 @@ export class StashesViewNode extends ViewNode { return child.getChildren(); } + return this.children; } @@ -116,6 +141,25 @@ export class StashesViewNode extends ViewNode { const item = new TreeItem('Stashes', TreeItemCollapsibleState.Expanded); return item; } + + async getSplattedChild() { + if (this.children == null) { + await this.getChildren(); + } + + return this.children?.length === 1 ? this.children[0] : undefined; + } + + @gate() + @debug() + refresh(reset: boolean = false) { + if (reset && this.children != null) { + for (const child of this.children) { + child.dispose(); + } + this.children = undefined; + } + } } export class StashesView extends ViewBase { @@ -172,6 +216,50 @@ export class StashesView extends ViewBase { return true; } + findStash(stash: GitStashReference, token?: CancellationToken) { + const repoNodeId = RepositoryNode.getId(stash.repoPath); + + return this.findNode(StashNode.getId(stash.repoPath, stash.ref), { + maxDepth: 2, + canTraverse: n => { + if (n instanceof StashesViewNode) return true; + + if (n instanceof StashesRepositoryNode) { + return n.id.startsWith(repoNodeId); + } + + return false; + }, + token: token, + }); + } + + @gate(() => '') + async revealStash( + stash: GitStashReference, + options?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }, + ) { + return window.withProgress( + { + location: ProgressLocation.Notification, + title: `Revealing ${GitReference.toString(stash, { icon: false })} in the side bar...`, + cancellable: true, + }, + async (progress, token) => { + const node = await this.findStash(stash, token); + if (node == null) return undefined; + + await this.ensureRevealNode(node, options); + + return node; + }, + ); + } + private setFilesLayout(layout: ViewFilesLayout) { return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); } diff --git a/src/views/tagsView.ts b/src/views/tagsView.ts index 9bd37c0..d2eb4c3 100644 --- a/src/views/tagsView.ts +++ b/src/views/tagsView.ts @@ -13,10 +13,8 @@ import { Container } from '../container'; import { GitReference, GitTagReference, Repository, RepositoryChange, RepositoryChangeEvent } from '../git/git'; import { GitUri } from '../git/gitUri'; import { - BranchOrTagFolderNode, ContextValues, MessageNode, - RepositoriesNode, RepositoryNode, SubscribeableViewNode, TagsNode, @@ -25,41 +23,53 @@ import { } from './nodes'; import { debug, gate } from '../system'; import { ViewBase } from './viewBase'; +import { BranchOrTagFolderNode } from './nodes/branchOrTagFolderNode'; export class TagsRepositoryNode extends SubscribeableViewNode { + protected splatted = true; private child: TagsNode | undefined; - constructor( - uri: GitUri, - view: TagsView, - parent: ViewNode, - public readonly repo: Repository, - private readonly root: boolean, - ) { + constructor(uri: GitUri, view: TagsView, parent: ViewNode, public readonly repo: Repository, splatted: boolean) { super(uri, view, parent); + + this.splatted = splatted; + } + + get id(): string { + return RepositoryNode.getId(this.repo.path); } async getChildren(): Promise { + void this.ensureSubscription(); + if (this.child == null) { this.child = new TagsNode(this.uri, this.view, this, this.repo); - - void this.ensureSubscription(); } + return this.child.getChildren(); } getTreeItem(): TreeItem { + this.splatted = false; + void this.ensureSubscription(); + const item = new TreeItem( this.repo.formattedName ?? this.uri.repoPath ?? '', TreeItemCollapsibleState.Expanded, ); item.contextValue = ContextValues.RepositoryFolder; - void this.ensureSubscription(); - return item; } + async getSplattedChild() { + if (this.child == null) { + await this.getChildren(); + } + + return this.child; + } + @gate() @debug() async refresh(reset: boolean = false) { @@ -89,14 +99,15 @@ export class TagsRepositoryNode extends SubscribeableViewNode { if (e.changed(RepositoryChange.Config) || e.changed(RepositoryChange.Tags)) { void this.triggerChange(true); - if (this.root) { - void this.parent?.triggerChange(true); - } + // if (this.root) { + // void this.parent?.triggerChange(true); + // } } } } export class TagsViewNode extends ViewNode { + protected splatted = true; private children: TagsRepositoryNode[] | undefined; constructor(view: TagsView) { @@ -104,22 +115,17 @@ export class TagsViewNode extends ViewNode { } async getChildren(): Promise { - if (this.children != null) { - for (const child of this.children) { - child.dispose?.(); - } - this.children = undefined; + if (this.children == null) { + const repositories = await Container.git.getOrderedRepositories(); + if (repositories.length === 0) return [new MessageNode(this.view, this, 'No tags could be found.')]; + + const splat = repositories.length === 1; + this.children = repositories.map( + r => new TagsRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, splat), + ); } - const repositories = await Container.git.getOrderedRepositories(); - if (repositories.length === 0) return [new MessageNode(this.view, this, 'No tags could be found.')]; - - const root = repositories.length === 1; - this.children = repositories.map( - r => new TagsRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, root), - ); - - if (root) { + if (this.children.length === 1) { const [child] = this.children; const tags = await child.repo.getTags(); @@ -127,6 +133,7 @@ export class TagsViewNode extends ViewNode { return child.getChildren(); } + return this.children; } @@ -134,6 +141,25 @@ export class TagsViewNode extends ViewNode { const item = new TreeItem('Tags', TreeItemCollapsibleState.Expanded); return item; } + + async getSplattedChild() { + if (this.children == null) { + await this.getChildren(); + } + + return this.children?.length === 1 ? this.children[0] : undefined; + } + + @gate() + @debug() + refresh(reset: boolean = false) { + if (reset && this.children != null) { + for (const child of this.children) { + child.dispose(); + } + this.children = undefined; + } + } } export class TagsView extends ViewBase { @@ -204,14 +230,13 @@ export class TagsView extends ViewBase { findTag(tag: GitTagReference, token?: CancellationToken) { const repoNodeId = RepositoryNode.getId(tag.repoPath); - return this.findNode((n: any) => n.tag !== undefined && n.tag.ref === tag.ref, { + return this.findNode((n: any) => n.tag?.ref === tag.ref, { allowPaging: true, - maxDepth: 5, + maxDepth: 2, canTraverse: n => { - // Only search for tag nodes in the same repo within TagsNode - if (n instanceof RepositoriesNode) return true; + if (n instanceof TagsViewNode) return true; - if (n instanceof RepositoryNode || n instanceof TagsNode || n instanceof BranchOrTagFolderNode) { + if (n instanceof TagsRepositoryNode || n instanceof BranchOrTagFolderNode) { return n.id.startsWith(repoNodeId); } @@ -233,12 +258,12 @@ export class TagsView extends ViewBase { return window.withProgress( { location: ProgressLocation.Notification, - title: `Revealing ${GitReference.toString(tag, { icon: false })} in the Repositories view...`, + title: `Revealing ${GitReference.toString(tag, { icon: false })} in the side bar...`, cancellable: true, }, async (progress, token) => { const node = await this.findTag(tag, token); - if (node === undefined) return node; + if (node == null) return undefined; await this.ensureRevealNode(node, options); diff --git a/src/views/viewBase.ts b/src/views/viewBase.ts index 528dac4..e514b8d 100644 --- a/src/views/viewBase.ts +++ b/src/views/viewBase.ts @@ -395,7 +395,11 @@ export abstract class ViewBase< children = await node.getChildren(); if (children.length === 0) continue; - if (PageableViewNode.is(node)) { + while (node != null && !PageableViewNode.is(node)) { + node = await node.getSplattedChild?.(); + } + + if (node != null && PageableViewNode.is(node)) { let child = children.find(predicate); if (child != null) return child; @@ -442,11 +446,14 @@ export abstract class ViewBase< const nodes: ViewNode[] = []; let parent: ViewNode | undefined = node; - while (parent !== undefined) { + while (parent != null) { nodes.push(parent); parent = parent.getParent(); } - nodes.pop(); + + if (nodes.length > 1) { + nodes.pop(); + } for (const n of nodes.reverse()) { try { diff --git a/src/webviews/apps/settings/partials/code-lens.html b/src/webviews/apps/settings/partials/code-lens.html index 5c13416..bf8d3e6 100644 --- a/src/webviews/apps/settings/partials/code-lens.html +++ b/src/webviews/apps/settings/partials/code-lens.html @@ -73,7 +73,7 @@ opens changes with the previous revision