Skip to content

Commit 874cfc3

Browse files
authored
SCM - repositories view improvements (#279190)
* SCM - save/restore repositories tree view stage * SCM - automatically expand repository if there is only one
1 parent bfc6d53 commit 874cfc3

File tree

3 files changed

+58
-14
lines changed

3 files changed

+58
-14
lines changed

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

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ import { URI } from '../../../../base/common/uri.js';
4242
import { basename } from '../../../../base/common/resources.js';
4343
import { ICompressibleTreeRenderer } from '../../../../base/browser/ui/tree/objectTree.js';
4444
import { ICompressedTreeNode } from '../../../../base/browser/ui/tree/compressedObjectTreeModel.js';
45-
import { ITreeCompressionDelegate } from '../../../../base/browser/ui/tree/asyncDataTree.js';
45+
import { IAsyncDataTreeViewState, ITreeCompressionDelegate } from '../../../../base/browser/ui/tree/asyncDataTree.js';
4646
import { Codicon } from '../../../../base/common/codicons.js';
47+
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
4748

4849
type TreeElement = ISCMRepository | SCMArtifactGroupTreeElement | SCMArtifactTreeElement | IResourceNode<SCMArtifactTreeElement, SCMArtifactGroupTreeElement>;
4950

@@ -394,13 +395,18 @@ export class SCMRepositoriesViewPane extends ViewPane {
394395
@IConfigurationService configurationService: IConfigurationService,
395396
@IOpenerService openerService: IOpenerService,
396397
@IThemeService themeService: IThemeService,
397-
@IHoverService hoverService: IHoverService
398+
@IHoverService hoverService: IHoverService,
399+
@IStorageService private readonly storageService: IStorageService
398400
) {
399401
super({ ...options, titleMenuId: MenuId.SCMSourceControlTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);
400402

401403
this.visibleCountObs = observableConfigValue('scm.repositories.visible', 10, this.configurationService);
402404
this.providerCountBadgeObs = observableConfigValue<'hidden' | 'auto' | 'visible'>('scm.providerCountBadge', 'hidden', this.configurationService);
403405

406+
this.storageService.onWillSaveState(() => {
407+
this.storeTreeViewState();
408+
}, this, this._store);
409+
404410
this._register(this.updateChildrenThrottler);
405411
}
406412

@@ -416,7 +422,8 @@ export class SCMRepositoriesViewPane extends ViewPane {
416422
treeContainer.classList.toggle('auto-provider-counts', providerCountBadge === 'auto');
417423
}));
418424

419-
this.createTree(treeContainer);
425+
const viewState = this.loadTreeViewState();
426+
this.createTree(treeContainer, viewState);
420427

