Skip to content

Commit aa3ac44

Browse files
authored
SCM - refactor selection in Repositories view (microsoft#256706)
Refactor selection
1 parent dd052e8 commit aa3ac44

File tree

1 file changed

+72
-32
lines changed

1 file changed

+72
-32
lines changed

src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { append, $ } from '../../../../base/browser/dom.js';
1010
import { IListVirtualDelegate, IIdentityProvider } from '../../../../base/browser/ui/list/list.js';
1111
import { IAsyncDataSource, ITreeEvent, ITreeContextMenuEvent } from '../../../../base/browser/ui/tree/tree.js';
1212
import { WorkbenchCompressibleAsyncDataTree } from '../../../../platform/list/browser/listService.js';
13-
import { ISCMRepository, ISCMViewService } from '../common/scm.js';
13+
import { ISCMRepository, ISCMService, ISCMViewService } from '../common/scm.js';
1414
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
1515
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
1616
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
@@ -27,7 +27,7 @@ import { Iterable } from '../../../../base/common/iterator.js';
2727
import { MenuId } from '../../../../platform/actions/common/actions.js';
2828
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
2929
import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js';
30-
import { autorun, IObservable, observableSignalFromEvent } from '../../../../base/common/observable.js';
30+
import { autorun, IObservable, observableFromEvent, observableSignalFromEvent } from '../../../../base/common/observable.js';
3131
import { Sequencer } from '../../../../base/common/async.js';
3232

3333
class ListDelegate implements IListVirtualDelegate<ISCMRepository> {
@@ -57,20 +57,6 @@ class RepositoryTreeDataSource extends Disposable implements IAsyncDataSource<IS
5757
return repositories;
5858
}
5959

60-
getParent(element: ISCMViewService | ISCMRepository): ISCMViewService | ISCMRepository {
61-
if (!isSCMRepository(element)) {
62-
throw new Error('Unexpected call to getParent');
63-
}
64-
65-
const repository = this.scmViewService.repositories
66-
.find(r => r.provider.id === element.provider.parentId);
67-
if (!repository) {
68-
throw new Error('Invalid element passed to getParent');
69-
}
70-
71-
return repository;
72-
}
73-
7460
hasChildren(inputOrElement: ISCMViewService | ISCMRepository): boolean {
7561
const parentId = isSCMRepository(inputOrElement)
7662
? inputOrElement.provider.id
@@ -103,6 +89,7 @@ export class SCMRepositoriesViewPane extends ViewPane {
10389

10490
constructor(
10591
options: IViewPaneOptions,
92+
@ISCMService private readonly scmService: ISCMService,
10693
@ISCMViewService private readonly scmViewService: ISCMViewService,
10794
@IKeybindingService keybindingService: IKeybindingService,
10895
@IContextMenuService contextMenuService: IContextMenuService,
@@ -150,13 +137,29 @@ export class SCMRepositoriesViewPane extends ViewPane {
150137
this.updateBodySize(this.tree.contentHeight, visibleCount);
151138
}));
152139

153-
// Update tree
154-
const onDidChangeRepositoriesSignal = observableSignalFromEvent(
155-
this, this.scmViewService.onDidChangeRepositories);
140+
// Update tree (add/remove repositories)
141+
const addedRepositoryObs = observableFromEvent(
142+
this, this.scmService.onDidAddRepository, e => e);
143+
144+
const removedRepositoryObs = observableFromEvent(
145+
this, this.scmService.onDidRemoveRepository, e => e);
156146

157147
this.visibilityDisposables.add(autorun(async reader => {
158-
onDidChangeRepositoriesSignal.read(reader);
159-
await this.treeOperationSequencer.queue(() => this.updateChildren());
148+
const addedRepository = addedRepositoryObs.read(reader);
149+
const removedRepository = removedRepositoryObs.read(reader);
150+
151+
if (addedRepository === undefined && removedRepository === undefined) {
152+
await this.updateChildren();
153+
return;
154+
}
155+
156+
if (addedRepository) {
157+
await this.updateRepository(addedRepository);
158+
}
159+
160+
if (removedRepository) {
161+
await this.updateRepository(removedRepository);
162+
}
160163
}));
161164

162165
// Update tree selection
@@ -203,6 +206,12 @@ export class SCMRepositoriesViewPane extends ViewPane {
203206
{
204207
identityProvider: this.treeIdentityProvider,
205208
horizontalScrolling: false,
209+
collapseByDefault: (e: unknown) => {
210+
if (isSCMRepository(e) && e.provider.parentId === undefined) {
211+
return false;
212+
}
213+
return true;
214+
},
206215
compressionEnabled: compressionEnabled.get(),
207216
overrideStyles: this.getLocationBasedColors().listOverrideStyles,
208217
expandOnDoubleClick: false,
@@ -267,11 +276,43 @@ export class SCMRepositoriesViewPane extends ViewPane {
267276

268277
private onTreeContentHeightChange(height: number): void {
269278
this.updateBodySize(height);
279+
280+
// Refresh the selection
281+
this.treeOperationSequencer.queue(() => this.updateTreeSelection());
270282
}
271283

272-
private async updateChildren(): Promise<void> {
273-
await this.tree.updateChildren();
274-
this.updateBodySize(this.tree.contentHeight);
284+
private async updateChildren(element?: ISCMRepository): Promise<void> {
285+
await this.treeOperationSequencer.queue(async () => {
286+
if (element && this.tree.hasNode(element)) {
287+
await this.tree.updateChildren(element, true);
288+
} else {
289+
await this.tree.updateChildren(undefined, true);
290+
}
291+
});
292+
}
293+
294+
private async expand(element: ISCMRepository): Promise<void> {
295+
await this.treeOperationSequencer.queue(() => this.tree.expand(element, true));
296+
}
297+
298+
private async updateRepository(repository: ISCMRepository): Promise<void> {
299+
if (repository.provider.parentId === undefined) {
300+
await this.updateChildren();
301+
return;
302+
}
303+
304+
await this.updateParentRepository(repository);
305+
}
306+
307+
private async updateParentRepository(repository: ISCMRepository): Promise<void> {
308+
const parentRepository = this.scmViewService.repositories
309+
.find(r => r.provider.id === repository.provider.parentId);
310+
if (!parentRepository) {
311+
return;
312+
}
313+
314+
await this.updateChildren(parentRepository);
315+
await this.expand(parentRepository);
275316
}
276317

277318
private updateBodySize(contentHeight: number, visibleCount?: number): void {
@@ -307,15 +348,14 @@ export class SCMRepositoriesViewPane extends ViewPane {
307348
}
308349
}
309350

310-
// Expand all selected items
311-
for (const item of selection) {
312-
await this.tree.expandTo(item);
313-
}
314-
this.tree.setSelection(selection);
351+
const visibleSelection = selection
352+
.filter(s => this.tree.hasNode(s));
353+
354+
this.tree.setSelection(visibleSelection);
315355

316-
if (selection.length > 0 && !this.tree.getFocus().includes(selection[0])) {
317-
this.tree.setAnchor(selection[0]);
318-
this.tree.setFocus([selection[0]]);
356+
if (visibleSelection.length > 0 && !this.tree.getFocus().includes(visibleSelection[0])) {
357+
this.tree.setAnchor(visibleSelection[0]);
358+
this.tree.setFocus([visibleSelection[0]]);
319359
}
320360
}
321361

0 commit comments

Comments
 (0)