diff --git a/images/dark/icon-ellipsis.svg b/images/dark/icon-ellipsis.svg
new file mode 100644
index 0000000..52cb665
--- /dev/null
+++ b/images/dark/icon-ellipsis.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/images/light/icon-ellipsis.svg b/images/light/icon-ellipsis.svg
new file mode 100644
index 0000000..badcae1
--- /dev/null
+++ b/images/light/icon-ellipsis.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/package.json b/package.json
index 32ac931..ed96a2f 100644
--- a/package.json
+++ b/package.json
@@ -2959,6 +2959,15 @@
"dark": "images/dark/icon-refresh.svg",
"light": "images/light/icon-refresh.svg"
}
+ },
+ {
+ "command": "gitlens.views.showAllChildren",
+ "title": "Show All",
+ "category": "GitLens",
+ "icon": {
+ "dark": "images/dark/icon-unfold.svg",
+ "light": "images/light/icon-unfold.svg"
+ }
}
],
"menus": {
@@ -3618,6 +3627,10 @@
{
"command": "gitlens.views.refreshNode",
"when": "false"
+ },
+ {
+ "command": "gitlens.views.showAllChildren",
+ "when": "false"
}
],
"editor/context": [
@@ -4806,6 +4819,16 @@
"command": "gitlens.views.refreshNode",
"when": "viewItem =~ /gitlens:(?!file\\b)/",
"group": "9_gitlens@1"
+ },
+ {
+ "command": "gitlens.views.showAllChildren",
+ "when": "viewItem =~ /gitlens:pager\\b/",
+ "group": "inline@1"
+ },
+ {
+ "command": "gitlens.views.showAllChildren",
+ "when": "viewItem =~ /gitlens:pager\\b/",
+ "group": "1_gitlens@1"
}
]
},
diff --git a/src/views/nodes/branchNode.ts b/src/views/nodes/branchNode.ts
index c613abb..2d713ff 100644
--- a/src/views/nodes/branchNode.ts
+++ b/src/views/nodes/branchNode.ts
@@ -79,7 +79,7 @@ export class BranchNode extends ViewRefNode implements Pageabl
}
const log = await Container.git.getLog(this.uri.repoPath!, {
- maxCount: this.maxCount || this.view.config.defaultItemLimit,
+ maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
ref: this.ref
});
if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')];
diff --git a/src/views/nodes/branchTrackingStatusNode.ts b/src/views/nodes/branchTrackingStatusNode.ts
index dabfbc2..a6e829a 100644
--- a/src/views/nodes/branchTrackingStatusNode.ts
+++ b/src/views/nodes/branchTrackingStatusNode.ts
@@ -44,7 +44,7 @@ export class BranchTrackingStatusNode extends ViewNode implements
: `${this.status.ref}..${this.status.upstream}`;
const log = await Container.git.getLog(this.uri.repoPath!, {
- maxCount: this.maxCount || this.view.config.defaultItemLimit,
+ maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
ref: range
});
if (log === undefined) return [];
diff --git a/src/views/nodes/branchesNode.ts b/src/views/nodes/branchesNode.ts
index 55455f2..e831739 100644
--- a/src/views/nodes/branchesNode.ts
+++ b/src/views/nodes/branchesNode.ts
@@ -3,7 +3,7 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewBranchesLayout } from '../../configuration';
import { Container } from '../../container';
import { GitUri, Repository } from '../../git/gitService';
-import { Arrays, Iterables } from '../../system';
+import { Arrays, debug, gate, Iterables } from '../../system';
import { RepositoriesView } from '../repositoriesView';
import { BranchNode } from './branchNode';
import { BranchOrTagFolderNode } from './branchOrTagFolderNode';
@@ -78,6 +78,8 @@ export class BranchesNode extends ViewNode {
return item;
}
+ @gate()
+ @debug()
refresh() {
this._children = undefined;
}
diff --git a/src/views/nodes/common.ts b/src/views/nodes/common.ts
index 11544e2..ae3f2b9 100644
--- a/src/views/nodes/common.ts
+++ b/src/views/nodes/common.ts
@@ -152,6 +152,10 @@ export abstract class PagerNode extends ViewNode {
this._args.previousNode = previousNode;
}
+ showAll() {
+ this.view.refreshNode(this.parent!, true, { ...this._args, maxCount: 0 });
+ }
+
getChildren(): ViewNode[] | Promise {
return [];
}
@@ -161,8 +165,8 @@ export abstract class PagerNode extends ViewNode {
item.contextValue = ResourceType.Pager;
item.command = this.getCommand();
item.iconPath = {
- dark: Container.context.asAbsolutePath('images/dark/icon-unfold.svg'),
- light: Container.context.asAbsolutePath('images/light/icon-unfold.svg')
+ dark: Container.context.asAbsolutePath('images/dark/icon-ellipsis.svg'),
+ light: Container.context.asAbsolutePath('images/light/icon-ellipsis.svg')
};
return item;
}
@@ -171,7 +175,7 @@ export abstract class PagerNode extends ViewNode {
return {
title: 'Refresh',
command: 'gitlens.views.refreshNode',
- arguments: [this.parent, false, this._args]
+ arguments: [this.parent, true, this._args]
};
}
}
diff --git a/src/views/nodes/compareResultsNode.ts b/src/views/nodes/compareResultsNode.ts
index 23e16c1..f99e550 100644
--- a/src/views/nodes/compareResultsNode.ts
+++ b/src/views/nodes/compareResultsNode.ts
@@ -3,13 +3,15 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { NamedRef } from '../../constants';
import { Container } from '../../container';
import { GitService, GitUri } from '../../git/gitService';
-import { log, Strings } from '../../system';
+import { debug, gate, log, Strings } from '../../system';
import { CompareView } from '../compareView';
import { CommitsQueryResults, ResultsCommitsNode } from './resultsCommitsNode';
import { ResultsFilesNode } from './resultsFilesNode';
import { ResourceType, SubscribeableViewNode, ViewNode } from './viewNode';
export class CompareResultsNode extends SubscribeableViewNode {
+ private _children: ViewNode[] | undefined;
+
constructor(
view: CompareView,
public readonly repoPath: string,
@@ -39,10 +41,21 @@ export class CompareResultsNode extends SubscribeableViewNode {
}
getChildren(): ViewNode[] {
- return [
- new ResultsCommitsNode(this.view, this, this.uri.repoPath!, this.getCommitsQuery.bind(this)),
- new ResultsFilesNode(this.view, this, this.uri.repoPath!, this._ref1.ref, this._ref2.ref)
- ];
+ if (this._children === undefined) {
+ this._children = [
+ new ResultsCommitsNode(
+ this.view,
+ this,
+ this.uri.repoPath!,
+ '? commits',
+ this.getCommitsQuery.bind(this),
+ true,
+ false
+ ),
+ new ResultsFilesNode(this.view, this, this.uri.repoPath!, this._ref1.ref, this._ref2.ref)
+ ];
+ }
+ return this._children;
}
async getTreeItem(): Promise {
@@ -86,14 +99,12 @@ export class CompareResultsNode extends SubscribeableViewNode {
void this.triggerChange();
}
- @log()
- async unpin() {
- if (!this._pinned) return;
-
- await this.view.updatePinnedComparison(this.getPinnableId());
+ @gate()
+ @debug()
+ refresh(reset: boolean = false) {
+ if (!reset) return;
- this._pinned = false;
- void this.triggerChange();
+ this._children = undefined;
}
@log()
@@ -118,6 +129,16 @@ export class CompareResultsNode extends SubscribeableViewNode {
this.view.triggerNodeChange(this);
}
+ @log()
+ async unpin() {
+ if (!this._pinned) return;
+
+ await this.view.updatePinnedComparison(this.getPinnableId());
+
+ this._pinned = false;
+ void this.triggerChange();
+ }
+
protected subscribe() {
return undefined;
}
diff --git a/src/views/nodes/contributorNode.ts b/src/views/nodes/contributorNode.ts
index 7e1c18b..2eaf1ea 100644
--- a/src/views/nodes/contributorNode.ts
+++ b/src/views/nodes/contributorNode.ts
@@ -26,7 +26,7 @@ export class ContributorNode extends ViewNode implements Pagea
async getChildren(): Promise {
const log = await Container.git.getLog(this.uri.repoPath!, {
- maxCount: this.maxCount || this.view.config.defaultItemLimit,
+ maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
authors: [this.contributor.name]
});
if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')];
diff --git a/src/views/nodes/lineHistoryTrackerNode.ts b/src/views/nodes/lineHistoryTrackerNode.ts
index 8ed57a8..1df7552 100644
--- a/src/views/nodes/lineHistoryTrackerNode.ts
+++ b/src/views/nodes/lineHistoryTrackerNode.ts
@@ -90,7 +90,9 @@ export class LineHistoryTrackerNode extends SubscribeableViewNode `returned ${r}`
+ })
async refresh(reset: boolean = false) {
const cc = Logger.getCorrelationContext();
diff --git a/src/views/nodes/resultsCommitsNode.ts b/src/views/nodes/resultsCommitsNode.ts
index 639b81d..00c474a 100644
--- a/src/views/nodes/resultsCommitsNode.ts
+++ b/src/views/nodes/resultsCommitsNode.ts
@@ -2,12 +2,12 @@
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import { GitLog, GitUri } from '../../git/gitService';
-import { Iterables } from '../../system';
+import { debug, gate, Iterables } from '../../system';
import { ViewWithFiles } from '../viewBase';
import { CommitNode } from './commitNode';
import { ShowMoreNode } from './common';
import { getBranchesAndTagTipsFn, insertDateMarkers } from './helpers';
-import { PageableViewNode, ResourceType, ViewNode } from './viewNode';
+import { getNextId, PageableViewNode, ResourceType, ViewNode } from './viewNode';
export interface CommitsQueryResults {
label: string;
@@ -18,15 +18,25 @@ export class ResultsCommitsNode extends ViewNode implements Pagea
readonly supportsPaging: boolean = true;
maxCount: number | undefined;
+ // Generate a unique id so the node order is preserved, since we update the label when the query completes
+ private readonly _uniqueId: number = getNextId('ResultsCommitsNode');
+
constructor(
view: ViewWithFiles,
parent: ViewNode,
public readonly repoPath: string,
- private readonly _commitsQuery: (maxCount: number | undefined) => Promise
+ private _label: string,
+ private readonly _commitsQuery: (maxCount: number | undefined) => Promise,
+ private _querying = true,
+ private readonly _expand = true
) {
super(GitUri.fromRepoPath(repoPath), view, parent);
}
+ get id(): string {
+ return `${this._uniqueId}|${this._instanceId}:${this.type}(${this.repoPath})`;
+ }
+
get type(): ResourceType {
return ResourceType.ResultsCommits;
}
@@ -47,14 +57,41 @@ export class ResultsCommitsNode extends ViewNode implements Pagea
];
if (log.truncated) {
- children.push(new ShowMoreNode(this.view, this, 'Results', children[children.length - 1]));
+ children.push(new ShowMoreNode(this.view, this, 'Results', children[children.length - 1], this.maxCount));
}
return children;
}
async getTreeItem(): Promise {
- const { label, log } = await this.getCommitsQueryResults();
+ let state;
+ let label;
+ let log;
+ if (this._querying) {
+ // Need to use Collapsed before we have results or the item won't show up in the view until the children are awaited
+ state = TreeItemCollapsibleState.Collapsed;
+ label = this._label;
+
+ this.getCommitsQueryResults().then(({ log }) => {
+ this._querying = false;
+ if (log != null) {
+ this.maxCount = log.maxCount;
+ }
+
+ this.triggerChange(false);
+ });
+ }
+ else {
+ ({ label, log } = await this.getCommitsQueryResults());
+ if (log != null) {
+ this.maxCount = log.maxCount;
+ }
+
+ state = this._expand ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.Collapsed;
+ if (log == null || log.count === 0) {
+ state = TreeItemCollapsibleState.None;
+ }
+ }
let description;
if ((await Container.git.getRepositoryCount()) > 1) {
@@ -62,17 +99,19 @@ export class ResultsCommitsNode extends ViewNode implements Pagea
description = (repo && repo.formattedName) || this.repoPath;
}
- const item = new TreeItem(
- label,
- log && log.count > 0 ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.None
- );
+ const item = new TreeItem(label, state);
item.contextValue = this.type;
item.description = description;
+ item.id = this.id;
return item;
}
- refresh() {
+ @gate()
+ @debug()
+ refresh(reset: boolean = false) {
+ if (!reset) return;
+
this._commitsQueryResults = this._commitsQuery(this.maxCount);
}
diff --git a/src/views/nodes/resultsFilesNode.ts b/src/views/nodes/resultsFilesNode.ts
index b2bf292..ef000f4 100644
--- a/src/views/nodes/resultsFilesNode.ts
+++ b/src/views/nodes/resultsFilesNode.ts
@@ -4,11 +4,11 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ViewFilesLayout } from '../../configuration';
import { Container } from '../../container';
import { GitFile, GitUri } from '../../git/gitService';
-import { Arrays, Iterables, Strings } from '../../system';
+import { Arrays, debug, gate, Iterables, Strings } from '../../system';
import { ViewWithFiles } from '../viewBase';
import { FileNode, FolderNode } from './folderNode';
import { ResultsFileNode } from './resultsFileNode';
-import { ResourceType, ViewNode } from './viewNode';
+import { getNextId, ResourceType, ViewNode } from './viewNode';
export interface FilesQueryResults {
label: string;
@@ -16,6 +16,9 @@ export interface FilesQueryResults {
}
export class ResultsFilesNode extends ViewNode {
+ // Generate a unique id so the node order is preserved, since we update the label when the query completes
+ private readonly _uniqueId: number = getNextId('ResultsFilesNode');
+
constructor(
view: ViewWithFiles,
parent: ViewNode,
@@ -26,6 +29,10 @@ export class ResultsFilesNode extends ViewNode {
super(GitUri.fromRepoPath(repoPath), view, parent);
}
+ get id(): string {
+ return `${this._uniqueId}|${this._instanceId}:gitlens:results:files(${this.repoPath})`;
+ }
+
async getChildren(): Promise {
const { diff } = await this.getFilesQueryResults();
if (diff === undefined) return [];
@@ -57,21 +64,45 @@ export class ResultsFilesNode extends ViewNode {
}
async getTreeItem(): Promise {
- const { diff, label } = await this.getFilesQueryResults();
+ let state;
+ let label;
+ let diff;
+ if (this._querying) {
+ // Need to use Collapsed before we have results or the item won't show up in the view until the children are awaited
+ state = TreeItemCollapsibleState.Collapsed;
+ label = '? files changed';
+
+ this.getFilesQueryResults().then(_ => {
+ this._querying = false;
+ this.triggerChange(false);
+ });
+ }
+ else {
+ ({ label, diff } = await this.getFilesQueryResults());
+
+ state = TreeItemCollapsibleState.Expanded;
+ if (diff == null || diff.length === 0) {
+ state = TreeItemCollapsibleState.None;
+ }
+ }
- const item = new TreeItem(
- label,
- diff && diff.length > 0 ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None
- );
+ const item = new TreeItem(label, state);
item.contextValue = ResourceType.ResultsFiles;
+ item.id = this.id;
+
return item;
}
- refresh() {
+ @gate()
+ @debug()
+ refresh(reset: boolean = false) {
+ if (!reset) return;
+
this._filesQueryResults = this.getFilesQueryResultsCore();
}
private _filesQueryResults: Promise | undefined;
+ private _querying = true;
private getFilesQueryResults() {
if (this._filesQueryResults === undefined) {
diff --git a/src/views/nodes/searchResultsCommitsNode.ts b/src/views/nodes/searchResultsCommitsNode.ts
index 4cf5333..0717190 100644
--- a/src/views/nodes/searchResultsCommitsNode.ts
+++ b/src/views/nodes/searchResultsCommitsNode.ts
@@ -1,5 +1,5 @@
'use strict';
-import { TreeItem } from 'vscode';
+import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { SearchCommitsCommandArgs } from '../../commands';
import { Commands } from '../../commands/common';
import { GitRepoSearchBy } from '../../git/gitService';
@@ -14,9 +14,11 @@ export class SearchResultsCommitsNode extends ResultsCommitsNode {
repoPath: string,
public readonly search: string,
public readonly searchBy: GitRepoSearchBy,
- commitsQuery: (maxCount: number | undefined) => Promise
+ label: string,
+ commitsQuery: (maxCount: number | undefined) => Promise,
+ _querying = true
) {
- super(view, parent, repoPath, commitsQuery);
+ super(view, parent, repoPath, label, commitsQuery, _querying, true);
}
get type(): ResourceType {
@@ -24,11 +26,9 @@ export class SearchResultsCommitsNode extends ResultsCommitsNode {
}
async getTreeItem(): Promise {
- const { log } = await super.getCommitsQueryResults();
-
const item = await super.getTreeItem();
- if (log == null || log.count === 0) {
+ if (item.collapsibleState === TreeItemCollapsibleState.None) {
const args: SearchCommitsCommandArgs = {
search: this.search,
searchBy: this.searchBy,
@@ -40,6 +40,7 @@ export class SearchResultsCommitsNode extends ResultsCommitsNode {
arguments: [args]
};
}
+ item.id = undefined;
return item;
}
diff --git a/src/views/nodes/tagNode.ts b/src/views/nodes/tagNode.ts
index 0ba0d6f..518e5a1 100644
--- a/src/views/nodes/tagNode.ts
+++ b/src/views/nodes/tagNode.ts
@@ -32,7 +32,7 @@ export class TagNode extends ViewRefNode implements PageableVi
async getChildren(): Promise {
const log = await Container.git.getLog(this.uri.repoPath!, {
- maxCount: this.maxCount || this.view.config.defaultItemLimit,
+ maxCount: this.maxCount !== undefined ? this.maxCount : this.view.config.defaultItemLimit,
ref: this.tag.name
});
if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')];
diff --git a/src/views/nodes/viewNode.ts b/src/views/nodes/viewNode.ts
index 1b6e44d..b62b48b 100644
--- a/src/views/nodes/viewNode.ts
+++ b/src/views/nodes/viewNode.ts
@@ -53,12 +53,21 @@ export interface ViewNode {
readonly id?: string;
}
-const counter = 0;
-function getViewNodeInstanceId() {
- // if (counter === Number.MAX_SAFE_INTEGER) {
- // counter = 0;
- // }
- // counter++;
+const counters: { [key: string]: number } = {
+ instanceId: 0
+};
+export function getNextId(key?: string) {
+ if (key === undefined) {
+ key = 'instanceId';
+ }
+
+ let counter = counters[key] || 0;
+ if (counter === Number.MAX_SAFE_INTEGER) {
+ counter = 0;
+ }
+ counter++;
+
+ counters[key] = counter;
return counter;
}
@@ -67,7 +76,7 @@ export abstract class ViewNode {
protected readonly _instanceId: number;
constructor(uri: GitUri, public readonly view: TView, protected readonly parent?: ViewNode) {
- this._instanceId = getViewNodeInstanceId();
+ this._instanceId = 0; //getNextId();
this._uri = uri;
if (Logger.isDebugging) {
@@ -110,8 +119,8 @@ export abstract class ViewNode {
@gate()
@debug()
- triggerChange(): Promise {
- return this.view.refreshNode(this);
+ triggerChange(reset: boolean = false): Promise {
+ return this.view.refreshNode(this, reset);
}
}
diff --git a/src/views/searchView.ts b/src/views/searchView.ts
index e8b9c6b..b4417c5 100644
--- a/src/views/searchView.ts
+++ b/src/views/searchView.ts
@@ -118,7 +118,16 @@ export class SearchView extends ViewBase {
);
return this.addResults(
- new SearchResultsCommitsNode(this, this._root!, repoPath, search, searchBy, searchQueryFn)
+ new SearchResultsCommitsNode(
+ this,
+ this._root!,
+ repoPath,
+ search,
+ searchBy,
+ `Searching for ${typeof options.label === 'string' ? options.label : options.label.label}`,
+ searchQueryFn,
+ true
+ )
);
}
@@ -143,7 +152,7 @@ export class SearchView extends ViewBase {
});
return this.addResults(
- new SearchResultsCommitsNode(this, this._root!, repoPath, search, searchBy, searchQueryFn)
+ new SearchResultsCommitsNode(this, this._root!, repoPath, search, searchBy, label, searchQueryFn, false)
);
}
diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts
index 039aa6c..3a3b56b 100644
--- a/src/views/viewCommands.ts
+++ b/src/views/viewCommands.ts
@@ -39,6 +39,7 @@ import {
viewSupportsNodeDismissal
} from './nodes';
import { Strings } from '../system/string';
+import { PagerNode } from './nodes/common';
export interface RefreshNodeCommandArgs {
maxCount?: number;
@@ -73,6 +74,7 @@ export class ViewCommands implements Disposable {
(node: ViewNode) => viewSupportsNodeDismissal(node.view) && node.view.dismissNode(node),
this
);
+ commands.registerCommand('gitlens.views.showAllChildren', (node: PagerNode) => node.showAll(), this);
commands.registerCommand('gitlens.views.fetch', this.fetch, this);
commands.registerCommand('gitlens.views.pull', this.pull, this);