Skip to content

Commit 70c7237

Browse files
authored
Move almost everything out of the tree view constructor (microsoft#159553)
1 parent dffbdeb commit 70c7237

File tree

1 file changed

+81
-28
lines changed

1 file changed

+81
-28
lines changed

src/vs/workbench/browser/parts/views/treeView.ts

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -176,22 +176,22 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
176176
private _hasIconForParentNode = false;
177177
private _hasIconForLeafNode = false;
178178

179-
private readonly collapseAllContextKey: RawContextKey<boolean>;
180-
private readonly collapseAllContext: IContextKey<boolean>;
181-
private readonly collapseAllToggleContextKey: RawContextKey<boolean>;
182-
private readonly collapseAllToggleContext: IContextKey<boolean>;
183-
private readonly refreshContextKey: RawContextKey<boolean>;
184-
private readonly refreshContext: IContextKey<boolean>;
179+
private collapseAllContextKey: RawContextKey<boolean> | undefined;
180+
private collapseAllContext: IContextKey<boolean> | undefined;
181+
private collapseAllToggleContextKey: RawContextKey<boolean> | undefined;
182+
private collapseAllToggleContext: IContextKey<boolean> | undefined;
183+
private refreshContextKey: RawContextKey<boolean> | undefined;
184+
private refreshContext: IContextKey<boolean> | undefined;
185185

186186
private focused: boolean = false;
187187
private domNode!: HTMLElement;
188-
private treeContainer!: HTMLElement;
188+
private treeContainer: HTMLElement | undefined;
189189
private _messageValue: string | undefined;
190190
private _canSelectMany: boolean = false;
191-
private messageElement!: HTMLDivElement;
191+
private messageElement: HTMLElement | undefined;
192192
private tree: Tree | undefined;
193193
private treeLabels: ResourceLabels | undefined;
194-
private treeViewDnd: CustomTreeViewDragAndDrop;
194+
private treeViewDnd: CustomTreeViewDragAndDrop | undefined;
195195
private _container: HTMLElement | undefined;
196196

197197
private root: ITreeItem;
@@ -242,13 +242,30 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
242242
) {
243243
super();
244244
this.root = new Root();
245-
this.collapseAllContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableCollapseAll`, false, localize('treeView.enableCollapseAll', "Whether the the tree view with id {0} enables collapse all.", this.id));
246-
this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService);
247-
this.collapseAllToggleContextKey = new RawContextKey<boolean>(`treeView.${this.id}.toggleCollapseAll`, false, localize('treeView.toggleCollapseAll', "Whether collapse all is toggled for the tree view with id {0}.", this.id));
248-
this.collapseAllToggleContext = this.collapseAllToggleContextKey.bindTo(contextKeyService);
249-
this.refreshContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableRefresh`, false, localize('treeView.enableRefresh', "Whether the tree view with id {0} enables refresh.", this.id));
250-
this.refreshContext = this.refreshContextKey.bindTo(contextKeyService);
245+
// Try not to add anything that could be costly to this constructor. It gets called once per tree view
246+
// during startup, and anything added here can affect performance.
247+
}
248+
249+
private _isInitialized: boolean = false;
250+
private initialize() {
251+
if (this._isInitialized) {
252+
return;
253+
}
254+
this._isInitialized = true;
255+
256+
// Remember when adding to this method that it isn't called until the the view is visible, meaning that
257+
// properties could be set and events could be fired before we're initialized and that this needs to be handled.
258+
259+
this.contextKeyService.bufferChangeEvents(() => {
260+
this.initializeShowCollapseAllAction();
261+
this.initializeCollapseAllToggle();
262+
this.initializeShowRefreshAction();
263+
});
264+
251265
this.treeViewDnd = this.instantiationService.createInstance(CustomTreeViewDragAndDrop, this.id);
266+
if (this._dragAndDropController) {
267+
this.treeViewDnd.controller = this._dragAndDropController;
268+
}
252269

253270
this._register(this.themeService.onDidFileIconThemeChange(() => this.tree?.rerender()));
254271
this._register(this.themeService.onDidColorThemeChange(() => this.tree?.rerender()));
@@ -280,7 +297,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
280297
}
281298
set dragAndDropController(dnd: ITreeViewDragAndDropController | undefined) {
282299
this._dragAndDropController = dnd;
283-
this.treeViewDnd.controller = dnd;
300+
if (this.treeViewDnd) {
301+
this.treeViewDnd.controller = dnd;
302+
}
284303
}
285304

286305
private _dataProvider: ITreeViewDataProvider | undefined;
@@ -418,20 +437,40 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
418437
return this.isVisible;
419438
}
420439

