Skip to content

Commit 8c55ad1

Browse files
authored
SCM - add throttling to scm treeview rendering (microsoft#199679)
1 parent 3635717 commit 8c55ad1

File tree

2 files changed

+60
-41
lines changed

2 files changed

+60
-41
lines changed

src/vs/base/browser/ui/tree/asyncDataTree.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,7 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
12771277
return { focus, selection, expanded, scrollTop: this.scrollTop };
12781278
}
12791279

1280-
protected override render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
1280+
protected override render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>, options?: IAsyncDataTreeUpdateChildrenOptions<T>): void {
12811281
if (!this.identityProvider) {
12821282
return super.render(node, viewStateContext);
12831283
}
@@ -1307,7 +1307,7 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
13071307
const oldSelection = getUncompressedIds(this.tree.getSelection() as IAsyncDataTreeNode<TInput, T>[]);
13081308
const oldFocus = getUncompressedIds(this.tree.getFocus() as IAsyncDataTreeNode<TInput, T>[]);
13091309

1310-
super.render(node, viewStateContext);
1310+
super.render(node, viewStateContext, options);
13111311

13121312
const selection = this.getSelection();
13131313
let didChangeSelection = false;

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

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeSer
2727
import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, getActionViewItemProvider, isSCMActionButton, isSCMViewService, isSCMHistoryItemGroupTreeElement, isSCMHistoryItemTreeElement, isSCMHistoryItemChangeTreeElement, toDiffEditorArguments, isSCMResourceNode, isSCMHistoryItemChangeNode, isSCMViewSeparator } from './util';
2828
import { WorkbenchCompressibleAsyncDataTree, IOpenEvent } from 'vs/platform/list/browser/listService';
2929
import { IConfigurationService, ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
30-
import { disposableTimeout, ThrottledDelayer, Sequencer } from 'vs/base/common/async';
30+
import { disposableTimeout, Sequencer, ThrottledDelayer, Throttler } from 'vs/base/common/async';
3131
import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
3232
import { ResourceTree, IResourceNode } from 'vs/base/common/resourceTree';
3333
import { ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree';
@@ -2504,6 +2504,7 @@ export class SCMViewPane extends ViewPane {
25042504
private treeScrollTop: number | undefined;
25052505
private treeContainer!: HTMLElement;
25062506
private tree!: WorkbenchCompressibleAsyncDataTree<ISCMViewService, TreeElement, FuzzyScore>;
2507+
private treeIdentityProvider!: IIdentityProvider<TreeElement>;
25072508

25082509
private listLabels!: ResourceLabels;
25092510
private inputRenderer!: InputRenderer;
@@ -2568,7 +2569,10 @@ export class SCMViewPane extends ViewPane {
25682569

25692570
private readonly items = new DisposableMap<ISCMRepository, IDisposable>();
25702571
private readonly visibilityDisposables = new DisposableStore();
2571-
private readonly asyncOperationSequencer = new Sequencer();
2572+
2573+
private readonly treeOperationSequencer = new Sequencer();
2574+
private readonly revealResourceThrottler = new Throttler();
2575+
private readonly updateChildrenThrottler = new Throttler();
25722576

25732577
private viewModeContextKey: IContextKey<ViewMode>;
25742578
private viewSortKeyContextKey: IContextKey<ViewSortKey>;
@@ -2633,6 +2637,9 @@ export class SCMViewPane extends ViewPane {
26332637

26342638
this.disposables.add(this.instantiationService.createInstance(ScmInputContentProvider));
26352639
Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository)(() => this._onDidChangeViewWelcomeState.fire(), this, this.disposables);
2640+
2641+
this.disposables.add(this.revealResourceThrottler);
2642+
this.disposables.add(this.updateChildrenThrottler);
26362643
}
26372644

26382645
protected override layoutBody(height: number | undefined = this.layoutCache.height, width: number | undefined = this.layoutCache.width): void {
@@ -2741,6 +2748,8 @@ export class SCMViewPane extends ViewPane {
27412748
const treeDataSource = this.instantiationService.createInstance(SCMTreeDataSource, () => this.viewMode, () => this.alwaysShowRepositories, () => this.showActionButton, () => this.showIncomingChanges, () => this.showOutgoingChanges);
27422749
this.disposables.add(treeDataSource);
27432750

2751+
this.treeIdentityProvider = new SCMResourceIdentityProvider();
2752+
27442753
this.tree = this.instantiationService.createInstance(
27452754
WorkbenchCompressibleAsyncDataTree,
27462755
'SCM Tree Repo',
@@ -2765,7 +2774,7 @@ export class SCMViewPane extends ViewPane {
27652774
transformOptimization: false,
27662775
filter: new SCMTreeFilter(),
27672776
dnd: new SCMTreeDragAndDrop(this.instantiationService),
2768-
identityProvider: new SCMResourceIdentityProvider(),
2777+
identityProvider: this.treeIdentityProvider,
27692778
sorter: new SCMTreeSorter(() => this.viewMode, () => this.viewSortKey),
27702779
keyboardNavigationLabelProvider: this.instantiationService.createInstance(SCMTreeKeyboardNavigationLabelProvider, () => this.viewMode),
27712780
overrideStyles: {
@@ -2889,30 +2898,32 @@ export class SCMViewPane extends ViewPane {
28892898
return;
28902899
}
28912900

2892-
this.asyncOperationSequencer.queue(async () => {
2893-
for (const repository of this.scmViewService.visibleRepositories) {
2894-
const item = this.items.get(repository);
2901+
this.revealResourceThrottler.queue(
2902+
() => this.treeOperationSequencer.queue(
2903+
async () => {
2904+
for (const repository of this.scmViewService.visibleRepositories) {
2905+
const item = this.items.get(repository);
28952906

2896-
if (!item) {
2897-
continue;
2898-
}
2907+
if (!item) {
2908+
continue;
2909+
}
28992910

2900-
// go backwards from last group
2901-
for (let j = repository.provider.groups.length - 1; j >= 0; j--) {
2902-
const groupItem = repository.provider.groups[j];
2903-
const resource = this.viewMode === ViewMode.Tree
2904-
? groupItem.resourceTree.getNode(uri)?.element
2905-
: groupItem.resources.find(r => this.uriIdentityService.extUri.isEqual(r.sourceUri, uri));
2906-
2907-
if (resource) {
2908-
await this.tree.expandTo(resource);
2909-
this.tree.setSelection([resource]);
2910-
this.tree.setFocus([resource]);
2911-
return;
2911+
// go backwards from last group
2912+
for (let j = repository.provider.groups.length - 1; j >= 0; j--) {
2913+
const groupItem = repository.provider.groups[j];
2914+
const resource = this.viewMode === ViewMode.Tree
2915+
? groupItem.resourceTree.getNode(uri)?.element
2916+
: groupItem.resources.find(r => this.uriIdentityService.extUri.isEqual(r.sourceUri, uri));
2917+
2918+
if (resource) {
2919+
await this.tree.expandTo(resource);
2920+
this.tree.setSelection([resource]);
2921+
this.tree.setFocus([resource]);
2922+
return;
2923+
}
2924+
}
29122925
}
2913-
}
2914-
}
2915-
});
2926+
}));
29162927
}
29172928

29182929
private onDidChangeVisibleRepositories({ added, removed }: ISCMViewVisibleRepositoryChangeEvent): void {
@@ -3084,23 +3095,31 @@ export class SCMViewPane extends ViewPane {
30843095
}
30853096

30863097
private updateChildren(element?: ISCMRepository) {
3087-
this.asyncOperationSequencer.queue(async () => {
3088-
const focusedInput = this.inputRenderer.getFocusedInput();
3089-
3090-
if (element && this.tree.hasNode(element)) {
3091-
// Refresh specific repository
3092-
await this.tree.updateChildren(element);
3093-
} else {
3094-
// Refresh the entire tree
3095-
await this.tree.updateChildren();
3096-
}
3098+
this.updateChildrenThrottler.queue(
3099+
() => this.treeOperationSequencer.queue(
3100+
async () => {
3101+
const focusedInput = this.inputRenderer.getFocusedInput();
3102+
3103+
if (element && this.tree.hasNode(element)) {
3104+
// Refresh specific repository
3105+
await this.tree.updateChildren(element, true, false, {
3106+
diffDepth: Infinity,
3107+
diffIdentityProvider: this.treeIdentityProvider
3108+
});
3109+
} else {
3110+
// Refresh the entire tree
3111+
await this.tree.updateChildren(undefined, true, false, {
3112+
diffDepth: Infinity,
3113+
diffIdentityProvider: this.treeIdentityProvider
3114+
});
3115+
}
30973116

3098-
if (focusedInput) {
3099-
this.inputRenderer.getRenderedInputWidget(focusedInput)?.forEach(widget => widget.focus());
3100-
}
3117+
if (focusedInput) {
3118+
this.inputRenderer.getRenderedInputWidget(focusedInput)?.forEach(widget => widget.focus());
3119+
}
31013120

3102-
this.updateRepositoryCollapseAllContextKeys();
3103-
});
3121+
this.updateRepositoryCollapseAllContextKeys();
3122+
}));
31043123
}
31053124

31063125
private updateIndentStyles(theme: IFileIconTheme): void {

0 commit comments

Comments
 (0)