Skip to content

Commit 7e859c8

Browse files
committed
Improves grouped view selection
- Moves selection tracking from Views class into ScmGroupedView - Adds default selection to ensure views always have a selected node
1 parent 8269872 commit 7e859c8

File tree

3 files changed

+70
-52
lines changed

3 files changed

+70
-52
lines changed

src/views/scmGroupedView.ts

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export class ScmGroupedView implements Disposable {
3434
private _cleared: Deferred<void> | undefined;
3535
private _clearLoadingTimer: ReturnType<typeof setTimeout> | undefined;
3636
private readonly _disposable: Disposable;
37+
private _lastSelectedByView = new Map<
38+
GroupableTreeViewTypes,
39+
{ node: ViewNode; parents: ViewNode[] | undefined; expanded: boolean }
40+
>();
3741
private _loaded: Deferred<void> | undefined;
3842
private _onDidChangeTreeData: EventEmitter<ViewNode | undefined> | undefined;
3943
private _tree: TreeView<ViewNode> | undefined;
@@ -63,6 +67,26 @@ export class ScmGroupedView implements Disposable {
6367
async clearView<T extends GroupableTreeViewTypes>(type: T): Promise<void> {
6468
if (this._view == null || this._view.type === type) return;
6569

70+
// Save current selection before switching views
71+
const node: ViewNode | undefined = this._view.selection?.[0];
72+
if (node != null) {
73+
const parents: ViewNode[] = [];
74+
75+
let parent: ViewNode | undefined = node;
76+
while (true) {
77+
parent = parent.getParent();
78+
if (parent == null) break;
79+
80+
parents.unshift(parent);
81+
}
82+
83+
this._lastSelectedByView.set(this._view.type, {
84+
node: node,
85+
parents: parents,
86+
expanded: this._view.isNodeExpanded(node),
87+
});
88+
}
89+
6690
void setContext('gitlens:views:scm:grouped:loading', true);
6791
clearTimeout(this._clearLoadingTimer);
6892

@@ -114,11 +138,39 @@ export class ScmGroupedView implements Disposable {
114138
this._loaded?.cancel();
115139
this._loaded = defer<void>();
116140
void this._loaded.promise.then(
117-
() => {
141+
async () => {
118142
this._loaded = undefined;
119143

120-
if (focus) {
121-
setTimeout(() => void this._view?.show({ preserveFocus: false }), 50);
144+
const view = this._view;
145+
if (view != null) {
146+
if (!view.visible) {
147+
await view.show({ preserveFocus: !focus });
148+
}
149+
150+
let selection = this._lastSelectedByView.get(type);
151+
152+
setTimeout(async () => {
153+
if (selection == null && view.selection?.length) {
154+
selection = { node: view.selection[0], parents: undefined, expanded: false };
155+
}
156+
if (selection == null) {
157+
if (focus) {
158+
await view.show({ preserveFocus: false });
159+
}
160+
return;
161+
}
162+
163+
const { node, parents, expanded } = selection;
164+
if (parents == null) {
165+
await view.revealDeep(node, { expand: expanded, focus: focus ?? false, select: true });
166+
} else {
167+
await view.revealDeep(node, parents, {
168+
expand: expanded,
169+
focus: focus ?? false,
170+
select: true,
171+
});
172+
}
173+
}, 50);
122174
}
123175

124176
this._clearLoadingTimer = setTimeout(

src/views/viewBase.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ export abstract class ViewBase<
448448
this.tree = window.createTreeView<ViewNode>(this.id, { ...options, treeDataProvider: this });
449449
this.disposables.push(this.tree);
450450
}
451+
this._defaultSelection = [];
451452

452453
this.disposables.push(
453454
configuration.onDidChange(e => {
@@ -513,12 +514,20 @@ export abstract class ViewBase<
513514
if (!children.length) return children;
514515

515516
const index = children.findIndex(n => n instanceof GroupedHeaderNode);
516-
if (index === 0) return children.length === 1 ? [] : children;
517+
if (index === 0) {
518+
this._defaultSelection = [children[0]];
519+
return children.length === 1 ? [] : children;
520+
}
517521

522+
let header: ViewNode | undefined;
518523
if (index === -1) {
519-
children.unshift(new GroupedHeaderNode(this as unknown as View, node));
524+
header = new GroupedHeaderNode(this as unknown as View, node);
520525
} else if (index > 0) {
521-
children.unshift(children.splice(index, 1)[0]);
526+
header = children.splice(index, 1)[0];
527+
}
528+
if (header != null) {
529+
this._defaultSelection = [header];
530+
children.unshift(header);
522531
}
523532

524533
return children;
@@ -666,10 +675,11 @@ export abstract class ViewBase<
666675
return this.tree.selection[0];
667676
}
668677

678+
private _defaultSelection: readonly ViewNode[] = [];
669679
get selection(): readonly ViewNode[] {
670680
if (this.tree == null || this.root == null) return [];
671681

672-
return this.tree.selection;
682+
return this.tree.selection.length === 0 ? this._defaultSelection : this.tree.selection;
673683
}
674684

675685
get visible(): boolean {

src/views/views.ts

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,6 @@ export class Views implements Disposable {
100100
return this._scmGroupedViews;
101101
}
102102

103-
private _lastSelectedByView = new Map<
104-
GroupableTreeViewTypes,
105-
{ node: ViewNode; parents: ViewNode[]; expanded: boolean }
106-
>();
107103
private _welcomeDismissed = false;
108104

109105
constructor(
@@ -504,48 +500,8 @@ export class Views implements Disposable {
504500

505501
private async setScmGroupedView<T extends GroupableTreeViewTypes>(type: T, focus?: boolean) {
506502
if (this._scmGroupedView != null) {
507-
// Save current selection before switching views
508-
let { view } = this._scmGroupedView;
509-
if (view) {
510-
const node: ViewNode | undefined = view.selection?.[0];
511-
if (node != null) {
512-
const parents: ViewNode[] = [];
513-
514-
let parent: ViewNode | undefined = node;
515-
while (true) {
516-
parent = parent.getParent();
517-
if (parent == null) break;
518-
519-
parents.unshift(parent);
520-
}
521-
522-
this._lastSelectedByView.set(view.type, {
523-
node: node,
524-
parents: parents,
525-
expanded: view.isNodeExpanded(node),
526-
});
527-
}
528-
}
529-
530503
await this._scmGroupedView.clearView(type);
531-
view = this._scmGroupedView.setView(type, focus);
532-
533-
// Restore the last selection for this view type (if any)
534-
if (view) {
535-
if (!view.visible) {
536-
await view.show({ preserveFocus: !focus });
537-
}
538-
539-
const selection = this._lastSelectedByView.get(type);
540-
if (selection != null) {
541-
setTimeout(async () => {
542-
const { node, parents, expanded } = selection;
543-
await view.revealDeep(node, parents, { expand: expanded, focus: focus ?? false, select: true });
544-
}, 1);
545-
}
546-
}
547-
548-
return view;
504+
return this._scmGroupedView.setView(type, focus);
549505
}
550506

551507
if (!this.scmGroupedViews?.has(type)) {

0 commit comments

Comments
 (0)