440+
private initializeShowCollapseAllAction(startingValue: boolean = false) {
441+
if (!this.collapseAllContext) {
442+
this.collapseAllContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableCollapseAll`, startingValue, localize('treeView.enableCollapseAll', "Whether the the tree view with id {0} enables collapse all.", this.id));
443+
this.collapseAllContext = this.collapseAllContextKey.bindTo(this.contextKeyService);
444+
}
445+
return true;
446+
}
447+
421448
get showCollapseAllAction(): boolean {
422-
return !!this.collapseAllContext.get();
449+
this.initializeShowCollapseAllAction();
450+
return !!this.collapseAllContext?.get();
423451
}
424452

425453
set showCollapseAllAction(showCollapseAllAction: boolean) {
426-
this.collapseAllContext.set(showCollapseAllAction);
454+
this.initializeShowCollapseAllAction(showCollapseAllAction);
455+
this.collapseAllContext?.set(showCollapseAllAction);
456+
}
457+
458+
459+
private initializeShowRefreshAction(startingValue: boolean = false) {
460+
if (!this.refreshContext) {
461+
this.refreshContextKey = new RawContextKey<boolean>(`treeView.${this.id}.enableRefresh`, startingValue, localize('treeView.enableRefresh', "Whether the tree view with id {0} enables refresh.", this.id));
462+
this.refreshContext = this.refreshContextKey.bindTo(this.contextKeyService);
463+
}
427464
}
428465

429466
get showRefreshAction(): boolean {
430-
return !!this.refreshContext.get();
467+
this.initializeShowRefreshAction();
468+
return !!this.refreshContext?.get();
431469
}
432470

433471
set showRefreshAction(showRefreshAction: boolean) {
434-
this.refreshContext.set(showRefreshAction);
472+
this.initializeShowRefreshAction(showRefreshAction);
473+
this.refreshContext?.set(showRefreshAction);
435474
}
436475

437476
private registerActions() {
@@ -478,6 +517,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
478517
}
479518

480519
setVisibility(isVisible: boolean): void {
520+
this.initialize();
481521
isVisible = !!isVisible;
482522
if (this.isVisible === isVisible) {
483523
return;
@@ -548,7 +588,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
548588
const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner);
549589
const widgetAriaLabel = this._title;
550590

551-
this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer],
591+
this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer!, new TreeViewDelegate(), [renderer],
552592
dataSource, {
553593
identityProvider: new TreeViewIdentityProvider(),
554594
accessibilityProvider: {
@@ -709,9 +749,12 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
709749
}
710750

711751
private showMessage(message: string): void {
752+
this._messageValue = message;
753+
if (!this.messageElement) {
754+
return;
755+
}
712756
this.messageElement.classList.remove('hide');
713757
this.resetMessageElement();
714-
this._messageValue = message;
715758
if (!isFalsyOrWhitespace(this._message)) {
716759
this.messageElement.textContent = this._messageValue;
717760
}
@@ -720,18 +763,20 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
720763

721764
private hideMessage(): void {
722765
this.resetMessageElement();
723-
this.messageElement.classList.add('hide');
766+
this.messageElement?.classList.add('hide');
724767
this.layout(this._height, this._width);
725768
}
726769

727770
private resetMessageElement(): void {
728-
DOM.clearNode(this.messageElement);
771+
if (this.messageElement) {
772+
DOM.clearNode(this.messageElement);
773+
}
729774
}
730775

731776
private _height: number = 0;
732777
private _width: number = 0;
733778
layout(height: number, width: number) {
734-
if (height && width) {
779+
if (height && width && this.messageElement && this.treeContainer) {
735780
this._height = height;
736781
this._width = width;
737782
const treeHeight = height - DOM.getTotalHeight(this.messageElement);
@@ -831,23 +876,31 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
831876
}
832877
}
833878

879+
private initializeCollapseAllToggle() {
880+
if (!this.collapseAllToggleContext) {
881+
this.collapseAllToggleContextKey = new RawContextKey<boolean>(`treeView.${this.id}.toggleCollapseAll`, false, localize('treeView.toggleCollapseAll', "Whether collapse all is toggled for the tree view with id {0}.", this.id));
882+
this.collapseAllToggleContext = this.collapseAllToggleContextKey.bindTo(this.contextKeyService);
883+
}
884+
}
885+
834886
private updateCollapseAllToggle() {
835887
if (this.showCollapseAllAction) {
836-
this.collapseAllToggleContext.set(!!this.root.children && (this.root.children.length > 0) &&
888+
this.initializeCollapseAllToggle();
889+
this.collapseAllToggleContext?.set(!!this.root.children && (this.root.children.length > 0) &&
837890
this.root.children.some(value => value.collapsibleState !== TreeItemCollapsibleState.None));
838891
}
839892
}
840893

841894
private updateContentAreas(): void {
842895
const isTreeEmpty = !this.root.children || this.root.children.length === 0;
843896
// Hide tree container only when there is a message and tree is empty and not refreshing
844-
if (this._messageValue && isTreeEmpty && !this.refreshing) {
897+
if (this._messageValue && isTreeEmpty && !this.refreshing && this.treeContainer) {
845898
// If there's a dnd controller then hiding the tree prevents it from being dragged into.
846899
if (!this.dragAndDropController) {
847900
this.treeContainer.classList.add('hide');
848901
}
849902
this.domNode.setAttribute('tabindex', '0');
850-
} else {
903+
} else if (this.treeContainer) {
851904
this.treeContainer.classList.remove('hide');
852905
this.domNode.removeAttribute('tabindex');
853906
}

0 commit comments

Comments
 (0)