Browse Source

Adds ambient context to view nodes & fixes ids

main
Eric Amodio 1 year ago
committed by Ramin Tadayon
parent
commit
1287978a3d
No known key found for this signature in database GPG Key ID: 79D60DDE3DFB95F5
53 changed files with 648 additions and 877 deletions
  1. +7
    -8
      src/views/branchesView.ts
  2. +23
    -16
      src/views/commitsView.ts
  3. +9
    -6
      src/views/contributorsView.ts
  4. +1
    -1
      src/views/fileHistoryView.ts
  5. +1
    -1
      src/views/lineHistoryView.ts
  6. +9
    -15
      src/views/nodes/UncommittedFilesNode.ts
  7. +7
    -5
      src/views/nodes/autolinkedItemNode.ts
  8. +7
    -3
      src/views/nodes/autolinkedItemsNode.ts
  9. +14
    -33
      src/views/nodes/branchNode.ts
  10. +9
    -30
      src/views/nodes/branchOrTagFolderNode.ts
  11. +7
    -30
      src/views/nodes/branchTrackingStatusFilesNode.ts
  12. +11
    -35
      src/views/nodes/branchTrackingStatusNode.ts
  13. +22
    -30
      src/views/nodes/branchesNode.ts
  14. +8
    -10
      src/views/nodes/commitFileNode.ts
  15. +9
    -11
      src/views/nodes/commitNode.ts
  16. +3
    -12
      src/views/nodes/compareBranchNode.ts
  17. +10
    -12
      src/views/nodes/compareResultsNode.ts
  18. +8
    -24
      src/views/nodes/contributorNode.ts
  19. +7
    -14
      src/views/nodes/contributorsNode.ts
  20. +10
    -11
      src/views/nodes/fileHistoryNode.ts
  21. +11
    -14
      src/views/nodes/folderNode.ts
  22. +15
    -13
      src/views/nodes/lineHistoryNode.ts
  23. +4
    -17
      src/views/nodes/mergeStatusNode.ts
  24. +19
    -20
      src/views/nodes/pullRequestNode.ts
  25. +5
    -18
      src/views/nodes/rebaseStatusNode.ts
  26. +7
    -13
      src/views/nodes/reflogNode.ts
  27. +5
    -24
      src/views/nodes/reflogRecordNode.ts
  28. +13
    -17
      src/views/nodes/remoteNode.ts
  29. +9
    -18
      src/views/nodes/remotesNode.ts
  30. +36
    -89
      src/views/nodes/repositoryNode.ts
  31. +8
    -7
      src/views/nodes/resultsCommitsNode.ts
  32. +5
    -4
      src/views/nodes/resultsFilesNode.ts
  33. +5
    -10
      src/views/nodes/searchResultsNode.ts
  34. +11
    -17
      src/views/nodes/stashNode.ts
  35. +13
    -20
      src/views/nodes/stashesNode.ts
  36. +9
    -15
      src/views/nodes/statusFilesNode.ts
  37. +8
    -19
      src/views/nodes/tagNode.ts
  38. +9
    -27
      src/views/nodes/tagsNode.ts
  39. +125
    -9
      src/views/nodes/viewNode.ts
  40. +18
    -20
      src/views/nodes/workspaceMissingRepositoryNode.ts
  41. +18
    -13
      src/views/nodes/workspaceNode.ts
  42. +4
    -11
      src/views/nodes/workspacesViewNode.ts
  43. +9
    -20
      src/views/nodes/worktreeNode.ts
  44. +7
    -15
      src/views/nodes/worktreesNode.ts
  45. +13
    -15
      src/views/remotesView.ts
  46. +47
    -55
      src/views/repositoriesView.ts
  47. +2
    -2
      src/views/searchAndCompareView.ts
  48. +5
    -6
      src/views/stashesView.ts
  49. +4
    -5
      src/views/tagsView.ts
  50. +7
    -22
      src/views/viewBase.ts
  51. +2
    -2
      src/views/viewCommands.ts
  52. +7
    -7
      src/views/workspacesView.ts
  53. +6
    -6
      src/views/worktreesView.ts

+ 7
- 8
src/views/branchesView.ts View File