421428
this.onDidChangeBodyVisibility(async visible => {
422429
if (!visible) {
@@ -426,7 +433,7 @@ export class SCMRepositoriesViewPane extends ViewPane {
426433

427434
this.treeOperationSequencer.queue(async () => {
428435
// Initial rendering
429-
await this.tree.setInput(this.scmViewService);
436+
await this.tree.setInput(this.scmViewService, viewState);
430437

431438
// scm.repositories.visible setting
432439
this.visibilityDisposables.add(autorun(reader => {
@@ -461,6 +468,17 @@ export class SCMRepositoriesViewPane extends ViewPane {
461468
for (const repository of this.scmService.repositories) {
462469
this.onDidAddRepository(repository);
463470
}
471+
472+
// Expand repository if there is only one
473+
this.visibilityDisposables.add(autorun(async reader => {
474+
const explorerEnabledConfig = this.scmViewService.explorerEnabledConfig.read(reader);
475+
const didFinishLoadingRepositories = this.scmViewService.didFinishLoadingRepositories.read(reader);
476+
477+
if (viewState === undefined && explorerEnabledConfig && didFinishLoadingRepositories && this.scmViewService.repositories.length === 1) {
478+
await this.treeOperationSequencer.queue(() =>
479+
this.tree.expand(this.scmViewService.repositories[0]));
480+
}
481+
}));
464482
});
465483
}, this, this._store);
466484
}
@@ -475,7 +493,7 @@ export class SCMRepositoriesViewPane extends ViewPane {
475493
this.tree.domFocus();
476494
}
477495

478-
private createTree(container: HTMLElement): void {
496+
private createTree(container: HTMLElement, viewState?: IAsyncDataTreeViewState): void {
479497
this.treeIdentityProvider = new RepositoryTreeIdentityProvider();
480498
this.treeDataSource = this.instantiationService.createInstance(RepositoryTreeDataSource);
481499
this._register(this.treeDataSource);
@@ -504,7 +522,10 @@ export class SCMRepositoriesViewPane extends ViewPane {
504522
}
505523

506524
// Explorer mode
507-
if (isSCMArtifactNode(e)) {
525+
if (viewState?.expanded && (isSCMRepository(e) || isSCMArtifactGroupTreeElement(e) || isSCMArtifactTreeElement(e))) {
526+
// Only expand repositories/artifact groups/artifacts that were expanded before
527+
return viewState.expanded.indexOf(this.treeIdentityProvider.getId(e)) === -1;
528+
} else if (isSCMArtifactNode(e)) {
508529
// Only expand artifact folders as they are compressed by default
509530
return !(e.childrenCount === 1 && Iterable.first(e.children)?.element === undefined);
510531
} else {
@@ -773,6 +794,26 @@ export class SCMRepositoriesViewPane extends ViewPane {
773794
});
774795
}
775796

797+
private loadTreeViewState(): IAsyncDataTreeViewState | undefined {
798+
const storageViewState = this.storageService.get('scm.repositoriesViewState', StorageScope.WORKSPACE);
799+
if (!storageViewState) {
800+
return undefined;
801+
}
802+
803+
try {
804+
const treeViewState = JSON.parse(storageViewState);
805+
return treeViewState;
806+
} catch {
807+
return undefined;
808+
}
809+
}
810+
811+
private storeTreeViewState(): void {
812+
if (this.tree) {
813+
this.storageService.store('scm.repositoriesViewState', JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE, StorageTarget.MACHINE);
814+
}
815+
}
816+
776817
override dispose(): void {
777818
this.visibilityDisposables.dispose();
778819
super.dispose();

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ export class SCMViewService implements ISCMViewService {
116116
readonly graphShowIncomingChangesConfig: IObservable<boolean>;
117117
readonly graphShowOutgoingChangesConfig: IObservable<boolean>;
118118

119-
private didFinishLoading: boolean = false;
120119
private didSelectRepository: boolean = false;
121120
private previousState: ISCMViewServiceState | undefined;
122121
private readonly disposables = new DisposableStore();
@@ -127,6 +126,8 @@ export class SCMViewService implements ISCMViewService {
127126
return this._repositories.map(r => r.repository);
128127
}
129128

129+
readonly didFinishLoadingRepositories = observableValue<boolean>(this, false);
130+
130131
get visibleRepositories(): ISCMRepository[] {
131132
// In order to match the legacy behaviour, when the repositories are sorted by discovery time,
132133
// the visible repositories are sorted by the selection index instead of the discovery time.
@@ -339,12 +340,12 @@ export class SCMViewService implements ISCMViewService {
339340
// or during a profile switch.
340341
extensionService.onWillStop(() => {
341342
this.onWillSaveState();
342-
this.didFinishLoading = false;
343+
this.didFinishLoadingRepositories.set(false, undefined);
343344
}, this, this.disposables);
344345
}
345346

346347
private onDidAddRepository(repository: ISCMRepository): void {
347-
if (!this.didFinishLoading) {
348+
if (!this.didFinishLoadingRepositories.get()) {
348349
this.eventuallyFinishLoading();
349350
}
350351

@@ -354,7 +355,7 @@ export class SCMViewService implements ISCMViewService {
354355

355356
let removed: Iterable<ISCMRepository> = Iterable.empty();
356357

357-
if (this.previousState && !this.didFinishLoading) {
358+
if (this.previousState && !this.didFinishLoadingRepositories.get()) {
358359
const index = this.previousState.all.indexOf(getProviderStorageKey(repository.provider));
359360

360361
if (index === -1) {
@@ -421,7 +422,7 @@ export class SCMViewService implements ISCMViewService {
421422
}
422423

423424
private onDidRemoveRepository(repository: ISCMRepository): void {
424-
if (!this.didFinishLoading) {
425+
if (!this.didFinishLoadingRepositories.get()) {
425426
this.eventuallyFinishLoading();
426427
}
427428

@@ -562,7 +563,8 @@ export class SCMViewService implements ISCMViewService {
562563
}
563564

564565
private onWillSaveState(): void {
565-
if (!this.didFinishLoading) { // don't remember state, if the workbench didn't really finish loading
566+
if (!this.didFinishLoadingRepositories.get()) {
567+
// Don't remember state, if the workbench didn't really finish loading
566568
return;
567569
}
568570

@@ -579,11 +581,11 @@ export class SCMViewService implements ISCMViewService {
579581
}
580582

581583
private finishLoading(): void {
582-
if (this.didFinishLoading) {
584+
if (this.didFinishLoadingRepositories.get()) {
583585
return;
584586
}
585587

586-
this.didFinishLoading = true;
588+
this.didFinishLoadingRepositories.set(true, undefined);
587589
}
588590

589591
dispose(): void {

src/vs/workbench/contrib/scm/common/scm.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ export interface ISCMViewService {
237237

238238
repositories: ISCMRepository[];
239239
readonly onDidChangeRepositories: Event<ISCMViewVisibleRepositoryChangeEvent>;
240+
readonly didFinishLoadingRepositories: IObservable<boolean>;
240241

241242
visibleRepositories: readonly ISCMRepository[];
242243
readonly onDidChangeVisibleRepositories: Event<ISCMViewVisibleRepositoryChangeEvent>;

0 commit comments

Comments
 (0)