@ -17,7 +17,6 @@ import { gate } from '../system/decorators/gate';
import { BranchesNode } from './nodes/branchesNode';
import { BranchNode } from './nodes/branchNode';
import { BranchOrTagFolderNode } from './nodes/branchOrTagFolderNode';
import { RepositoryNode } from './nodes/repositoryNode';
import type { ViewNode } from './nodes/viewNode';
import { RepositoriesSubscribeableNode, RepositoryFolderNode } from './nodes/viewNode';
import { ViewBase } from './viewBase';
@ -98,7 +97,7 @@ export class BranchesView extends ViewBase
protected readonly configKey = 'branches';
constructor(container: Container) {
super(container, 'gitlens.views.branches', 'Branches', 'branchesView');
super(container, 'branches', 'Branches', 'branchesView');
}
override get canReveal(): boolean {
@ -198,7 +197,7 @@ export class BranchesView extends ViewBase
findBranch(branch: GitBranchReference, token?: CancellationToken) {
if (branch.remote) return undefined;
const repoNodeId = RepositoryNode.getId(branch.repoPath);
const { repoPath } = branch;
return this.findNode((n: any) => n.branch?.ref === branch.ref, {
allowPaging: true,
@ -207,7 +206,7 @@ export class BranchesView extends ViewBase
if (n instanceof BranchesViewNode) return true;
if (n instanceof BranchesRepositoryNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -217,7 +216,7 @@ export class BranchesView extends ViewBase
}
async findCommit(commit: GitCommit | { repoPath: string; ref: string }, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(commit.repoPath);
const { repoPath } = commit;
// Get all the branches the commit is on
const branches = await this.container.git.getCommitBranches(
@ -234,10 +233,10 @@ export class BranchesView extends ViewBase
if (n instanceof BranchesViewNode) return true;
if (n instanceof BranchesRepositoryNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
if (n instanceof BranchNode && branches.includes(n.branch.name)) {
if (n instanceof BranchNode && n.repoPath === repoPath && branches.includes(n.branch.name)) {
await n.loadMore({ until: commit.ref });
return true;
}
@ -311,7 +310,7 @@ export class BranchesView extends ViewBase
repoPath: string,
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
) {
const node = await this.findNode(RepositoryFolderNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof RepositoryFolderNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof BranchesViewNode || n instanceof RepositoryFolderNode,
});

+ 23
- 16
src/views/commitsView.ts View File

@ -16,7 +16,7 @@ import type { GitRevisionReference } from '../git/models/reference';
import { getReferenceLabel } from '../git/models/reference';
import type { RepositoryChangeEvent } from '../git/models/repository';
import { Repository, RepositoryChange, RepositoryChangeComparisonMode } from '../git/models/repository';
import { executeCommand } from '../system/command';
import { createCommand, executeCommand } from '../system/command';
import { configuration } from '../system/configuration';
import { setContext } from '../system/context';
import { gate } from '../system/decorators/gate';
@ -29,7 +29,6 @@ import { CommitFileNode } from './nodes/commitFileNode';
import { CommitNode } from './nodes/commitNode';
import { CommandMessageNode } from './nodes/common';
import { FileRevisionAsCommitNode } from './nodes/fileRevisionAsCommitNode';
import { RepositoryNode } from './nodes/repositoryNode';
import type { ViewNode } from './nodes/viewNode';
import { RepositoriesSubscribeableNode, RepositoryFolderNode } from './nodes/viewNode';
import { ViewBase } from './viewBase';
@ -55,14 +54,22 @@ export class CommitsRepositoryNode extends RepositoryFolderNode
}
}
this.child = new BranchNode(this.uri, this.view, this.splatted ? this.parent ?? this : this, branch, true, {
expanded: true,
limitCommits: !this.splatted,
showComparison: this.view.config.showBranchComparison,
showCurrent: false,
showTracking: true,
authors: authors,
});
this.child = new BranchNode(
this.uri,
this.view,
this.splatted ? this.parent ?? this : this,
this.repo,
branch,
true,
{
expanded: true,
limitCommits: !this.splatted,
showComparison: this.view.config.showBranchComparison,
showCurrent: false,
showTracking: true,
authors: authors,
},
);
}
return this.child.getChildren();
@ -146,7 +153,7 @@ export class CommitsViewNode extends RepositoriesSubscribeableNode
? new CommandMessageNode(
this.view,
this,
{ command: Commands.ShowGraph, title: 'Show Commit Graph' },
createCommand(Commands.ShowGraph, 'Show Commit Graph'),
'Visualize commits on the Commit Graph ✨',
undefined,
'Visualize commits on the Commit Graph ✨',
@ -187,7 +194,7 @@ export class CommitsView extends ViewBase {
protected readonly configKey = 'commits';
constructor(container: Container) {
super(container, 'gitlens.views.commits', 'Commits', 'commitsView');
super(container, 'commits', 'Commits', 'commitsView');
this.disposables.push(container.usage.onDidChange(this.onUsageChanged, this));
}
@ -341,7 +348,7 @@ export class CommitsView extends ViewBase {
}
async findCommit(commit: GitCommit | { repoPath: string; ref: string }, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(commit.repoPath);
const { repoPath } = commit;
const branch = await this.container.git.getBranch(commit.repoPath);
if (branch == null) return undefined;
@ -370,7 +377,7 @@ export class CommitsView extends ViewBase {
}
if (n instanceof CommitsRepositoryNode) {
if (n.id.startsWith(repoNodeId)) {
if (n.repoPath === repoPath) {
const node = await n.getSplattedChild?.();
if (node instanceof BranchNode) {
await node.loadMore({ until: commit.ref });
@ -380,7 +387,7 @@ export class CommitsView extends ViewBase {
}
if (n instanceof BranchTrackingStatusNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -423,7 +430,7 @@ export class CommitsView extends ViewBase {
repoPath: string,
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
) {
const node = await this.findNode(RepositoryFolderNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof RepositoryFolderNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof CommitsViewNode || n instanceof RepositoryFolderNode,
});

+ 9
- 6
src/views/contributorsView.ts View File

@ -15,7 +15,6 @@ import { gate } from '../system/decorators/gate';
import { debug } from '../system/decorators/log';
import { ContributorNode } from './nodes/contributorNode';
import { ContributorsNode } from './nodes/contributorsNode';
import { RepositoryNode } from './nodes/repositoryNode';
import type { ViewNode } from './nodes/viewNode';
import { RepositoriesSubscribeableNode, RepositoryFolderNode } from './nodes/viewNode';
import { ViewBase } from './viewBase';
@ -116,7 +115,7 @@ export class ContributorsView extends ViewBase
protected readonly configKey = 'contributors';
constructor(container: Container) {
super(container, 'gitlens.views.contributors', 'Contributors', 'contributorsView');
super(container, 'contributors', 'Contributors', 'contributorsView');
}
override get canReveal(): boolean {
@ -207,17 +206,21 @@ export class ContributorsView extends ViewBase
}
findContributor(contributor: GitContributor, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(contributor.repoPath);
const { repoPath, username, email, name } = contributor;
return this.findNode(
ContributorNode.getId(contributor.repoPath, contributor.name, contributor.email, contributor.username),
n =>
n instanceof ContributorNode &&
n.contributor.username === username &&
n.contributor.email === email &&
n.contributor.name === name,
{
maxDepth: 2,
canTraverse: n => {
if (n instanceof ContributorsViewNode) return true;
if (n instanceof ContributorsRepositoryNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -232,7 +235,7 @@ export class ContributorsView extends ViewBase
repoPath: string,
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
) {
const node = await this.findNode(RepositoryFolderNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof RepositoryFolderNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof ContributorsViewNode || n instanceof RepositoryFolderNode,
});

+ 1
- 1
src/views/fileHistoryView.ts View File

@ -20,7 +20,7 @@ export class FileHistoryView extends ViewBase
private _followEditor: boolean = true;
constructor(container: Container) {
super(container, 'gitlens.views.fileHistory', 'File History', 'fileHistoryView');
super(container, 'fileHistory', 'File History', 'fileHistoryView');
void setContext('gitlens:views:fileHistory:cursorFollowing', this._followCursor);
void setContext('gitlens:views:fileHistory:editorFollowing', this._followEditor);

+ 1
- 1
src/views/lineHistoryView.ts View File

@ -15,7 +15,7 @@ export class LineHistoryView extends ViewBase
protected readonly configKey = 'lineHistory';
constructor(container: Container) {
super(container, 'gitlens.views.lineHistory', 'Line History', 'lineHistoryView');
super(container, 'lineHistory', 'Line History', 'lineHistoryView');
void setContext('gitlens:views:lineHistory:editorFollowing', true);
}

+ 9
- 15
src/views/nodes/UncommittedFilesNode.ts View File

@ -13,18 +13,10 @@ import { joinPaths, normalizePath } from '../../system/path';
import type { ViewsWithWorkingTree } from '../viewBase';
import type { FileNode } from './folderNode';
import { FolderNode } from './folderNode';
import { RepositoryNode } from './repositoryNode';
import { UncommittedFileNode } from './UncommittedFileNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class UncommittedFilesNode extends ViewNode<ViewsWithWorkingTree> {
static key = ':uncommitted-files';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
readonly repoPath: string;
constructor(
view: ViewsWithWorkingTree,
protected override readonly parent: ViewNode,
@ -37,16 +29,18 @@ export class UncommittedFilesNode extends ViewNode {
readonly upstream?: string;
},
public readonly range: string | undefined,
private readonly options?: {
workspaceId?: string;
},
) {
super(GitUri.fromRepoPath(status.repoPath), view, parent);
this.repoPath = status.repoPath;
this._uniqueId = getViewNodeId('uncommitted-files', this.context);
}
override get id(): string {
return UncommittedFilesNode.getId(this.repoPath, this.options?.workspaceId);
return this._uniqueId;
}
get repoPath(): string {
return this.status.repoPath;
}
getChildren(): ViewNode[] {
@ -87,7 +81,7 @@ export class UncommittedFilesNode extends ViewNode {
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, repoPath, '', hierarchy, true);
const root = new FolderNode(this.view, this, hierarchy, repoPath, '', undefined, true);
children = root.getChildren() as FileNode[];
} else {
children.sort(

+ 7
- 5
src/views/nodes/autolinkedItemNode.ts View File

@ -10,7 +10,7 @@ import {
} from '../../git/models/issue';
import { fromNow } from '../../system/date';
import type { ViewsWithCommits } from '../viewBase';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class AutolinkedItemNode extends ViewNode<ViewsWithCommits> {
constructor(
@ -20,14 +20,16 @@ export class AutolinkedItemNode extends ViewNode {
public readonly item: Autolink | IssueOrPullRequest,
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
}
override toClipboard(): string {
return this.item.url;
this._uniqueId = getViewNodeId(`autolink+${item.id}`, this.context);
}
override get id(): string {
return `${this.parent.id}:item(${this.item.id})`;
return this._uniqueId;
}
override toClipboard(): string {
return this.item.url;
}
getChildren(): ViewNode[] {

+ 7
- 3
src/views/nodes/autolinkedItemsNode.ts View File

@ -11,12 +11,11 @@ import type { ViewsWithCommits } from '../viewBase';
import { AutolinkedItemNode } from './autolinkedItemNode';
import { LoadMoreNode, MessageNode } from './common';
import { PullRequestNode } from './pullRequestNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
let instanceId = 0;
export class AutolinkedItemsNode extends ViewNode<ViewsWithCommits> {
private _children: ViewNode[] | undefined;
private _instanceId: number;
constructor(
@ -27,13 +26,18 @@ export class AutolinkedItemsNode extends ViewNode {
private expand: boolean,
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
this._instanceId = instanceId++;
this.updateContext({ autolinksId: String(this._instanceId) });
this._uniqueId = getViewNodeId(`autolinks`, this.context);
}
override get id(): string {
return `${this.parent.id}:results:autolinked:${this._instanceId}`;
return this._uniqueId;
}
private _children: ViewNode[] | undefined;
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const commits = [...this.log.commits.values()];

+ 14
- 33
src/views/nodes/branchNode.ts View File

@ -10,6 +10,7 @@ import type { PullRequest } from '../../git/models/pullRequest';
import { PullRequestState } from '../../git/models/pullRequest';
import type { GitBranchReference } from '../../git/models/reference';
import { GitRemote, GitRemoteType } from '../../git/models/remote';
import type { Repository } from '../../git/models/repository';
import type { GitUser } from '../../git/models/user';
import { getContext } from '../../system/context';
import { gate } from '../../system/decorators/gate';
@ -28,9 +29,8 @@ import { insertDateMarkers } from './helpers';
import { MergeStatusNode } from './mergeStatusNode';
import { PullRequestNode } from './pullRequestNode';
import { RebaseStatusNode } from './rebaseStatusNode';
import { RepositoryNode } from './repositoryNode';
import type { PageableViewNode, ViewNode } from './viewNode';
import { ContextValues, ViewRefNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewRefNode } from './viewNode';
type State = {
pullRequest: PullRequest | null | undefined;
@ -38,11 +38,6 @@ type State = {
};
export class BranchNode extends ViewRefNode<ViewsWithBranches, GitBranchReference, State> implements PageableViewNode {
static key = ':branch';
static getId(repoPath: string, name: string, root: boolean, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}(${name})${root ? ':root' : ''}`;
}
private readonly options: {
expanded: boolean;
limitCommits: boolean;
@ -52,7 +47,6 @@ export class BranchNode extends ViewRefNode
showStatus: boolean;
showTracking: boolean;
authors?: GitUser[];
workspaceId?: string;
};
protected override splatted = true;
@ -60,10 +54,10 @@ export class BranchNode extends ViewRefNode
uri: GitUri,
view: ViewsWithBranches,
public override parent: ViewNode,
public readonly repo: Repository,
public readonly branch: GitBranch,
// Specifies that the node is shown as a root
public readonly root: boolean,
options?: {
expanded?: boolean;
limitCommits?: boolean;
@ -73,11 +67,13 @@ export class BranchNode extends ViewRefNode
showStatus?: boolean;
showTracking?: boolean;
authors?: GitUser[];
workspaceId?: string;
},
) {
super(uri, view, parent);
this.updateContext({ repository: repo, branch: branch, root: root });
this._uniqueId = getViewNodeId('branch', this.context);
this.options = {
expanded: false,
limitCommits: false,
@ -93,12 +89,12 @@ export class BranchNode extends ViewRefNode
};
}
override toClipboard(): string {
return this.branch.name;
override get id(): string {
return this._uniqueId;
}
override get id(): string {
return BranchNode.getId(this.branch.repoPath, this.branch.name, this.root, this.options?.workspaceId);
override toClipboard(): string {
return this.branch.name;
}
compacted: boolean = false;
@ -228,7 +224,6 @@ export class BranchNode extends ViewRefNode
branch,
this.options.showComparison,
this.splatted,
{ workspaceId: this.options.workspaceId },
),
);
}
@ -250,7 +245,6 @@ export class BranchNode extends ViewRefNode
mergeStatus,
status ?? (await this.view.container.git.getStatusForRepo(this.uri.repoPath)),
this.root,
{ workspaceId: this.options?.workspaceId },
),
);
} else if (
@ -266,7 +260,6 @@ export class BranchNode extends ViewRefNode
rebaseStatus,
status ?? (await this.view.container.git.getStatusForRepo(this.uri.repoPath)),
this.root,
{ workspaceId: this.options?.workspaceId },
),
);
} else if (this.options.showTracking) {
@ -279,34 +272,22 @@ export class BranchNode extends ViewRefNode
if (branch.upstream != null) {
if (this.root && !status.state.behind && !status.state.ahead) {
children.push(
new BranchTrackingStatusNode(this.view, this, branch, status, 'same', this.root, {
workspaceId: this.options?.workspaceId,
}),
);
children.push(new BranchTrackingStatusNode(this.view, this, branch, status, 'same', this.root));
} else {
if (status.state.behind) {
children.push(
new BranchTrackingStatusNode(this.view, this, branch, status, 'behind', this.root, {
workspaceId: this.options?.workspaceId,
}),
new BranchTrackingStatusNode(this.view, this, branch, status, 'behind', this.root),
);
}
if (status.state.ahead) {
children.push(
new BranchTrackingStatusNode(this.view, this, branch, status, 'ahead', this.root, {
workspaceId: this.options?.workspaceId,
}),
new BranchTrackingStatusNode(this.view, this, branch, status, 'ahead', this.root),
);
}
}
} else {
children.push(
new BranchTrackingStatusNode(this.view, this, branch, status, 'none', this.root, {
workspaceId: this.options?.workspaceId,
}),
);
children.push(new BranchTrackingStatusNode(this.view, this, branch, status, 'none', this.root));
}
}

+ 9
- 30
src/views/nodes/branchOrTagFolderNode.ts View File

@ -3,50 +3,31 @@ import { GitUri } from '../../git/gitUri';
import type { HierarchicalItem } from '../../system/array';
import type { View } from '../viewBase';
import { BranchNode } from './branchNode';
import { RepositoryNode } from './repositoryNode';
import type { TagNode } from './tagNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class BranchOrTagFolderNode extends ViewNode {
static getId(
repoPath: string,
key: string | undefined,
type: string,
relativePath: string | undefined,
workspaceId?: string,
): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}:${
key === undefined ? type : `${key}:${type}`
}-folder(${relativePath})`;
}
constructor(
view: View,
protected override readonly parent: ViewNode,
public readonly type: 'branch' | 'remote-branch' | 'tag',
public readonly root: HierarchicalItem<BranchNode | TagNode>,
public readonly repoPath: string,
public readonly folderName: string,
public readonly relativePath: string | undefined,
public readonly root: HierarchicalItem<BranchNode | TagNode>,
private readonly _key?: string,
private readonly _expanded: boolean = false,
private readonly options?: { workspaceId?: string },
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
}
override toClipboard(): string {
return this.folderName;
this._uniqueId = getViewNodeId(`${type}-folder+${relativePath ?? folderName}`, this.context);
}
override get id(): string {
return BranchOrTagFolderNode.getId(
this.repoPath,
this._key,
this.type,
this.relativePath,
this.options?.workspaceId,
);
return this._uniqueId;
}
override toClipboard(): string {
return this.folderName;
}
getChildren(): ViewNode[] {
@ -63,13 +44,11 @@ export class BranchOrTagFolderNode extends ViewNode {
this.view,
this.folderName ? this : this.parent,
this.type,
folder,
this.repoPath,
folder.name,
folder.relativePath,
folder,
this._key,
expanded,
this.options,
),
);
continue;

+ 7
- 30
src/views/nodes/branchTrackingStatusFilesNode.ts View File

@ -9,51 +9,28 @@ import { filter, flatMap, map } from '../../system/iterable';
import { joinPaths, normalizePath } from '../../system/path';
import { pluralize, sortCompare } from '../../system/string';
import type { ViewsWithCommits } from '../viewBase';
import { BranchNode } from './branchNode';
import type { BranchTrackingStatus } from './branchTrackingStatusNode';
import type { FileNode } from './folderNode';
import { FolderNode } from './folderNode';
import { StatusFileNode } from './statusFileNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class BranchTrackingStatusFilesNode extends ViewNode<ViewsWithCommits> {
static key = ':status-branch:files';
static getId(
repoPath: string,
name: string,
root: boolean,
upstream: string,
direction: string,
workspaceId?: string,
): string {
return `${BranchNode.getId(repoPath, name, root, workspaceId)}${this.key}(${upstream}|${direction})`;
}
readonly repoPath: string;
constructor(
view: ViewsWithCommits,
protected override readonly parent: ViewNode,
public readonly branch: GitBranch,
public readonly status: Required<BranchTrackingStatus>,
public readonly direction: 'ahead' | 'behind',
// Specifies that the node is shown as a root
private readonly root: boolean = false,
private readonly options?: { workspaceId?: string },
) {
super(GitUri.fromRepoPath(status.repoPath), view, parent);
this.repoPath = status.repoPath;
this.updateContext({ branch: branch, branchStatus: status, branchStatusUpstreamType: direction });
this._uniqueId = getViewNodeId('tracking-status-files', this.context);
}
override get id(): string {
return BranchTrackingStatusFilesNode.getId(
this.status.repoPath,
this.status.ref,
this.root,
this.status.upstream,
this.direction,
this.options?.workspaceId,
);
get repoPath(): string {
return this.status.repoPath;
}
async getChildren(): Promise<ViewNode[]> {
@ -106,7 +83,7 @@ export class BranchTrackingStatusFilesNode extends ViewNode {
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy, false);
const root = new FolderNode(this.view, this, hierarchy, this.repoPath, '', undefined, false);
children = root.getChildren() as FileNode[];
} else {
children.sort((a, b) => a.priority - b.priority || sortCompare(a.label!, b.label!));

+ 11
- 35
src/views/nodes/branchTrackingStatusNode.ts View File

@ -12,13 +12,12 @@ import { debug } from '../../system/decorators/log';
import { first, map } from '../../system/iterable';
import { pluralize } from '../../system/string';
import type { ViewsWithCommits } from '../viewBase';
import { BranchNode } from './branchNode';
import { BranchTrackingStatusFilesNode } from './branchTrackingStatusFilesNode';
import { CommitNode } from './commitNode';
import { LoadMoreNode } from './common';
import { insertDateMarkers } from './helpers';
import type { PageableViewNode } from './viewNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export interface BranchTrackingStatus {
ref: string;
@ -28,23 +27,6 @@ export interface BranchTrackingStatus {
}
export class BranchTrackingStatusNode extends ViewNode<ViewsWithCommits> implements PageableViewNode {
static key = ':status-branch:upstream';
static getId(
repoPath: string,
name: string,
root: boolean,
upstream: string | undefined,
upstreamType: string,
workspaceId?: string,
): string {
return `${BranchNode.getId(repoPath, name, root, workspaceId)}${this.key}(${upstream ?? ''}):${upstreamType}`;
}
private readonly options: {
showAheadCommits?: boolean;
workspaceId?: string;
};
constructor(
view: ViewsWithCommits,
protected override readonly parent: ViewNode,
@ -53,25 +35,23 @@ export class BranchTrackingStatusNode extends ViewNode impleme
public readonly upstreamType: 'ahead' | 'behind' | 'same' | 'none',
// Specifies that the node is shown as a root
public readonly root: boolean = false,
options?: {
private readonly options?: {
showAheadCommits?: boolean;
workspaceId?: string;
},
) {
super(GitUri.fromRepoPath(status.repoPath), view, parent);
this.options = { showAheadCommits: false, ...options };
this.updateContext({
branch: branch,
branchStatus: status,
branchStatusUpstreamType: upstreamType,
root: root,
});
this._uniqueId = getViewNodeId('tracking-status', this.context);
}
override get id(): string {
return BranchTrackingStatusNode.getId(
this.status.repoPath,
this.status.ref,
this.root,
this.status.upstream,
this.upstreamType,
this.options?.workspaceId,
);
return this._uniqueId;
}
get repoPath(): string {
@ -107,7 +87,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme
let showFiles = true;
if (
!this.options.showAheadCommits &&
!this.options?.showAheadCommits &&
this.upstreamType === 'ahead' &&
this.status.upstream &&
this.status.state.ahead > 0
@ -121,8 +101,6 @@ export class BranchTrackingStatusNode extends ViewNode impleme
this.branch,
this.status as Required<BranchTrackingStatus>,
this.upstreamType,
this.root,
{ workspaceId: this.options?.workspaceId },
).getChildren()),
);
} else {
@ -147,8 +125,6 @@ export class BranchTrackingStatusNode extends ViewNode impleme
this.branch,
this.status as Required<BranchTrackingStatus>,
this.upstreamType,
this.root,
{ workspaceId: this.options?.workspaceId },
),
);
}

+ 22
- 30
src/views/nodes/branchesNode.ts View File

@ -10,35 +10,31 @@ import type { ViewsWithBranchesNode } from '../viewBase';
import { BranchNode } from './branchNode';
import { BranchOrTagFolderNode } from './branchOrTagFolderNode';
import { MessageNode } from './common';
import { RepositoryNode } from './repositoryNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class BranchesNode extends ViewNode<ViewsWithBranchesNode> {
static key = ':branches';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
private _children: ViewNode[] | undefined;
constructor(
uri: GitUri,
view: ViewsWithBranchesNode,
protected override readonly parent: ViewNode,
public readonly repo: Repository,
private readonly options?: { workspaceId?: string },
) {
super(uri, view, parent);
this.updateContext({ repository: repo });
this._uniqueId = getViewNodeId('branches', this.context);
}
override get id(): string {
return BranchesNode.getId(this.repo.path, this.options?.workspaceId);
return this._uniqueId;
}
get repoPath(): string {
return this.repo.path;
}
private _children: ViewNode[] | undefined;
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const branches = await this.repo.getBranches({
@ -51,13 +47,20 @@ export class BranchesNode extends ViewNode {
// TODO@eamodio handle paging
const branchNodes = branches.values.map(
b =>
new BranchNode(GitUri.fromRepoPath(this.uri.repoPath!, b.ref), this.view, this, b, false, {
showComparison:
this.view instanceof RepositoriesView
? this.view.config.branches.showBranchComparison
: this.view.config.showBranchComparison,
workspaceId: this.options?.workspaceId,
}),
new BranchNode(
GitUri.fromRepoPath(this.uri.repoPath!, b.ref),
this.view,
this,
this.repo,
b,
false,
{
showComparison:
this.view instanceof RepositoriesView
? this.view.config.branches.showBranchComparison
: this.view.config.showBranchComparison,
},
),
);
if (this.view.config.branches.layout === ViewBranchesLayout.List) return branchNodes;
@ -72,18 +75,7 @@ export class BranchesNode extends ViewNode {
},
);
const root = new BranchOrTagFolderNode(
this.view,
this,
'branch',
this.repo.path,
'',
undefined,
hierarchy,
'branches',
undefined,
{ workspaceId: this.options?.workspaceId },
);
const root = new BranchOrTagFolderNode(this.view, this, 'branch', hierarchy, this.repo.path, '', undefined);
this._children = root.getChildren();
}

+ 8
- 10
src/views/nodes/commitFileNode.ts View File

@ -12,16 +12,11 @@ import type { GitRevisionReference } from '../../git/models/reference';
import { joinPaths, relativeDir } from '../../system/path';
import type { ViewsWithCommits, ViewsWithStashes } from '../viewBase';
import type { ViewNode } from './viewNode';
import { ContextValues, ViewRefFileNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewRefFileNode } from './viewNode';
export class CommitFileNode<
TView extends ViewsWithCommits | ViewsWithStashes = ViewsWithCommits,
> extends ViewRefFileNode<TView> {
static key = ':file';
static getId(parent: ViewNode, path: string): string {
return `${parent.id}${this.key}(${path})`;
}
constructor(
view: TView,
parent: ViewNode,
@ -34,14 +29,17 @@ export class CommitFileNode<
} = {},
) {
super(GitUri.fromFile(file, commit.repoPath, commit.sha), view, parent, file);
}
override toClipboard(): string {
return this.file.path;
this.updateContext({ commit: commit, file: file });
this._uniqueId = getViewNodeId('commit-file', this.context);
}
override get id(): string {
return CommitFileNode.getId(this.parent, this.file.path);
return this._uniqueId;
}
override toClipboard(): string {
return this.file.path;
}
get priority(): number {

+ 9
- 11
src/views/nodes/commitNode.ts View File

@ -27,7 +27,7 @@ import type { FileNode } from './folderNode';
import { FolderNode } from './folderNode';
import { PullRequestNode } from './pullRequestNode';
import type { ViewNode } from './viewNode';
import { ContextValues, ViewRefNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewRefNode } from './viewNode';
type State = {
pullRequest: PullRequest | null | undefined;
@ -35,11 +35,6 @@ type State = {
};
export class CommitNode extends ViewRefNode<ViewsWithCommits | FileHistoryView, GitRevisionReference, State> {
static key = ':commit';
static getId(parent: ViewNode, sha: string): string {
return `${parent.id}${this.key}(${sha})`;
}
constructor(
view: ViewsWithCommits | FileHistoryView,
parent: ViewNode,
@ -50,14 +45,17 @@ export class CommitNode extends ViewRefNode
private readonly _options: { expand?: boolean } = {},
) {
super(commit.getGitUri(), view, parent);
}
override toClipboard(): string {
return `${this.commit.shortSha}: ${this.commit.summary}`;
this.updateContext({ commit: commit });
this._uniqueId = getViewNodeId('commit', this.context);
}
override get id(): string {
return CommitNode.getId(this.parent, this.commit.sha);
return this._uniqueId;
}
override toClipboard(): string {
return `${this.commit.shortSha}: ${this.commit.summary}`;
}
get isTip(): boolean {
@ -133,7 +131,7 @@ export class CommitNode extends ViewRefNode
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy);
const root = new FolderNode(this.view, this, hierarchy, this.repoPath, '', undefined);
children = root.getChildren() as FileNode[];
} else {
(children as FileNode[]).sort((a, b) => sortCompare(a.label!, b.label!));

+ 3
- 12
src/views/nodes/compareBranchNode.ts View File

@ -13,19 +13,13 @@ import { getSettledValue } from '../../system/promise';
import { pluralize } from '../../system/string';
import type { ViewsWithBranches } from '../viewBase';
import type { WorktreesView } from '../worktreesView';
import { RepositoryNode } from './repositoryNode';
import type { CommitsQueryResults } from './resultsCommitsNode';
import { ResultsCommitsNode } from './resultsCommitsNode';
import type { FilesQueryResults } from './resultsFilesNode';
import { ResultsFilesNode } from './resultsFilesNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class CompareBranchNode extends ViewNode<ViewsWithBranches | WorktreesView> {
static key = ':compare-branch';
static getId(repoPath: string, name: string, root: boolean, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}(${name})${root ? ':root' : ''}`;
}
private _children: ViewNode[] | undefined;
private _compareWith: StoredBranchComparison | undefined;
@ -37,10 +31,11 @@ export class CompareBranchNode extends ViewNode
private showComparison: ViewShowBranchComparison,
// Specifies that the node is shown as a root
public readonly root: boolean = false,
private readonly options?: { workspaceId?: string },
) {
super(uri, view, parent);
this.updateContext({ branch: branch, root: root });
this._uniqueId = getViewNodeId('compare-branch', this.context);
this.loadCompareWith();
}
@ -58,10 +53,6 @@ export class CompareBranchNode extends ViewNode
};
}
override get id(): string {
return CompareBranchNode.getId(this.branch.repoPath, this.branch.name, this.root, this.options?.workspaceId);
}
get repoPath(): string {
return this.branch.repoPath;
}

+ 10
- 12
src/views/nodes/compareResultsNode.ts View File

@ -8,26 +8,19 @@ import { debug, log } from '../../system/decorators/log';
import { getSettledValue } from '../../system/promise';
import { pluralize } from '../../system/string';
import type { SearchAndCompareView } from '../searchAndCompareView';
import { RepositoryNode } from './repositoryNode';
import type { CommitsQueryResults } from './resultsCommitsNode';
import { ResultsCommitsNode } from './resultsCommitsNode';
import type { FilesQueryResults } from './resultsFilesNode';
import { ResultsFilesNode } from './resultsFilesNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
let instanceId = 0;
export class CompareResultsNode extends ViewNode<SearchAndCompareView> {
static key = ':compare-results';
static getId(repoPath: string, ref1: string, ref2: string, instanceId: number): string {
return `${RepositoryNode.getId(repoPath)}${this.key}(${ref1}|${ref2}):${instanceId}`;
}
static getPinnableId(repoPath: string, ref1: string, ref2: string) {
return md5(`${repoPath}|${ref1}|${ref2}`, 'base64');
}
private _children: ViewNode[] | undefined;
private _instanceId: number;
constructor(
@ -39,7 +32,14 @@ export class CompareResultsNode extends ViewNode {
private _pinned: number = 0,
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
this._instanceId = instanceId++;
this.updateContext({ comparisonId: `${_ref.ref}+${_compareWith.ref}+${this._instanceId}` });
this._uniqueId = getViewNodeId('comparison-results', this.context);
}
override get id(): string {
return this._uniqueId;
}
get ahead(): { readonly ref1: string; readonly ref2: string } {
@ -56,10 +56,6 @@ export class CompareResultsNode extends ViewNode {
};
}
override get id(): string {
return CompareResultsNode.getId(this.repoPath, this._ref.ref, this._compareWith.ref, this._instanceId);
}
get canDismiss(): boolean {
return !this.pinned;
}
@ -73,6 +69,8 @@ export class CompareResultsNode extends ViewNode {
return this._pinned !== 0;
}
private _children: ViewNode[] | undefined;
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const ahead = this.ahead;

+ 8
- 24
src/views/nodes/contributorNode.ts View File

@ -14,22 +14,10 @@ import type { ViewsWithContributors } from '../viewBase';
import { CommitNode } from './commitNode';
import { LoadMoreNode, MessageNode } from './common';
import { insertDateMarkers } from './helpers';
import { RepositoryNode } from './repositoryNode';
import type { PageableViewNode } from './viewNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class ContributorNode extends ViewNode<ViewsWithContributors> implements PageableViewNode {
static key = ':contributor';
static getId(
repoPath: string,
name: string | undefined,
email: string | undefined,
username: string | undefined,
workspaceId?: string,
): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}(${name}|${email}|${username})`;
}
constructor(
uri: GitUri,
view: ViewsWithContributors,
@ -39,24 +27,20 @@ export class ContributorNode extends ViewNode implements
all?: boolean;
ref?: string;
presence: Map<string, ContactPresence> | undefined;
workspaceId?: string;
},
) {
super(uri, view, parent);
}
override toClipboard(): string {
return `${this.contributor.name}${this.contributor.email ? ` <${this.contributor.email}>` : ''}`;
this.updateContext({ contributor: contributor });
this._uniqueId = getViewNodeId('contributor', this.context);
}
override get id(): string {
return ContributorNode.getId(
this.contributor.repoPath,
this.contributor.name,
this.contributor.email,
this.contributor.username,
this._options?.workspaceId,
);
return this._uniqueId;
}
override toClipboard(): string {
return `${this.contributor.name}${this.contributor.email ? ` <${this.contributor.email}>` : ''}`;
}
get repoPath(): string {

+ 7
- 14
src/views/nodes/contributorsNode.ts View File

@ -9,39 +9,33 @@ import { timeout } from '../../system/decorators/timeout';
import type { ViewsWithContributorsNode } from '../viewBase';
import { MessageNode } from './common';
import { ContributorNode } from './contributorNode';
import { RepositoryNode } from './repositoryNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class ContributorsNode extends ViewNode<ViewsWithContributorsNode> {
static key = ':contributors';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
protected override splatted = true;
private _children: ContributorNode[] | undefined;
constructor(
uri: GitUri,
view: ViewsWithContributorsNode,
protected override readonly parent: ViewNode,
public readonly repo: Repository,
private readonly options?: {
workspaceId?: string;
},
) {
super(uri, view, parent);
this.updateContext({ repository: repo });
this._uniqueId = getViewNodeId('contributors', this.context);
}
override get id(): string {
return ContributorsNode.getId(this.repo.path, this.options?.workspaceId);
return this._uniqueId;
}
get repoPath(): string {
return this.repo.path;
}
private _children: ContributorNode[] | undefined;
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const all = configuration.get('views.contributors.showAllBranches');
@ -71,7 +65,6 @@ export class ContributorsNode extends ViewNode {
all: all,
ref: ref,
presence: presenceMap,
workspaceId: this.options?.workspaceId,
}),
);
}

+ 10
- 11
src/views/nodes/fileHistoryNode.ts View File

@ -18,16 +18,10 @@ import { LoadMoreNode, MessageNode } from './common';
import { FileHistoryTrackerNode } from './fileHistoryTrackerNode';
import { FileRevisionAsCommitNode } from './fileRevisionAsCommitNode';
import { insertDateMarkers } from './helpers';
import { RepositoryNode } from './repositoryNode';
import type { PageableViewNode, ViewNode } from './viewNode';
import { ContextValues, SubscribeableViewNode } from './viewNode';
import { ContextValues, getViewNodeId, SubscribeableViewNode } from './viewNode';
export class FileHistoryNode extends SubscribeableViewNode<FileHistoryView> implements PageableViewNode {
static key = ':history:file';
static getId(repoPath: string, uri: string): string {
return `${RepositoryNode.getId(repoPath)}${this.key}(${uri})`;
}
protected override splatted = true;
constructor(
@ -38,14 +32,19 @@ export class FileHistoryNode extends SubscribeableViewNode impl
private readonly branch: GitBranch | undefined,
) {
super(uri, view, parent);
}
override toClipboard(): string {
return this.uri.fileName;
if (branch != null) {
this.updateContext({ branch: branch });
}
this._uniqueId = getViewNodeId(`file-history+${uri.toString()}`, this.context);
}
override get id(): string {
return FileHistoryNode.getId(this.uri.repoPath!, this.uri.toString(true));
return this._uniqueId;
}
override toClipboard(): string {
return this.uri.fileName;
}
async getChildren(): Promise<ViewNode[]> {

+ 11
- 14
src/views/nodes/folderNode.ts View File

@ -7,7 +7,7 @@ import { sortCompare } from '../../system/string';
import type { StashesView } from '../stashesView';
import type { ViewsWithCommits } from '../viewBase';
import type { ViewFileNode } from './viewNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export interface FileNode extends ViewFileNode {
folderName: string;
@ -20,31 +20,28 @@ export interface FileNode extends ViewFileNode {
}
export class FolderNode extends ViewNode<ViewsWithCommits | StashesView> {
static key = ':folder';
static getId(parent: ViewNode, path: string): string {
return `${parent.id}${this.key}(${path})`;
}
readonly priority: number = 1;
constructor(
view: ViewsWithCommits | StashesView,
protected override parent: ViewNode,
public readonly root: HierarchicalItem<FileNode>,
public readonly repoPath: string,
public readonly folderName: string,
public readonly root: HierarchicalItem<FileNode>,
public readonly relativePath: string | undefined,
private readonly containsWorkingFiles?: boolean,
public readonly relativePath?: string,
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
}
override toClipboard(): string {
return this.folderName;
this._uniqueId = getViewNodeId(`folder+${relativePath ?? folderName}`, this.context);
}
override get id(): string {
return FolderNode.getId(this.parent, this.folderName);
return this._uniqueId;
}
override toClipboard(): string {
return this.folderName;
}
getChildren(): (FolderNode | FileNode)[] {
@ -68,11 +65,11 @@ export class FolderNode extends ViewNode {
new FolderNode(
this.view,
this.folderName ? this : this.parent,
folder,
this.repoPath,
folder.name,
folder,
this.containsWorkingFiles,
folder.relativePath,
this.containsWorkingFiles,
),
);
continue;

+ 15
- 13
src/views/nodes/lineHistoryNode.ts View File

@ -19,21 +19,13 @@ import { LoadMoreNode, MessageNode } from './common';
import { FileRevisionAsCommitNode } from './fileRevisionAsCommitNode';
import { insertDateMarkers } from './helpers';
import { LineHistoryTrackerNode } from './lineHistoryTrackerNode';
import { RepositoryNode } from './repositoryNode';
import type { PageableViewNode, ViewNode } from './viewNode';
import { ContextValues, SubscribeableViewNode } from './viewNode';
import { ContextValues, getViewNodeId, SubscribeableViewNode } from './viewNode';
export class LineHistoryNode
extends SubscribeableViewNode<FileHistoryView | LineHistoryView>
implements PageableViewNode
{
static key = ':history:line';
static getId(repoPath: string, uri: string, selection: Selection): string {
return `${RepositoryNode.getId(repoPath)}${this.key}(${uri}[${selection.start.line},${
selection.start.character
}-${selection.end.line},${selection.end.character}])`;
}
protected override splatted = true;
constructor(
@ -45,14 +37,24 @@ export class LineHistoryNode
private readonly editorContents: string | undefined,
) {
super(uri, view, parent);
}
override toClipboard(): string {
return this.uri.fileName;
if (branch != null) {
this.updateContext({ branch: branch });
}
this._uniqueId = getViewNodeId(
`file-history+${uri.toString()}+[${selection.start.line},${selection.start.character}-${
selection.end.line
},${selection.end.character}]`,
this.context,
);
}
override get id(): string {
return LineHistoryNode.getId(this.uri.repoPath!, this.uri.toString(true), this.selection);
return this._uniqueId;
}
override toClipboard(): string {
return this.uri.fileName;
}
async getChildren(): Promise<ViewNode[]> {

+ 4
- 17
src/views/nodes/mergeStatusNode.ts View File

@ -10,18 +10,12 @@ import { makeHierarchical } from '../../system/array';
import { joinPaths, normalizePath } from '../../system/path';
import { pluralize, sortCompare } from '../../system/string';
import type { ViewsWithCommits } from '../viewBase';
import { BranchNode } from './branchNode';
import type { FileNode } from './folderNode';
import { FolderNode } from './folderNode';
import { MergeConflictFileNode } from './mergeConflictFileNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class MergeStatusNode extends ViewNode<ViewsWithCommits> {
static key = ':merge';
static getId(repoPath: string, name: string, root: boolean, workspaceId?: string): string {
return `${BranchNode.getId(repoPath, name, root, workspaceId)}${this.key}`;
}
constructor(
view: ViewsWithCommits,
protected override readonly parent: ViewNode,
@ -30,18 +24,11 @@ export class MergeStatusNode extends ViewNode {
public readonly status: GitStatus | undefined,
// Specifies that the node is shown as a root
public readonly root: boolean,
private readonly options?: { workspaceId?: string },
) {
super(GitUri.fromRepoPath(mergeStatus.repoPath), view, parent);
}
override get id(): string {
return MergeStatusNode.getId(
this.mergeStatus.repoPath,
this.mergeStatus.current.name,
this.root,
this.options?.workspaceId,
);
this.updateContext({ branch: branch, root: root });
this._uniqueId = getViewNodeId('merge-status', this.context);
}
get repoPath(): string {
@ -63,7 +50,7 @@ export class MergeStatusNode extends ViewNode {
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy);
const root = new FolderNode(this.view, this, hierarchy, this.repoPath, '', undefined);
children = root.getChildren() as FileNode[];
} else {
children.sort((a, b) => sortCompare(a.label!, b.label!));

+ 19
- 20
src/views/nodes/pullRequestNode.ts View File

@ -1,26 +1,18 @@
import { MarkdownString, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GitUri } from '../../git/gitUri';
import type { GitBranch } from '../../git/models/branch';
import { GitBranch } from '../../git/models/branch';
import type { GitCommit } from '../../git/models/commit';
import { isCommit } from '../../git/models/commit';
import { PullRequest, PullRequestState } from '../../git/models/pullRequest';
import type { ViewsWithCommits } from '../viewBase';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class PullRequestNode extends ViewNode<ViewsWithCommits> {
static key = ':pullrequest';
static getId(parent: ViewNode, id: string, ref?: string): string {
return `${parent.id}${this.key}(${id}):${ref}`;
}
public readonly pullRequest: PullRequest;
private readonly branchOrCommit?: GitBranch | GitCommit;
private readonly repoPath: string;
readonly repoPath: string;
constructor(
view: ViewsWithCommits,
protected override readonly parent: ViewNode,
pullRequest: PullRequest,
public readonly pullRequest: PullRequest,
branchOrCommitOrRepoPath: GitBranch | GitCommit | string,
) {
let branchOrCommit;
@ -34,17 +26,24 @@ export class PullRequestNode extends ViewNode {
super(GitUri.fromRepoPath(repoPath), view, parent);
this.branchOrCommit = branchOrCommit;
this.pullRequest = pullRequest;
if (branchOrCommit != null) {
if (branchOrCommit instanceof GitBranch) {
this.updateContext({ branch: branchOrCommit });
} else {
this.updateContext({ commit: branchOrCommit });
}
}
this._uniqueId = getViewNodeId('pullrequest', this.context);
this.repoPath = repoPath;
}
override toClipboard(): string {
return this.pullRequest.url;
override get id(): string {
return this._uniqueId;
}
override get id(): string {
return PullRequestNode.getId(this.parent, this.pullRequest.id, this.branchOrCommit?.ref);
override toClipboard(): string {
return this.pullRequest.url;
}
getChildren(): ViewNode[] {
@ -62,9 +61,9 @@ export class PullRequestNode extends ViewNode {
tooltip.supportHtml = true;
tooltip.isTrusted = true;
if (isCommit(this.branchOrCommit)) {
if (this.context.commit != null) {
tooltip.appendMarkdown(
`Commit \`$(git-commit) ${this.branchOrCommit.shortSha}\` was introduced by $(git-pull-request) PR #${this.pullRequest.id}\n\n`,
`Commit \`$(git-commit) ${this.context.commit.shortSha}\` was introduced by $(git-pull-request) PR #${this.pullRequest.id}\n\n`,
);
}

+ 5
- 18
src/views/nodes/rebaseStatusNode.ts View File

@ -19,19 +19,13 @@ import { joinPaths, normalizePath } from '../../system/path';
import { getSettledValue } from '../../system/promise';
import { pluralize, sortCompare } from '../../system/string';
import type { ViewsWithCommits } from '../viewBase';
import { BranchNode } from './branchNode';
import { CommitFileNode } from './commitFileNode';
import type { FileNode } from './folderNode';
import { FolderNode } from './folderNode';
import { MergeConflictFileNode } from './mergeConflictFileNode';
import { ContextValues, ViewNode, ViewRefNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode, ViewRefNode } from './viewNode';
export class RebaseStatusNode extends ViewNode<ViewsWithCommits> {
static key = ':rebase';
static getId(repoPath: string, name: string, root: boolean, workspaceId?: string): string {
return `${BranchNode.getId(repoPath, name, root, workspaceId)}${this.key}`;
}
constructor(
view: ViewsWithCommits,
protected override readonly parent: ViewNode,
@ -40,18 +34,11 @@ export class RebaseStatusNode extends ViewNode {
public readonly status: GitStatus | undefined,
// Specifies that the node is shown as a root
public readonly root: boolean,
private readonly options?: { workspaceId?: string },
) {
super(GitUri.fromRepoPath(rebaseStatus.repoPath), view, parent);
}
override get id(): string {
return RebaseStatusNode.getId(
this.rebaseStatus.repoPath,
this.rebaseStatus.incoming.name,
this.root,
this.options?.workspaceId,
);
this.updateContext({ branch: branch, root: root });
this._uniqueId = getViewNodeId('merge-status', this.context);
}
get repoPath(): string {
@ -70,7 +57,7 @@ export class RebaseStatusNode extends ViewNode {
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy);
const root = new FolderNode(this.view, this, hierarchy, this.repoPath, '', undefined);
children = root.getChildren() as FileNode[];
} else {
children.sort((a, b) => sortCompare(a.label!, b.label!));
@ -157,7 +144,7 @@ export class RebaseCommitNode extends ViewRefNode
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy);
const root = new FolderNode(this.view, this, hierarchy, this.repoPath, '', undefined);
children = root.getChildren() as FileNode[];
} else {
children.sort((a, b) => sortCompare(a.label!, b.label!));

+ 7
- 13
src/views/nodes/reflogNode.ts View File

@ -8,34 +8,28 @@ import type { RepositoriesView } from '../repositoriesView';
import type { WorkspacesView } from '../workspacesView';
import { LoadMoreNode, MessageNode } from './common';
import { ReflogRecordNode } from './reflogRecordNode';
import { RepositoryNode } from './repositoryNode';
import type { PageableViewNode } from './viewNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class ReflogNode extends ViewNode<RepositoriesView | WorkspacesView> implements PageableViewNode {
static key = ':reflog';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
private _children: ViewNode[] | undefined;
constructor(
uri: GitUri,
view: RepositoriesView | WorkspacesView,
parent: ViewNode,
public readonly repo: Repository,
private readonly options?: {
workspaceId?: string;
},
) {
super(uri, view, parent);
this.updateContext({ repository: repo });
this._uniqueId = getViewNodeId('reflog', this.context);
}
override get id(): string {
return ReflogNode.getId(this.repo.path, this.options?.workspaceId);
return this._uniqueId;
}
private _children: ViewNode[] | undefined;
async getChildren(): Promise<ViewNode[]> {
if (this._children === undefined) {
const children = [];

+ 5
- 24
src/views/nodes/reflogRecordNode.ts View File

@ -9,38 +9,19 @@ import { map } from '../../system/iterable';
import type { ViewsWithCommits } from '../viewBase';
import { CommitNode } from './commitNode';
import { LoadMoreNode, MessageNode } from './common';
import { RepositoryNode } from './repositoryNode';
import type { PageableViewNode } from './viewNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class ReflogRecordNode extends ViewNode<ViewsWithCommits> implements PageableViewNode {
static key = ':reflog-record';
static getId(
repoPath: string,
sha: string,
selector: string,
command: string,
commandArgs: string | undefined,
date: Date,
): string {
return `${RepositoryNode.getId(repoPath)}${this.key}(${sha}|${selector}|${command}|${
commandArgs ?? ''
}|${date.getTime()})`;
}
constructor(view: ViewsWithCommits, parent: ViewNode, public readonly record: GitReflogRecord) {
super(GitUri.fromRepoPath(record.repoPath), view, parent);
this.updateContext({ reflog: record });
this._uniqueId = getViewNodeId('reflog-record', this.context);
}
override get id(): string {
return ReflogRecordNode.getId(
this.uri.repoPath!,
this.record.sha,
this.record.selector,
this.record.command,
this.record.commandArgs,
this.record.date,
);
return this._uniqueId;
}
async getChildren(): Promise<ViewNode[]> {

+ 13
- 17
src/views/nodes/remoteNode.ts View File

@ -11,32 +11,32 @@ import type { ViewsWithRemotes } from '../viewBase';
import { BranchNode } from './branchNode';
import { BranchOrTagFolderNode } from './branchOrTagFolderNode';
import { MessageNode } from './common';
import { RepositoryNode } from './repositoryNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class RemoteNode extends ViewNode<ViewsWithRemotes> {
static key = ':remote';
static getId(repoPath: string, name: string, id: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}(${name}|${id})`;
}
constructor(
uri: GitUri,
view: ViewsWithRemotes,
protected override readonly parent: ViewNode,
public readonly remote: GitRemote,
public readonly repo: Repository,
private readonly options?: { workspaceId?: string },
public readonly remote: GitRemote,
) {
super(uri, view, parent);
this.updateContext({ repository: repo, remote: remote });
this._uniqueId = getViewNodeId('remote', this.context);
}
override get id(): string {
return this._uniqueId;
}
override toClipboard(): string {
return this.remote.name;
}
override get id(): string {
return RemoteNode.getId(this.remote.repoPath, this.remotepan>.name, this.remote.id, this.options?.workspaceId);
get repoPath(): string {
return this.repo.path;
}
async getChildren(): Promise<ViewNode[]> {
@ -50,10 +50,9 @@ export class RemoteNode extends ViewNode {
// TODO@eamodio handle paging
const branchNodes = branches.values.map(
b =>
new BranchNode(GitUri.fromRepoPath(this.uri.repoPath!, b.ref), this.view, this, b, false, {
new BranchNode(GitUri.fromRepoPath(this.uri.repoPath!, b.ref), this.view, this, this.repo, b, false, {
showComparison: false,
showTracking: false,
workspaceId: this.options?.workspaceId,
}),
);
if (this.view.config.branches.layout === ViewBranchesLayout.List) return branchNodes;
@ -73,13 +72,10 @@ export class RemoteNode extends ViewNode {
this.view,
this,
'remote-branch',
hierarchy,
this.repo.path,
'',
undefined,
hierarchy,
`remote(${this.remote.name})`,
undefined,
{ workspaceId: this.options?.workspaceId },
);
const children = root.getChildren();
return children;

+ 9
- 18
src/views/nodes/remotesNode.ts View File

@ -6,37 +6,31 @@ import { debug } from '../../system/decorators/log';
import type { ViewsWithRemotesNode } from '../viewBase';
import { MessageNode } from './common';
import { RemoteNode } from './remoteNode';
import { RepositoryNode } from './repositoryNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class RemotesNode extends ViewNode<ViewsWithRemotesNode> {
static key = ':remotes';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
private _children: ViewNode[] | undefined;
constructor(
uri: GitUri,
view: ViewsWithRemotesNode,
parent: ViewNode,
protected override readonly parent: ViewNode,
public readonly repo: Repository,
private readonly options?: {
workspaceId?: string;
},
) {
super(uri, view, parent);
this.updateContext({ repository: repo });
this._uniqueId = getViewNodeId('remotes', this.context);
}
override get id(): string {
return RemotesNode.getId(this.repo.path, this.options?.workspaceId);
return this._uniqueId;
}
get repoPath(): string {
return this.repo.path;
}
private _children: ViewNode[] | undefined;
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const remotes = await this.repo.getRemotes({ sort: true });
@ -44,10 +38,7 @@ export class RemotesNode extends ViewNode {
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, { workspaceId: this.options?.workspaceId }),
);
this._children = remotes.map(r => new RemoteNode(this.uri, this.view, this, this.repo, r));
}
return this._children;

+ 36
- 89
src/views/nodes/repositoryNode.ts View File

@ -33,16 +33,11 @@ import { RemotesNode } from './remotesNode';
import { StashesNode } from './stashesNode';
import { StatusFilesNode } from './statusFilesNode';
import { TagsNode } from './tagsNode';
import type { ViewNode } from './viewNode';
import { ContextValues, SubscribeableViewNode } from './viewNode';
import type { AmbientContext, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, SubscribeableViewNode } from './viewNode';
import { WorktreesNode } from './worktreesNode';
export class RepositoryNode extends SubscribeableViewNode<ViewsWithRepositories> {
static key = ':repository';
static getId(repoPath: string, workspaceId?: string): string {
return `gitlens${this.key}(${repoPath})${workspaceId != null ? `(${workspaceId})` : ''}`;
}
private _children: ViewNode[] | undefined;
private _status: Promise<GitStatus | undefined>;
@ -51,33 +46,34 @@ export class RepositoryNode extends SubscribeableViewNode
view: ViewsWithRepositories,
parent: ViewNode,
public readonly repo: Repository,
private readonly options?: {
workspace?: CloudWorkspace | LocalWorkspace;
workspaceRepoDescriptor: CloudWorkspaceRepositoryDescriptor | LocalWorkspaceRepositoryDescriptor;
},
context?: AmbientContext,
) {
super(uri, view, parent);
this.updateContext({ ...context, repository: this.repo });
this._uniqueId = getViewNodeId('repository', this.context);
this._status = this.repo.getStatus();
}
override get id(): string {
return this._uniqueId;
}
override toClipboard(): string {
return this.repo.path;
}
override get id(): string {
return RepositoryNode.getId(this.repo.path, this.options?.workspace?.id);
get repoPath(): string {
return this.repo.path;
}
get workspaceId(): string | undefined {
return this.options?.workspace?.id;
get workspace(): CloudWorkspace | LocalWorkspace | undefined {
return this.context.workspace;
}
get workspaceRepositoryDescriptor():
| CloudWorkspaceRepositoryDescriptor
| LocalWorkspaceRepositoryDescriptor
| undefined {
return this.options?.workspaceRepoDescriptor;
get wsRepositoryDescriptor(): CloudWorkspaceRepositoryDescriptor | LocalWorkspaceRepositoryDescriptor | undefined {
return this.context.wsRepositoryDescriptor;
}
async getChildren(): Promise<ViewNode[]> {
@ -109,7 +105,6 @@ export class RepositoryNode extends SubscribeableViewNode
branch,
this.view.config.showBranchComparison,
true,
{ workspaceId: this.options?.workspace?.id },
),
);
}
@ -120,31 +115,17 @@ export class RepositoryNode extends SubscribeableViewNode
]);
if (mergeStatus != null) {
children.push(
new MergeStatusNode(this.view, this, branch, mergeStatus, status, true, {
workspaceId: this.options?.workspace?.id,
}),
);
children.push(new MergeStatusNode(this.view, this, branch, mergeStatus, status, true));
} else if (rebaseStatus != null) {
children.push(
new RebaseStatusNode(this.view, this, branch, rebaseStatus, status, true, {
workspaceId: this.options?.workspace?.id,
}),
);
children.push(new RebaseStatusNode(this.view, this, branch, rebaseStatus, status, true));
} else if (this.view.config.showUpstreamStatus) {
if (status.upstream) {
if (!status.state.behind && !status.state.ahead) {
children.push(
new BranchTrackingStatusNode(this.view, this, branch, status, 'same', true, {
workspaceId: this.options?.workspace?.id,
}),
);
children.push(new BranchTrackingStatusNode(this.view, this, branch, status, 'same', true));
} else {
if (status.state.behind) {
children.push(
new BranchTrackingStatusNode(this.view, this, branch, status, 'behind', true, {
workspaceId: this.options?.workspace?.id,
}),
new BranchTrackingStatusNode(this.view, this, branch, status, 'behind', true),
);
}
@ -152,27 +133,18 @@ export class RepositoryNode extends SubscribeableViewNode
children.push(
new BranchTrackingStatusNode(this.view, this, branch, status, 'ahead', true, {
showAheadCommits: true,
workspaceId: this.options?.workspace?.id,
}),
);
}
}
} else {
children.push(
new BranchTrackingStatusNode(this.view, this, branch, status, 'none', true, {
workspaceId: this.options?.workspace?.id,
}),
);
children.push(new BranchTrackingStatusNode(this.view, this, branch, status, 'none', true));
}
}
if (this.view.config.includeWorkingTree && status.files.length !== 0) {
const range = undefined; //status.upstream ? createRange(status.upstream, branch.ref) : undefined;
children.push(
new StatusFilesNode(this.view, this, status, range, {
workspaceId: this.options?.workspace?.id,
}),
);
children.push(new StatusFilesNode(this.view, this, status, range));
}
if (children.length !== 0 && !this.view.config.compact) {
@ -181,64 +153,43 @@ export class RepositoryNode extends SubscribeableViewNode
if (this.view.config.showCommits) {
children.push(
new BranchNode(this.uri, this.view, this, branch, true, {
new BranchNode(this.uri, this.view, this, this.repo, branch, true, {
showAsCommits: true,
showComparison: false,
showCurrent: false,
showStatus: false,
showTracking: false,
workspaceId: this.options?.workspace?.id,
}),
);
}
}
if (this.view.config.showBranches) {
children.push(
new BranchesNode(this.uri, this.view, this, this.repo, {
workspaceId: this.options?.workspace?.id,
}),
);
children.push(new BranchesNode(this.uri, this.view, this, this.repo));
}
if (this.view.config.showRemotes) {
children.push(
new RemotesNode(this.uri, this.view, this, this.repo, { workspaceId: this.options?.workspace?.id }),
);
children.push(new RemotesNode(this.uri, this.view, this, this.repo));
}
if (this.view.config.showStashes && (await this.repo.supports(Features.Stashes))) {
children.push(
new StashesNode(this.uri, this.view, this, this.repo, { workspaceId: this.options?.workspace?.id }),
);
children.push(new StashesNode(this.uri, this.view, this, this.repo));
}
if (this.view.config.showTags) {
children.push(
new TagsNode(this.uri, this.view, this, this.repo, { workspaceId: this.options?.workspace?.id }),
);
children.push(new TagsNode(this.uri, this.view, this, this.repo));
}
if (this.view.config.showWorktrees && (await this.repo.supports(Features.Worktrees))) {
children.push(
new WorktreesNode(this.uri, this.view, this, this.repo, {
workspaceId: this.options?.workspace?.id,
}),
);
children.push(new WorktreesNode(this.uri, this.view, this, this.repo));
}
if (this.view.config.showContributors) {
children.push(
new ContributorsNode(this.uri, this.view, this, this.repo, {
workspaceId: this.options?.workspace?.id,
}),
);
children.push(new ContributorsNode(this.uri, this.view, this, this.repo));
}
if (this.view.config.showIncomingActivity && !this.repo.provider.virtual) {
children.push(
new ReflogNode(this.uri, this.view, this, this.repo, { workspaceId: this.options?.workspace?.id }),
);
children.push(new ReflogNode(this.uri, this.view, this, this.repo));
}
this._children = children;
@ -264,11 +215,11 @@ export class RepositoryNode extends SubscribeableViewNode
if (this.repo.starred) {
contextValue += '+starred';
}
if (this.options?.workspace) {
if (this.context.workspace != null) {
contextValue += '+workspace';
if (this.options.workspace.type === WorkspaceType.Cloud) {
if (this.context.workspace.type === WorkspaceType.Cloud) {
contextValue += '+cloud';
} else if (this.options.workspace.type === WorkspaceType.Local) {
} else if (this.context.workspace.type === WorkspaceType.Local) {
contextValue += '+local';
}
if (this.repo.closed) {
@ -340,7 +291,7 @@ export class RepositoryNode extends SubscribeableViewNode
const item = new TreeItem(
label,
this.options?.workspace ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.Expanded,
this.context.workspace != null ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.Expanded,
);
item.id = this.id;
item.contextValue = contextValue;
@ -352,7 +303,7 @@ export class RepositoryNode extends SubscribeableViewNode
light: this.view.container.context.asAbsolutePath(`images/light/icon-repo${iconSuffix}.svg`),
};
if (this.options?.workspace != null && !this.repo.closed) {
if (this.context.workspace != null && !this.repo.closed) {
item.resourceUri = Uri.parse(`gitlens-view://workspaces/repository/open`);
}
@ -470,11 +421,7 @@ export class RepositoryNode extends SubscribeableViewNode
}
const range = undefined; //status.upstream ? createRange(status.upstream, status.sha) : undefined;
this._children.splice(
index,
deleteCount,
new StatusFilesNode(this.view, this, status, range, { workspaceId: this.options?.workspace?.id }),
);
this._children.splice(index, deleteCount, new StatusFilesNode(this.view, this, status, range));
} else if (index !== -1) {
this._children.splice(index, 1);
}

+ 8
- 7
src/views/nodes/resultsCommitsNode.ts View File

@ -16,7 +16,7 @@ import type { FilesQueryResults } from './resultsFilesNode';
import { ResultsFilesNode } from './resultsFilesNode';
import { StashNode } from './stashNode';
import type { PageableViewNode } from './viewNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export interface CommitsQueryResults {
readonly label: string;
@ -49,15 +49,20 @@ export class ResultsCommitsNode
id?: string;
description?: string;
expand?: boolean;
} = {},
} = undefined!,
splatted?: boolean,
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
this._uniqueId = getViewNodeId(`results-commits${_options?.id ? `+${_options.id}` : ''}`, this.context);
this._options = { expand: true, ..._options };
if (splatted != null) {
this.splatted = splatted;
}
this._options = { expand: true, ..._options };
}
override get id(): string {
return this._uniqueId;
}
get ref1(): string | undefined {
@ -68,10 +73,6 @@ export class ResultsCommitsNode
return this._results.comparison?.ref2;
}
override get id(): string {
return `${this.parent.id}:results:commits${this._options.id ? `:${this._options.id}` : ''}`;
}
async getChildren(): Promise<ViewNode[]> {
const { log } = await this.getCommitsQueryResults();
if (log == null) return [];

+ 5
- 4
src/views/nodes/resultsFilesNode.ts View File

@ -14,7 +14,7 @@ import type { ViewsWithCommits } from '../viewBase';
import type { FileNode } from './folderNode';
import { FolderNode } from './folderNode';
import { ResultsFileNode } from './resultsFileNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export enum FilesQueryFilter {
Left = 0,
@ -40,15 +40,16 @@ export class ResultsFilesNode extends ViewNode {
private readonly direction: 'ahead' | 'behind' | undefined,
private readonly _options: {
expand?: boolean;
} = {},
} = undefined!,
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
this._uniqueId = getViewNodeId(`results-files${direction ? `+${direction}` : ''}`, this.context);
this._options = { expand: true, ..._options };
}
override get id(): string {
return `${this.parent.id}:results:files`;
return this._uniqueId;
}
get filter(): FilesQueryFilter | undefined {
@ -98,7 +99,7 @@ export class ResultsFilesNode extends ViewNode {
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy);
const root = new FolderNode(this.view, this, hierarchy, this.repoPath, '', undefined);
children = root.getChildren() as FileNode[];
} else {
children.sort((a, b) => a.priority - b.priority || sortCompare(a.label!, b.label!));

+ 5
- 10
src/views/nodes/searchResultsNode.ts View File

@ -10,11 +10,10 @@ import { gate } from '../../system/decorators/gate';
import { debug, log } from '../../system/decorators/log';
import { pluralize } from '../../system/string';
import type { SearchAndCompareView } from '../searchAndCompareView';
import { RepositoryNode } from './repositoryNode';
import type { CommitsQueryResults } from './resultsCommitsNode';
import { ResultsCommitsNode } from './resultsCommitsNode';
import type { PageableViewNode } from './viewNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
let instanceId = 0;
@ -26,13 +25,6 @@ interface SearchQueryResults {
}
export class SearchResultsNode extends ViewNode<SearchAndCompareView> implements PageableViewNode {
static key = ':search-results';
static getId(repoPath: string, search: SearchQuery | undefined, instanceId: number): string {
return `${RepositoryNode.getId(repoPath)}${this.key}(${
search == null ? '?' : getSearchQueryComparisonKey(search)
}):${instanceId}`;
}
static getPinnableId(repoPath: string, search: SearchQuery | StoredSearchQuery) {
return md5(`${repoPath}|${getSearchQueryComparisonKey(search)}`, 'base64');
}
@ -65,10 +57,13 @@ export class SearchResultsNode extends ViewNode implements
this._search = search;
this._instanceId = instanceId++;
this._order = Date.now();
this.updateContext({ searchId: `${getSearchQueryComparisonKey(search)}++${this._instanceId}` });
this._uniqueId = getViewNodeId('search-results', this.context);
}
override get id(): string {
return SearchResultsNode.getId(this.repoPath, this.search, this._instanceId);
return this._uniqueId;
}
get canDismiss(): boolean {

+ 11
- 17
src/views/nodes/stashNode.ts View File

@ -10,35 +10,29 @@ import { sortCompare } from '../../system/string';
import type { ViewsWithStashes } from '../viewBase';
import type { FileNode } from './folderNode';
import { FolderNode } from './folderNode';
import { RepositoryNode } from './repositoryNode';
import { StashFileNode } from './stashFileNode';
import type { ViewNode } from './viewNode';
import { ContextValues, ViewRefNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewRefNode } from './viewNode';
export class StashNode extends ViewRefNode<ViewsWithStashes, GitStashReference> {
static key = ':stash';
static getId(repoPath: string, ref: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}(${ref})`;
}
constructor(
view: ViewsWithStashes,
parent: ViewNode,
protected override parent: ViewNode,
public readonly commit: GitStashCommit,
private readonly options?: {
icon?: boolean;
workspaceId?: string;
},
private readonly options?: { icon?: boolean },
) {
super(commit.getGitUri(), view, parent);
}
override toClipboard(): string {
return this.commit.stashName;
this.updateContext({ commit: commit });
this._uniqueId = getViewNodeId('stash', this.context);
}
override get id(): string {
return StashNode.getId(this.commit.repoPath, this.commit.sha, this.options?.workspaceId);
return this._uniqueId;
}
override toClipboard(): string {
return this.commit.stashName;
}
get ref(): GitStashReference {
@ -58,7 +52,7 @@ export class StashNode extends ViewRefNode
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy);
const root = new FolderNode(this.view, this, hierarchy, this.repoPath, '', undefined);
children = root.getChildren() as FileNode[];
} else {
children.sort((a, b) => sortCompare(a.label!, b.label!));

+ 13
- 20
src/views/nodes/stashesNode.ts View File

@ -6,45 +6,38 @@ import { debug } from '../../system/decorators/log';
import { map } from '../../system/iterable';
import type { ViewsWithStashesNode } from '../viewBase';
import { MessageNode } from './common';
import { RepositoryNode } from './repositoryNode';
import { StashNode } from './stashNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class StashesNode extends ViewNode<ViewsWithStashesNode> {
static key = ':stashes';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
private _children: ViewNode[] | undefined;
constructor(
uri: GitUri,
view: ViewsWithStashesNode,
parent: ViewNode,
protected override parent: ViewNode,
public readonly repo: Repository,
private readonly options?: {
workspaceId?: string;
},
) {
super(uri, view, parent);
this.updateContext({ repository: repo });
this._uniqueId = getViewNodeId('stashes', this.context);
}
override get id(): string {
return StashesNode.getId(this.repo.path, this.options?.workspaceId);
return this._uniqueId;
}
get repoPath(): string {
return this.repo.path;
}
private _children: ViewNode[] | undefined;
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const stash = await this.repo.getStash();
if (stash == null) return [new MessageNode(this.view, this, 'No stashes could be found.')];
this._children = [
...map(
stash.commits.values(),
c => new StashNode(this.view, this, c, { workspaceId: this.options?.workspaceId }),
),
];
this._children = [...map(stash.commits.values(), c => new StashNode(this.view, this, c))];
}
return this._children;

+ 9
- 15
src/views/nodes/statusFilesNode.ts View File

@ -14,18 +14,10 @@ import type { ViewsWithWorkingTree } from '../viewBase';
import { WorktreesView } from '../worktreesView';
import type { FileNode } from './folderNode';
import { FolderNode } from './folderNode';
import { RepositoryNode } from './repositoryNode';
import { StatusFileNode } from './statusFileNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class StatusFilesNode extends ViewNode<ViewsWithWorkingTree> {
static key = ':status-files';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
readonly repoPath: string;
constructor(
view: ViewsWithWorkingTree,
protected override readonly parent: ViewNode,
@ -38,16 +30,18 @@ export class StatusFilesNode extends ViewNode {
readonly upstream?: string;
},
public readonly range: string | undefined,
private readonly options?: {
workspaceId?: string;
},
) {
super(GitUri.fromRepoPath(status.repoPath), view, parent);
this.repoPath = status.repoPath;
this._uniqueId = getViewNodeId('status-files', this.context);
}
override get id(): string {
return StatusFilesNode.getId(this.repoPath, this.options?.workspaceId);
return this._uniqueId;
}
get repoPath(): string {
return this.status.repoPath;
}
async getChildren(): Promise<ViewNode[]> {
@ -109,7 +103,7 @@ export class StatusFilesNode extends ViewNode {
this.view.config.files.compact,
);
const root = new FolderNode(this.view, this, repoPath, '', hierarchy, true);
const root = new FolderNode(this.view, this, hierarchy, repoPath, '', undefined, true);
children = root.getChildren() as FileNode[];
} else {
children.sort((a, b) => a.priority - b.priority || sortCompare(a.label!, b.label!));

+ 8
- 19
src/views/nodes/tagNode.ts View File

@ -15,36 +15,25 @@ import type { ViewsWithTags } from '../viewBase';
import { CommitNode } from './commitNode';
import { LoadMoreNode, MessageNode } from './common';
import { insertDateMarkers } from './helpers';
import { RepositoryNode } from './repositoryNode';
import type { PageableViewNode, ViewNode } from './viewNode';
import { ContextValues, ViewRefNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewRefNode } from './viewNode';
export class TagNode extends ViewRefNode<ViewsWithTags, GitTagReference> implements PageableViewNode {
static key = ':tag';
static getId(repoPath: string, name: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}(${name})`;
constructor(uri: GitUri, view: ViewsWithTags, public override parent: ViewNode, public readonly tag: GitTag) {
super(uri, view, parent);
this.updateContext({ tag: tag });
this._uniqueId = getViewNodeId('tag', this.context);
}
constructor(
uri: GitUri,
view: ViewsWithTags,
public override parent: ViewNode,
public readonly tag: GitTag,
private readonly options?: {
workspaceId?: string;
},
) {
super(uri, view, parent);
override get id(): string {
return this._uniqueId;
}
override toClipboard(): string {
return this.tag.name;
}
override get id(): string {
return TagNode.getId(this.tag.repoPath, this.tag.name, this.options?.workspaceId);
}
get label(): string {
return this.view.config.branches.layout === ViewBranchesLayout.Tree ? this.tag.getBasename() : this.tag.name;
}

+ 9
- 27
src/views/nodes/tagsNode.ts View File

@ -8,36 +8,32 @@ import { debug } from '../../system/decorators/log';
import type { ViewsWithTagsNode } from '../viewBase';
import { BranchOrTagFolderNode } from './branchOrTagFolderNode';
import { MessageNode } from './common';
import { RepositoryNode } from './repositoryNode';
import { TagNode } from './tagNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class TagsNode extends ViewNode<ViewsWithTagsNode> {
static key = ':tags';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
private _children: ViewNode[] | undefined;
constructor(
uri: GitUri,
view: ViewsWithTagsNode,
protected override readonly parent: ViewNode,
public readonly repo: Repository,
private readonly options?: { workspaceId?: string },
) {
super(uri, view, parent);
this.updateContext({ repository: repo });
this._uniqueId = getViewNodeId('tags', this.context);
}
override get id(): string {
return TagsNode.getId(this.repo.path, this.options?.workspaceId);
return this._uniqueId;
}
get repoPath(): string {
return this.repo.path;
}
private _children: ViewNode[] | undefined;
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const tags = await this.repo.getTags({ sort: true });
@ -45,10 +41,7 @@ export class TagsNode extends ViewNode {
// TODO@eamodio handle paging
const tagNodes = tags.values.map(
t =>
new TagNode(GitUri.fromRepoPath(this.uri.repoPath!, t.ref), this.view, this, t, {
workspaceId: this.options?.workspaceId,
}),
t => new TagNode(GitUri.fromRepoPath(this.uri.repoPath!, t.ref), this.view, this, t),
);
if (this.view.config.branches.layout === ViewBranchesLayout.List) return tagNodes;
@ -59,18 +52,7 @@ export class TagsNode extends ViewNode {
this.view.config.files.compact,
);
const root = new BranchOrTagFolderNode(
this.view,
this,
'tag',
this.repo.path,
'',
undefined,
hierarchy,
'tags',
undefined,
{ workspaceId: this.options?.workspaceId },
);
const root = new BranchOrTagFolderNode(this.view, this, 'tag', hierarchy, this.repo.path, '', undefined);
this._children = root.getChildren();
}

+ 125
- 9
src/views/nodes/viewNode.ts View File

@ -4,19 +4,32 @@ import { GlyphChars } from '../../constants';
import type { RepositoriesChangeEvent } from '../../git/gitProviderService';
import type { GitUri } from '../../git/gitUri';
import { unknownGitUri } from '../../git/gitUri';
import type { GitBranch } from '../../git/models/branch';
import type { GitCommit } from '../../git/models/commit';
import type { GitContributor } from '../../git/models/contributor';
import type { GitFile } from '../../git/models/file';
import type { GitReference, GitRevisionReference } from '../../git/models/reference';
import { getReferenceLabel } from '../../git/models/reference';
import type { GitReflogRecord } from '../../git/models/reflog';
import { GitRemote } from '../../git/models/remote';
import type { RepositoryChangeEvent } from '../../git/models/repository';
import { Repository, RepositoryChange, RepositoryChangeComparisonMode } from '../../git/models/repository';
import type { GitTag } from '../../git/models/tag';
import type { GitWorktree } from '../../git/models/worktree';
import type { SubscriptionChangeEvent } from '../../plus/subscription/subscriptionService';
import type {
CloudWorkspace,
CloudWorkspaceRepositoryDescriptor,
LocalWorkspace,
LocalWorkspaceRepositoryDescriptor,
} from '../../plus/workspaces/models';
import { gate } from '../../system/decorators/gate';
import { debug, log, logName } from '../../system/decorators/log';
import { is as isA, szudzikPairing } from '../../system/function';
import { getLoggableName } from '../../system/logger';
import { pad } from '../../system/string';
import type { TreeViewNodeCollapsibleStateChangeEvent, View } from '../viewBase';
import type { BranchTrackingStatus } from './branchTrackingStatusNode';
export const enum ContextValues {
ActiveFileHistory = 'gitlens:history:active:file',
@ -84,16 +97,117 @@ export const enum ContextValues {
Worktrees = 'gitlens:worktrees',
}
export interface AmbientContext {
readonly autolinksId?: string;
readonly branch?: GitBranch;
readonly branchStatus?: BranchTrackingStatus;
readonly branchStatusUpstreamType?: 'ahead' | 'behind' | 'same' | 'none';
readonly commit?: GitCommit;
readonly comparisonId?: string;
readonly contributor?: GitContributor;
readonly file?: GitFile;
readonly reflog?: GitReflogRecord;
readonly remote?: GitRemote;
readonly repository?: Repository;
readonly root?: boolean;
readonly searchId?: string;
readonly tag?: GitTag;
readonly workspace?: CloudWorkspace | LocalWorkspace;
readonly wsRepositoryDescriptor?: CloudWorkspaceRepositoryDescriptor | LocalWorkspaceRepositoryDescriptor;
readonly worktree?: GitWorktree;
}
export function getViewNodeId(type: string, context: AmbientContext): string {
let uniqueness = '';
if (context.root) {
uniqueness += '/root';
}
if (context.workspace != null) {
uniqueness += `/ws/${context.workspace.id}`;
}
if (context.wsRepositoryDescriptor != null) {
uniqueness += `/wsrepo/${context.wsRepositoryDescriptor.id}`;
}
if (context.repository != null) {
uniqueness += `/repo/${context.repository.id}`;
}
if (context.worktree != null) {
uniqueness += `/worktree/${context.worktree.uri.path}`;
}
if (context.remote != null) {
uniqueness += `/remote/${context.remote.id}`;
}
if (context.tag != null) {
uniqueness += `/tag/${context.tag.id}`;
}
if (context.branch != null) {
uniqueness += `/branch/${context.branch.id}`;
}
if (context.branchStatus != null) {
uniqueness += `/status/${context.branchStatus.upstream ?? '-'}${
context.branchStatusUpstreamType ? `/${context.branchStatusUpstreamType}` : ''
}`;
}
if (context.reflog != null) {
uniqueness += `/reflog/${context.reflog.sha}+${context.reflog.selector}+${context.reflog.command}+${
context.reflog.commandArgs ?? ''
}+${context.reflog.date.getTime()}`;
}
if (context.contributor != null) {
uniqueness += `/contributor/${
context.contributor.id ??
`${context.contributor.username}+${context.contributor.email}+${context.contributor.name}`
}`;
}
if (context.autolinksId != null) {
uniqueness += `/autolinks/${context.autolinksId}`;
}
if (context.comparisonId != null) {
uniqueness += `/comparison/${context.comparisonId}`;
}
if (context.searchId != null) {
uniqueness += `/search/${context.searchId}`;
}
if (context.commit != null) {
uniqueness += `/commit/${context.commit.sha}`;
}
if (context.file != null) {
uniqueness += `/file/${context.file.path}+${context.file.status}`;
}
return `gitlens://viewnode/${type}${uniqueness}`;
}
@logName<ViewNode>((c, name) => `${name}${c.id != null ? `(${c.id})` : ''}`)
export abstract class ViewNode<TView extends View = View, State extends object = any> {
protected _uniqueId!: string;
protected splatted = false;
constructor(uri: GitUri, public readonly view: TView, protected parent?: ViewNode) {
constructor(
// public readonly id: string | undefined,
uri: GitUri,
public readonly view: TView,
protected parent?: ViewNode,
) {
this._uri = uri;
}
get id(): string | undefined {
return undefined;
return this._uniqueId;
}
private _context: AmbientContext | undefined;
protected get context(): AmbientContext {
return this._context ?? this.parent?.context ?? {};
}
protected updateContext(context: AmbientContext, reset: boolean = false) {
this._context = this.getNewContext(context, reset);
}
protected getNewContext(context: AmbientContext, reset: boolean = false) {
return { ...(reset ? this.parent?.context : this.context), ...context };
}
toClipboard?(): string;
@ -394,11 +508,6 @@ export abstract class RepositoryFolderNode<
TView extends View = View,
TChild extends ViewNode = ViewNode,
> extends SubscribeableViewNode<TView> {
static key = ':repository';
static getId(repoPath: string): string {
return `gitlens${this.key}(${repoPath})`;
}
protected override splatted = true;
protected child: TChild | undefined;
@ -412,15 +521,22 @@ export abstract class RepositoryFolderNode<
) {
super(uri, view, parent);
this.updateContext({ repository: this.repo });
this._uniqueId = getViewNodeId('repository-folder', this.context);
this.splatted = splatted;
}
override get id(): string {
return this._uniqueId;
}
override toClipboard(): string {
return this.repo.path;
}
override get id(): string {
return RepositoryFolderNode.getId(this.repo.path);
get repoPath(): string {
return this.repo.path;
}
async getTreeItem(): Promise<TreeItem> {

+ 18
- 20
src/views/nodes/workspaceMissingRepositoryNode.ts View File

@ -1,39 +1,41 @@
import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
import { unknownGitUri } from '../../git/gitUri';
import type {
CloudWorkspace,
CloudWorkspaceRepositoryDescriptor,
LocalWorkspace,
LocalWorkspaceRepositoryDescriptor,
} from '../../plus/workspaces/models';
import type { WorkspacesView } from '../workspacesView';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
export class WorkspaceMissingRepositoryNode extends ViewNode<WorkspacesView> {
static key = ':workspaceMissingRepository';
static getId(workspaceId: string, repoName: string): string {
return `gitlens${this.key}(${workspaceId}/${repoName})`;
}
constructor(
view: WorkspacesView,
parent: ViewNode,
public readonly workspaceId: string,
public readonly workspaceRepositoryDescriptor:
| CloudWorkspaceRepositoryDescriptor
| LocalWorkspaceRepositoryDescriptor,
public readonly workspace: CloudWorkspace | LocalWorkspace,
public readonly wsRepositoryDescriptor: CloudWorkspaceRepositoryDescriptor | LocalWorkspaceRepositoryDescriptor,
) {
super(unknownGitUri, view, parent);
this.updateContext({ wsRepositoryDescriptor: wsRepositoryDescriptor });
this._uniqueId = getViewNodeId('missing-workspace-repository', this.context);
}
override get id(): string {
return this._uniqueId;
}
override toClipboard(): string {
return this.name;
}
override get id(): string {
return WorkspaceMissingRepositoryNode.getId(this.workspaceId, this.workspaceRepositoryDescriptor.name);
get name(): string {
return this.wsRepositoryDescriptor.name;
}
get name(): string {
return this.workspaceRepositoryDescriptor.name;
get workspaceId(): string {
return this.wsRepositoryDescriptor.workspaceId;
}
getChildren(): ViewNode[] {
@ -41,16 +43,12 @@ export class WorkspaceMissingRepositoryNode extends ViewNode {
}
getTreeItem(): TreeItem {
const description = 'repo not found \u2022 please locate';
const icon: ThemeIcon = new ThemeIcon('question');
const item = new TreeItem(this.name, TreeItemCollapsibleState.None);
item.id = this.id;
item.description = description;
item.description = 'Unable to find repo, please locate';
item.tooltip = `${this.name} (missing)`;
item.contextValue = ContextValues.WorkspaceMissingRepository;
item.iconPath = icon;
item.iconPath = new ThemeIcon('question');
item.resourceUri = Uri.parse(`gitlens-view://workspaces/repository/missing`);
return item;
}

+ 18
- 13
src/views/nodes/workspaceNode.ts View File

@ -6,26 +6,28 @@ import { createCommand } from '../../system/command';
import type { WorkspacesView } from '../workspacesView';
import { CommandMessageNode, MessageNode } from './common';
import { RepositoryNode } from './repositoryNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
import { WorkspaceMissingRepositoryNode } from './workspaceMissingRepositoryNode';
export class WorkspaceNode extends ViewNode<WorkspacesView> {
static key = ':workspace';
static getId(workspaceId: string): string {
return `gitlens${this.key}(${workspaceId})`;
}
constructor(
uri: GitUri,
view: WorkspacesView,
parent: ViewNode,
protected override parent: ViewNode,
public readonly workspace: CloudWorkspace | LocalWorkspace,
) {
super(uri, view, parent);
this.updateContext({ workspace: workspace });
this._uniqueId = getViewNodeId('workspace', this.context);
}
override get id(): string {
return WorkspaceNode.getId(this.workspace.id);
return this._uniqueId;
}
override toClipboard(): string {
return this.workspace.name;
}
private _children: ViewNode[] | undefined;
@ -64,16 +66,19 @@ export class WorkspaceNode extends ViewNode {
const repo = reposByName.get(descriptor.name)?.repository;
if (!repo) {
this._children.push(
new WorkspaceMissingRepositoryNode(this.view, this, this.workspace.id, descriptor),
new WorkspaceMissingRepositoryNode(this.view, this, this.workspace, descriptor),
);
continue;
}
this._children.push(
new RepositoryNode(GitUri.fromRepoPath(repo.path), this.view, this, repo, {
workspace: this.workspace,
workspaceRepoDescriptor: descriptor,
}),
new RepositoryNode(
GitUri.fromRepoPath(repo.path),
this.view,
this,
repo,
this.getNewContext({ wsRepositoryDescriptor: descriptor }),
),
);
}
} catch (ex) {

+ 4
- 11
src/views/nodes/workspacesViewNode.ts View File

@ -1,4 +1,6 @@
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { gate } from '../../system/decorators/gate';
import { debug } from '../../system/decorators/log';
import type { WorkspacesView } from '../workspacesView';
import { MessageNode } from './common';
import { RepositoriesNode } from './repositoriesNode';
@ -6,17 +8,8 @@ import { ViewNode } from './viewNode';
import { WorkspaceNode } from './workspaceNode';
export class WorkspacesViewNode extends ViewNode<WorkspacesView> {
static key = ':workspaces';
static getId(): string {
return `gitlens${this.key}`;
}
private _children: (WorkspaceNode | MessageNode | RepositoriesNode)[] | undefined;
override get id(): string {
return WorkspacesViewNode.getId();
}
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
const children: (WorkspaceNode | MessageNode | RepositoriesNode)[] = [];
@ -56,12 +49,12 @@ export class WorkspacesViewNode extends ViewNode {
getTreeItem(): TreeItem {
const item = new TreeItem('Workspaces', TreeItemCollapsibleState.Expanded);
return item;
}
@gate()
@debug()
override refresh() {
this._children = undefined;
void this.getChildren();
}
}

+ 9
- 20
src/views/nodes/worktreeNode.ts View File

@ -21,9 +21,8 @@ import { LoadMoreNode, MessageNode } from './common';
import { CompareBranchNode } from './compareBranchNode';
import { insertDateMarkers } from './helpers';
import { PullRequestNode } from './pullRequestNode';
import { RepositoryNode } from './repositoryNode';
import { UncommittedFilesNode } from './UncommittedFilesNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
type State = {
pullRequest: PullRequest | null | undefined;
@ -31,11 +30,6 @@ type State = {
};
export class WorktreeNode extends ViewNode<ViewsWithWorktrees, State> {
static key = ':worktree';
static getId(repoPath: string, uri: Uri, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}(${uri.path})`;
}
private _branch: GitBranch | undefined;
constructor(
@ -43,19 +37,19 @@ export class WorktreeNode extends ViewNode {
view: ViewsWithWorktrees,
protected override readonly parent: ViewNode,
public readonly worktree: GitWorktree,
private readonly options?: {
workspaceId?: string;
},
) {
super(uri, view, parent);
}
override toClipboard(): string {
return this.worktree.uri.fsPath;
this.updateContext({ worktree: worktree });
this._uniqueId = getViewNodeId('worktree', this.context);
}
override get id(): string {
return WorktreeNode.getId(this.worktree.repoPath, this.worktree.uri, this.options?.workspaceId);
return this._uniqueId;
}
override toClipboard(): string {
return this.worktree.uri.fsPath;
}
get repoPath(): string {
@ -145,7 +139,6 @@ export class WorktreeNode extends ViewNode {
branch,
this.view.config.showBranchComparison,
this.splatted,
{ workspaceId: this.options?.workspaceId },
),
);
}
@ -182,11 +175,7 @@ export class WorktreeNode extends ViewNode {
const status = getSettledValue(statusResult);
if (status?.hasChanges) {
children.unshift(
new UncommittedFilesNode(this.view, this, status, undefined, {
workspaceId: this.options?.workspaceId,
}),
);
children.unshift(new UncommittedFilesNode(this.view, this, status, undefined));
}
this._children = children;

+ 7
- 15
src/views/nodes/worktreesNode.ts View File

@ -7,16 +7,10 @@ import { gate } from '../../system/decorators/gate';
import { debug } from '../../system/decorators/log';
import type { ViewsWithWorktreesNode } from '../viewBase';
import { MessageNode } from './common';
import { RepositoryNode } from './repositoryNode';
import { ContextValues, ViewNode } from './viewNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
import { WorktreeNode } from './worktreeNode';
export class WorktreesNode extends ViewNode<ViewsWithWorktreesNode> {
static key = ':worktrees';
static getId(repoPath: string, workspaceId?: string): string {
return `${RepositoryNode.getId(repoPath, workspaceId)}${this.key}`;
}
private _children: WorktreeNode[] | undefined;
constructor(
@ -24,15 +18,15 @@ export class WorktreesNode extends ViewNode {
view: ViewsWithWorktreesNode,
protected override readonly parent: ViewNode,
public readonly repo: Repository,
private readonly options?: {
workspaceId?: string;
},
) {
super(uri, view, parent);
this.updateContext({ repository: repo });
this._uniqueId = getViewNodeId('worktrees', this.context);
}
override get id(): string {
return WorktreesNode.getId(this.repo.path, this.options?.workspaceId);
return this._uniqueId;
}
get repoPath(): string {
@ -47,9 +41,7 @@ export class WorktreesNode extends ViewNode {
const worktrees = await this.repo.getWorktrees();
if (worktrees.length === 0) return [new MessageNode(this.view, this, 'No worktrees could be found.')];
this._children = worktrees.map(
c => new WorktreeNode(this.uri, this.view, this, c, { workspaceId: this.options?.workspaceId }),
);
this._children = worktrees.map(wt => new WorktreeNode(this.uri, this.view, this, wt));
}
return this._children;
@ -66,7 +58,7 @@ export class WorktreesNode extends ViewNode {
item.contextValue = ContextValues.Worktrees;
item.description = access.allowed
? undefined
: ` ${GlyphChars.Warning} Requires GitLens Pro to access Worktrees on private repos`;
: ` ${GlyphChars.Warning} Requires a trial or subscription for use on privately hosted repos`;
// TODO@eamodio `folder` icon won't work here for some reason
item.iconPath = new ThemeIcon('folder-opened');
return item;

+ 13
- 15
src/views/remotesView.ts View File

@ -98,7 +98,7 @@ export class RemotesView extends ViewBase {
protected readonly configKey = 'remotes';
constructor(container: Container) {
super(container, 'gitlens.views.remotes', 'Remotes', 'remotesView');
super(container, 'remotes', 'Remotes', 'remotesView');
}
override get canReveal(): boolean {
@ -189,7 +189,7 @@ export class RemotesView extends ViewBase {
findBranch(branch: GitBranchReference, token?: CancellationToken) {
if (!branch.remote) return undefined;
const repoNodeId = RepositoryNode.getId(branch.repoPath);
const { repoPath } = branch;
return this.findNode((n: any) => n.branch?.ref === branch.ref, {
allowPaging: true,
@ -198,13 +198,11 @@ export class RemotesView extends ViewBase {
if (n instanceof RemotesViewNode) return true;
if (n instanceof RemotesRepositoryNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
if (n instanceof RemoteNode) {
if (!n.id.startsWith(repoNodeId)) return false;
return n.remote.name === getRemoteNameFromBranchName(branch.name);
return n.repoPath === repoPath && n.remote.name === getRemoteNameFromBranchName(branch.name);
}
return false;
@ -214,7 +212,7 @@ export class RemotesView extends ViewBase {
}
async findCommit(commit: GitCommit | { repoPath: string; ref: string }, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(commit.repoPath);
const { repoPath } = commit;
// Get all the remote branches the commit is on
const branches = await this.container.git.getCommitBranches(
@ -226,26 +224,26 @@ export class RemotesView extends ViewBase {
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: 6,
canTraverse: n => {
if (n instanceof RemotesViewNode) return true;
if (n instanceof RemotesRepositoryNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
if (n instanceof RemoteNode) {
return n.id.startsWith(repoNodeId) && remotes.includes(n.remote.name);
return n.repoPath === repoPath && remotes.includes(n.remote.name);
}
if (n instanceof BranchNode) {
return n.id.startsWith(repoNodeId) && branches.includes(n.branch.name);
return n.repoPath === repoPath && branches.includes(n.branch.name);
}
if (n instanceof RepositoryNode || n instanceof RemotesNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -255,7 +253,7 @@ export class RemotesView extends ViewBase {
}
findRemote(remote: GitRemote, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(remote.repoPath);
const { repoPath } = remote;
return this.findNode((n: any) => n.remote?.name === remote.name, {
allowPaging: true,
@ -264,7 +262,7 @@ export class RemotesView extends ViewBase {
if (n instanceof RemotesViewNode) return true;
if (n instanceof RemotesRepositoryNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -362,7 +360,7 @@ export class RemotesView extends ViewBase {
repoPath: string,
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
) {
const node = await this.findNode(RepositoryFolderNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof RepositoryFolderNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof RemotesViewNode || n instanceof RepositoryFolderNode,
});

+ 47
- 55
src/views/repositoriesView.ts View File

@ -34,7 +34,6 @@ import { RemotesNode } from './nodes/remotesNode';
import { RepositoriesNode } from './nodes/repositoriesNode';
import { RepositoryNode } from './nodes/repositoryNode';
import { StashesNode } from './nodes/stashesNode';
import { StashNode } from './nodes/stashNode';
import { TagsNode } from './nodes/tagsNode';
import { WorktreeNode } from './nodes/worktreeNode';
import { WorktreesNode } from './nodes/worktreesNode';
@ -45,7 +44,7 @@ export class RepositoriesView extends ViewBase
protected readonly configKey = 'repositories';
constructor(container: Container) {
super(container, 'gitlens.views.repositories', 'Repositories', 'repositoriesView');
super(container, 'repositories', 'Repositories', 'repositoriesView');
}
private _onDidChangeAutoRefresh = new EventEmitter<void>();
@ -276,10 +275,10 @@ export class RepositoriesView extends ViewBase
}
findBranch(branch: GitBranchReference, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(branch.repoPath);
const { repoPath } = branch;
if (branch.remote) {
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,
canTraverse: n => {
@ -287,7 +286,7 @@ export class RepositoriesView extends ViewBase
if (n instanceof RepositoriesNode) return true;
if (n instanceof RemoteNode) {
if (!n.id.startsWith(repoNodeId)) return false;
if (n.repoPath !== repoPath) return false;
return branch.remote && n.remote.name === getRemoteNameFromBranchName(branch.name); //branch.getRemoteName();
}
@ -298,7 +297,7 @@ export class RepositoriesView extends ViewBase
n instanceof RemotesNode ||
n instanceof BranchOrTagFolderNode
) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -307,7 +306,7 @@ export class RepositoriesView extends ViewBase
});
}
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,
canTraverse: n => {
@ -315,7 +314,7 @@ export class RepositoriesView extends ViewBase
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode || n instanceof BranchesNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -325,7 +324,7 @@ export class RepositoriesView extends ViewBase
}
async findCommit(commit: GitCommit | { repoPath: string; ref: string }, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(commit.repoPath);
const { repoPath } = commit;
// Get all the branches the commit is on
let branches = await this.container.git.getCommitBranches(
@ -334,26 +333,24 @@ export class RepositoriesView extends ViewBase
isCommit(commit) ? { commitDate: commit.committer.date } : undefined,
);
if (branches.length !== 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: 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.loadMore({ until: commit.ref });
return true;
}
}
if (
n instanceof RepositoryNode ||
n instanceof BranchesNode ||
n instanceof BranchOrTagFolderNode
) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
if (n instanceof BranchNode && n.repoPath === repoPath && branches.includes(n.branch.name)) {
await n.loadMore({ until: commit.ref });
return true;
}
return false;
@ -372,7 +369,7 @@ export class RepositoriesView extends ViewBase
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 => {
@ -380,15 +377,15 @@ export class RepositoriesView extends ViewBase
if (n instanceof RepositoriesNode) return true;
if (n instanceof RemoteNode) {
return n.id.startsWith(repoNodeId) && remotes.includes(n.remote.name);
return n.repoPath === repoPath && remotes.includes(n.remote.name);
}
if (n instanceof BranchNode) {
return n.id.startsWith(repoNodeId) && branches.includes(n.branch.name);
return n.repoPath === repoPath && branches.includes(n.branch.name);
}
if (n instanceof RepositoryNode || n instanceof RemotesNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -398,10 +395,14 @@ export class RepositoriesView extends ViewBase
}
findContributor(contributor: GitContributor, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(contributor.repoPath);
const { repoPath, username, email, name } = contributor;
return this.findNode(
ContributorNode.getId(contributor.repoPath, contributor.name, contributor.email, contributor.username),
n =>
n instanceof ContributorNode &&
n.contributor.username === username &&
n.contributor.email === email &&
n.contributor.name === name,
{
maxDepth: 2,
canTraverse: n => {
@ -409,7 +410,7 @@ export class RepositoriesView extends ViewBase
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode || n instanceof ContributorsNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -420,7 +421,7 @@ export class RepositoriesView extends ViewBase
}
findRemote(remote: GitRemote, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(remote.repoPath);
const { repoPath } = remote;
return this.findNode((n: any) => n.remote?.name === remote.name, {
allowPaging: true,
@ -430,7 +431,7 @@ export class RepositoriesView extends ViewBase
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode || n instanceof RemotesNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -440,16 +441,16 @@ export class RepositoriesView extends ViewBase
}
findStash(stash: GitStashReference, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(stash.repoPath);
const { repoPath } = stash;
return this.findNode(StashNode.getId(stash.repoPath, stash.ref), {
return this.findNode((n: any) => n.commit?.ref === stash.ref, {
maxDepth: 3,
canTraverse: n => {
// Only search for stash nodes in the same repo within a StashesNode
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode || n instanceof StashesNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -459,9 +460,9 @@ export class RepositoriesView extends ViewBase
}
findTag(tag: GitTagReference, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(tag.repoPath);
const { repoPath } = tag;
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,
canTraverse: n => {
@ -469,7 +470,7 @@ export class RepositoriesView extends ViewBase
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode || n instanceof TagsNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -479,16 +480,17 @@ export class RepositoriesView extends ViewBase
}
findWorktree(worktree: GitWorktree, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(worktree.repoPath);
const { repoPath, uri } = worktree;
const url = uri.toString();
return this.findNode(WorktreeNode.getId(worktree.repoPath, worktree.uri), {
return this.findNode(n => n instanceof WorktreeNode && worktree.uri.toString() === url, {
maxDepth: 2,
canTraverse: n => {
// Only search for worktree nodes in the same repo within WorktreesNode
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode || n instanceof WorktreesNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -535,16 +537,14 @@ export class RepositoriesView extends ViewBase
expand?: boolean | number;
},
) {
const repoNodeId = RepositoryNode.getId(repoPath);
const node = await this.findNode(BranchesNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof BranchesNode && n.repoPath === repoPath, {
maxDepth: 2,
canTraverse: n => {
// Only search for branches nodes in the same repo
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -648,9 +648,7 @@ export class RepositoriesView extends ViewBase
expand?: boolean | number;
},
) {
const repoNodeId = RepositoryNode.getId(repoPath);
const node = await this.findNode(repoNodeId, {
const node = await this.findNode(n => n instanceof RepositoryNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof RepositoriesNode,
});
@ -700,16 +698,14 @@ export class RepositoriesView extends ViewBase
expand?: boolean | number;
},
) {
const repoNodeId = RepositoryNode.getId(repoPath);
const node = await this.findNode(StashesNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof StashesNode && n.repoPath === repoPath, {
maxDepth: 2,
canTraverse: n => {
// Only search for stashes nodes in the same repo
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -761,16 +757,14 @@ export class RepositoriesView extends ViewBase
expand?: boolean | number;
},
) {
const repoNodeId = RepositoryNode.getId(repoPath);
const node = await this.findNode(TagsNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof TagsNode && n.repoPath === repoPath, {
maxDepth: 2,
canTraverse: n => {
// Only search for tags nodes in the same repo
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -819,16 +813,14 @@ export class RepositoriesView extends ViewBase
expand?: boolean | number;
},
) {
const repoNodeId = RepositoryNode.getId(repoPath);
const node = await this.findNode(WorktreesNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof WorktreesNode && n.repoPath === repoPath, {
maxDepth: 2,
canTraverse: n => {
// Only search for worktrees nodes in the same repo
if (n instanceof RepositoriesNode) return true;
if (n instanceof RepositoryNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;

+ 2
- 2
src/views/searchAndCompareView.ts View File

@ -249,7 +249,7 @@ export class SearchAndCompareView extends ViewBase
protected readonly configKey = 'searchAndCompare';
constructor(container: Container) {
super(container, 'gitlens.views.searchAndCompare', 'Search & Compare', 'searchAndCompareView');
super(container, 'searchAndCompare', 'Search & Compare', 'searchAndCompareView');
void setContext('gitlens:views:searchAndCompare:keepResults', this.keepResults);
}
@ -501,7 +501,7 @@ export class SearchAndCompareView extends ViewBase
repoPath: string,
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
) {
const node = await this.findNode(RepositoryFolderNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof RepositoryFolderNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof SearchAndCompareViewNode || n instanceof RepositoryFolderNode,
});

+ 5
- 6
src/views/stashesView.ts View File

@ -18,7 +18,6 @@ import { RepositoryChange, RepositoryChangeComparisonMode } from '../git/models/
import { executeCommand } from '../system/command';
import { configuration } from '../system/configuration';
import { gate } from '../system/decorators/gate';
import { RepositoryNode } from './nodes/repositoryNode';
import { StashesNode } from './nodes/stashesNode';
import { StashFileNode } from './nodes/stashFileNode';
import { StashNode } from './nodes/stashNode';
@ -93,7 +92,7 @@ export class StashesView extends ViewBase {
protected readonly configKey = 'stashes';
constructor(container: Container) {
super(container, 'gitlens.views.stashes', 'Stashes', 'stashesView');
super(container, 'stashes', 'Stashes', 'stashesView');
}
override get canReveal(): boolean {
@ -200,15 +199,15 @@ export class StashesView extends ViewBase {
}
findStash(stash: GitStashReference, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(stash.repoPath);
const { repoPath } = stash;
return this.findNode(StashNode.getId(stash.repoPath, stash.ref), {
return this.findNode((n: any) => n.commit?.ref === stash.ref, {
maxDepth: 2,
canTraverse: n => {
if (n instanceof StashesViewNode) return true;
if (n instanceof StashesRepositoryNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -222,7 +221,7 @@ export class StashesView extends ViewBase {
repoPath: string,
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
) {
const node = await this.findNode(RepositoryFolderNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof RepositoryFolderNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof StashesViewNode || n instanceof RepositoryFolderNode,
});

+ 4
- 5
src/views/tagsView.ts View File

@ -13,7 +13,6 @@ import { executeCommand } from '../system/command';
import { configuration } from '../system/configuration';
import { gate } from '../system/decorators/gate';
import { BranchOrTagFolderNode } from './nodes/branchOrTagFolderNode';
import { RepositoryNode } from './nodes/repositoryNode';
import { TagsNode } from './nodes/tagsNode';
import type { ViewNode } from './nodes/viewNode';
import { RepositoriesSubscribeableNode, RepositoryFolderNode } from './nodes/viewNode';
@ -86,7 +85,7 @@ export class TagsView extends ViewBase {
protected readonly configKey = 'tags';
constructor(container: Container) {
super(container, 'gitlens.views.tags', 'Tags', 'tagsView');
super(container, 'tags', 'Tags', 'tagsView');
}
override get canReveal(): boolean {
@ -164,7 +163,7 @@ export class TagsView extends ViewBase {
}
findTag(tag: GitTagReference, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(tag.repoPath);
const { repoPath } = tag;
return this.findNode((n: any) => n.tag?.ref === tag.ref, {
allowPaging: true,
@ -173,7 +172,7 @@ export class TagsView extends ViewBase {
if (n instanceof TagsViewNode) return true;
if (n instanceof TagsRepositoryNode || n instanceof BranchOrTagFolderNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -187,7 +186,7 @@ export class TagsView extends ViewBase {
repoPath: string,
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
) {
const node = await this.findNode(RepositoryFolderNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof RepositoryFolderNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof TagsViewNode || n instanceof RepositoryFolderNode,
});

+ 7
- 22
src/views/viewBase.ts View File

@ -136,12 +136,15 @@ export abstract class ViewBase<
private readonly _lastKnownLimits = new Map<string, number | undefined>();
readonly id: `gitlens.views.${TreeViewTypes}`;
constructor(
public readonly container: Container,
public readonly id: `gitlens.views.${TreeViewTypes}`,
public readonly type: TreeViewTypes,
public readonly name: string,
private readonly trackingFeature: TrackedUsageFeatures,
) {
this.id = `gitlens.views.${type}`;
this.disposables.push(once(container.onReady)(this.onReady, this));
if (this.container.debugging || configuration.get('debug')) {
@ -378,32 +381,14 @@ export abstract class ViewBase<
return this.tree?.visible ?? false;
}
async findNode(
id: string,
options?: {
allowPaging?: boolean;
canTraverse?: (node: ViewNode) => boolean | Promise<boolean>;
maxDepth?: number;
token?: CancellationToken;
},
): Promise<ViewNode | undefined>;
async findNode(
predicate: (node: ViewNode) => boolean,
options?: {
allowPaging?: boolean;
canTraverse?: (node: ViewNode) => boolean | Promise<boolean>;
maxDepth?: number;
token?: CancellationToken;
},
): Promise<ViewNode | undefined>;
@log<ViewBase<RootNode, ViewConfig>['findNode']>({
args: {
0: predicate => (typeof predicate === 'string' ? predicate : '<function>'),
0: '<function>',
1: opts => `options=${JSON.stringify({ ...opts, canTraverse: undefined, token: undefined })}`,
},
})
async findNode(
predicate: string | ((node: ViewNode) => boolean),
predicate: (node: ViewNode) => boolean,
{
allowPaging = false,
canTraverse,
@ -421,7 +406,7 @@ export abstract class ViewBase<
async function find(this: ViewBase<RootNode, ViewConfig>) {
try {
const node = await this.findNodeCoreBFS(
typeof predicate === 'string' ? n => n.id === predicate : predicate,
predicate,
this.ensureRoot(),
allowPaging,
canTraverse,

+ 2
- 2
src/views/viewCommands.ts View File

@ -589,14 +589,14 @@ export class ViewCommands {
private pruneRemote(node: RemoteNode) {
if (!(node instanceof RemoteNode)) return Promise.resolve();
return RemoteActions.prune(node.repo, node.remote.name);
return RemoteActions.prune(node.remote.repoPath, node.remote.name);
}
@debug()
private async removeRemote(node: RemoteNode) {
if (!(node instanceof RemoteNode)) return Promise.resolve();
return RemoteActions.remove(node.repo, node.remote.name);
return RemoteActions.remove(node.remote.repoPath, node.remote.name);
}
@debug()

+ 7
- 7
src/views/workspacesView.ts View File

@ -24,7 +24,7 @@ export class WorkspacesView extends ViewBase
private _visibleDisposable: Disposable | undefined;
constructor(container: Container) {
super(container, 'gitlens.views.workspaces', 'Workspaces', 'workspaceView');
super(container, 'workspaces', 'Workspaces', 'workspaceView');
this._workspacesChangedDisposable = this.container.workspaces.onDidChangeWorkspaces(() => {
void this.ensureRoot().triggerChange(true);
});
@ -155,10 +155,10 @@ export class WorkspacesView extends ViewBase
registerViewCommand(
this.getQualifiedCommand('repo.locate'),
async (node: RepositoryNode | WorkspaceMissingRepositoryNode) => {
const descriptor = node.workspaceRepositoryDescriptor;
if (descriptor == null || node.workspaceId == null) return;
const descriptor = node.wsRepositoryDescriptor;
if (descriptor == null || node.workspace?.id == null) return;
await this.container.workspaces.locateWorkspaceRepo(node.workspaceId, descriptor);
await this.container.workspaces.locateWorkspaceRepo(node.workspace.id, descriptor);
void node.getParent()?.triggerChange(true);
},
@ -203,10 +203,10 @@ export class WorkspacesView extends ViewBase
registerViewCommand(
this.getQualifiedCommand('repo.remove'),
async (node: RepositoryNode | WorkspaceMissingRepositoryNode) => {
const descriptor = node.workspaceRepositoryDescriptor;
if (descriptor?.id == null || node.workspaceId == null) return;
const descriptor = node.wsRepositoryDescriptor;
if (descriptor?.id == null || node.workspace?.id == null) return;
await this.container.workspaces.removeCloudWorkspaceRepo(node.workspaceId, descriptor);
await this.container.workspaces.removeCloudWorkspaceRepo(node.workspace.id, descriptor);
// TODO@axosoft-ramint Do we need the grandparent here?
void node.getParent()?.getParent()?.triggerChange(true);
},

+ 6
- 6
src/views/worktreesView.ts View File

@ -15,7 +15,6 @@ import { SubscriptionState } from '../subscription';
import { executeCommand } from '../system/command';
import { configuration } from '../system/configuration';
import { gate } from '../system/decorators/gate';
import { RepositoryNode } from './nodes/repositoryNode';
import type { ViewNode } from './nodes/viewNode';
import { RepositoriesSubscribeableNode, RepositoryFolderNode } from './nodes/viewNode';
import { WorktreeNode } from './nodes/worktreeNode';
@ -97,7 +96,7 @@ export class WorktreesView extends ViewBase
protected readonly configKey = 'worktrees';
constructor(container: Container) {
super(container, 'gitlens.views.worktrees', 'Worktrees', 'workspaceView');
super(container, 'worktrees', 'Worktrees', 'workspaceView');
this.disposables.push(
window.registerFileDecorationProvider({
@ -232,15 +231,16 @@ export class WorktreesView extends ViewBase
}
findWorktree(worktree: GitWorktree, token?: CancellationToken) {
const repoNodeId = RepositoryNode.getId(worktree.repoPath);
const { repoPath, uri } = worktree;
const url = uri.toString();
return this.findNode(WorktreeNode.getId(worktree.repoPath, worktree.uri), {
return this.findNode(n => n instanceof WorktreeNode && worktree.uri.toString() === url, {
maxDepth: 2,
canTraverse: n => {
if (n instanceof WorktreesViewNode) return true;
if (n instanceof WorktreesRepositoryNode) {
return n.id.startsWith(repoNodeId);
return n.repoPath === repoPath;
}
return false;
@ -254,7 +254,7 @@ export class WorktreesView extends ViewBase
repoPath: string,
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
) {
const node = await this.findNode(RepositoryFolderNode.getId(repoPath), {
const node = await this.findNode(n => n instanceof RepositoryFolderNode && n.repoPath === repoPath, {
maxDepth: 1,
canTraverse: n => n instanceof WorktreesViewNode || n instanceof RepositoryFolderNode,
});

Loading…
Cancel
Save