Skip to content

Commit fdf21e1

Browse files
committed
Fix #70330
1 parent 2c137e1 commit fdf21e1

File tree

5 files changed

+79
-65
lines changed

5 files changed

+79
-65
lines changed

Diff for: src/tsconfig.strictNullChecks.json

+2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@
123123
"./vs/workbench/api/node/extHostTerminalService.ts",
124124
"./vs/workbench/api/node/extHostTextEditor.ts",
125125
"./vs/workbench/api/node/extHostTextEditors.ts",
126+
"./vs/workbench/api/node/extHostTreeViews.ts",
127+
"./vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts",
126128
"./vs/workbench/api/node/extHostTypeConverters.ts",
127129
"./vs/workbench/api/node/extHostTypes.ts",
128130
"./vs/workbench/api/node/extHostUrls.ts",

Diff for: src/vs/workbench/api/node/extHostTreeViews.ts

+73-61
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
2222

2323
type TreeItemHandle = string;
2424

25-
function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeItemLabel {
25+
function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeItemLabel | undefined {
2626
if (isString(label)) {
2727
return { label };
2828
}
@@ -31,7 +31,7 @@ function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeIte
3131
&& typeof label === 'object'
3232
&& typeof label.label === 'string') {
3333
checkProposedApiEnabled(extension);
34-
let highlights: [number, number][] = undefined;
34+
let highlights: [number, number][] | undefined = undefined;
3535
if (Array.isArray(label.highlights)) {
3636
highlights = (<[number, number][]>label.highlights).filter((highlight => highlight.length === 2 && typeof highlight[0] === 'number' && typeof highlight[1] === 'number'));
3737
highlights = highlights.length ? highlights : undefined;
@@ -136,14 +136,15 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
136136
}
137137
}
138138

139+
type Root = null | undefined;
140+
type TreeData<T> = { message: boolean, element: T | Root | false };
141+
139142
interface TreeNode {
140143
item: ITreeItem;
141-
parent: TreeNode;
142-
children: TreeNode[];
144+
parent: TreeNode | Root;
145+
children?: TreeNode[];
143146
}
144147

145-
type TreeData<T> = { message: boolean, element: T | null | undefined | false };
146-
147148
class ExtHostTreeView<T> extends Disposable {
148149

149150
private static LABEL_HANDLE_PREFIX = '0';
@@ -159,7 +160,7 @@ class ExtHostTreeView<T> extends Disposable {
159160
get visible(): boolean { return this._visible; }
160161

161162
private _selectedHandles: TreeItemHandle[] = [];
162-
get selectedElements(): T[] { return this._selectedHandles.map(handle => this.getExtensionElement(handle)).filter(element => !isUndefinedOrNull(element)); }
163+
get selectedElements(): T[] { return <T[]>this._selectedHandles.map(handle => this.getExtensionElement(handle)).filter(element => !isUndefinedOrNull(element)); }
163164

164165
private _onDidExpandElement: Emitter<vscode.TreeViewExpansionEvent<T>> = this._register(new Emitter<vscode.TreeViewExpansionEvent<T>>());
165166
readonly onDidExpandElement: Event<vscode.TreeViewExpansionEvent<T>> = this._onDidExpandElement.event;
@@ -186,7 +187,7 @@ class ExtHostTreeView<T> extends Disposable {
186187
}
187188

188189
let refreshingPromise, promiseCallback;
189-
this._register(Event.debounce<TreeData<T>, { message: boolean, elements: T[] }>(this._onDidChangeData.event, (result, current) => {
190+
this._register(Event.debounce<TreeData<T>, { message: boolean, elements: (T | Root)[] }>(this._onDidChangeData.event, (result, current) => {
190191
if (!result) {
191192
result = { message: false, elements: [] };
192193
}
@@ -214,7 +215,7 @@ class ExtHostTreeView<T> extends Disposable {
214215
}));
215216
}
216217

217-
getChildren(parentHandle?: TreeItemHandle): Promise<ITreeItem[]> {
218+
getChildren(parentHandle: TreeItemHandle | Root): Promise<ITreeItem[]> {
218219
const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : undefined;
219220
if (parentHandle && !parentElement) {
220221
console.error(`No tree item with id \'${parentHandle}\' found.`);
@@ -226,7 +227,7 @@ class ExtHostTreeView<T> extends Disposable {
226227
.then(nodes => nodes.map(n => n.item));
227228
}
228229

229-
getExtensionElement(treeItemHandle: TreeItemHandle): T {
230+
getExtensionElement(treeItemHandle: TreeItemHandle): T | undefined {
230231
return this.elements.get(treeItemHandle);
231232
}
232233

@@ -295,12 +296,12 @@ class ExtHostTreeView<T> extends Disposable {
295296
});
296297
}
297298

298-
private resolveParent(element: T): Promise<T> {
299+
private resolveParent(element: T): Promise<T | Root> {
299300
const node = this.nodes.get(element);
300301
if (node) {
301-
return Promise.resolve(node.parent ? this.elements.get(node.parent.item.handle) : null);
302+
return Promise.resolve(node.parent ? this.elements.get(node.parent.item.handle) : undefined);
302303
}
303-
return asPromise(() => this.dataProvider.getParent(element));
304+
return asPromise(() => this.dataProvider.getParent!(element));
304305
}
305306

306307
private resolveTreeNode(element: T, parent?: TreeNode): Promise<TreeNode> {
@@ -310,7 +311,7 @@ class ExtHostTreeView<T> extends Disposable {
310311
}
311312
return asPromise(() => this.dataProvider.getTreeItem(element))
312313
.then(extTreeItem => this.createHandle(element, extTreeItem, parent, true))
313-
.then(handle => this.getChildren(parent ? parent.item.handle : null)
314+
.then(handle => this.getChildren(parent ? parent.item.handle : undefined)
314315
.then(() => {
315316
const cachedElement = this.getExtensionElement(handle);
316317
if (cachedElement) {
@@ -323,16 +324,16 @@ class ExtHostTreeView<T> extends Disposable {
323324
}));
324325
}
325326

326-
private getChildrenNodes(parentNodeOrHandle?: TreeNode | TreeItemHandle): TreeNode[] {
327+
private getChildrenNodes(parentNodeOrHandle: TreeNode | TreeItemHandle | Root): TreeNode[] | null {
327328
if (parentNodeOrHandle) {
328-
let parentNode: TreeNode;
329+
let parentNode: TreeNode | undefined;
329330
if (typeof parentNodeOrHandle === 'string') {
330331
const parentElement = this.getExtensionElement(parentNodeOrHandle);
331-
parentNode = parentElement ? this.nodes.get(parentElement) : null;
332+
parentNode = parentElement ? this.nodes.get(parentElement) : undefined;
332333
} else {
333334
parentNode = parentNodeOrHandle;
334335
}
335-
return parentNode ? parentNode.children : null;
336+
return parentNode ? parentNode.children || null : null;
336337
}
337338
return this.roots;
338339
}
@@ -350,13 +351,13 @@ class ExtHostTreeView<T> extends Disposable {
350351
.then(coalesce);
351352
}
352353

353-
private refresh(elements: T[]): Promise<void> {
354+
private refresh(elements: (T | Root)[]): Promise<void> {
354355
const hasRoot = elements.some(element => !element);
355356
if (hasRoot) {
356357
this.clearAll(); // clear cache
357358
return this.proxy.$refresh(this.viewId);
358359
} else {
359-
const handlesToRefresh = this.getHandlesToRefresh(elements);
360+
const handlesToRefresh = this.getHandlesToRefresh(<T[]>elements);
360361
if (handlesToRefresh.length) {
361362
return this.refreshHandles(handlesToRefresh);
362363
}
@@ -370,12 +371,12 @@ class ExtHostTreeView<T> extends Disposable {
370371
const elementNode = this.nodes.get(element);
371372
if (elementNode && !elementsToUpdate.has(elementNode.item.handle)) {
372373
// check if an ancestor of extElement is already in the elements to update list
373-
let currentNode = elementNode;
374+
let currentNode: TreeNode | undefined = elementNode;
374375
while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent.item.handle)) {
375376
const parentElement = this.elements.get(currentNode.parent.item.handle);
376-
currentNode = this.nodes.get(parentElement);
377+
currentNode = parentElement ? this.nodes.get(parentElement) : undefined;
377378
}
378-
if (!currentNode.parent) {
379+
if (currentNode && !currentNode.parent) {
379380
elementsToUpdate.add(elementNode.item.handle);
380381
}
381382
}
@@ -385,9 +386,11 @@ class ExtHostTreeView<T> extends Disposable {
385386
// Take only top level elements
386387
elementsToUpdate.forEach((handle) => {
387388
const element = this.elements.get(handle);
388-
const node = this.nodes.get(element);
389-
if (node && (!node.parent || !elementsToUpdate.has(node.parent.item.handle))) {
390-
handlesToUpdate.push(handle);
389+
if (element) {
390+
const node = this.nodes.get(element);
391+
if (node && (!node.parent || !elementsToUpdate.has(node.parent.item.handle))) {
392+
handlesToUpdate.push(handle);
393+
}
391394
}
392395
});
393396

@@ -403,25 +406,30 @@ class ExtHostTreeView<T> extends Disposable {
403406
itemsToRefresh[treeItemHandle] = node.item;
404407
}
405408
})))
406-
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : null);
409+
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : undefined);
407410
}
408411

409-
private refreshNode(treeItemHandle: TreeItemHandle): Promise<TreeNode> {
412+
private refreshNode(treeItemHandle: TreeItemHandle): Promise<TreeNode | null> {
410413
const extElement = this.getExtensionElement(treeItemHandle);
411-
const existing = this.nodes.get(extElement);
412-
this.clearChildren(extElement); // clear children cache
413-
return asPromise(() => this.dataProvider.getTreeItem(extElement))
414-
.then(extTreeItem => {
415-
if (extTreeItem) {
416-
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
417-
this.updateNodeCache(extElement, newNode, existing, existing.parent);
418-
return newNode;
419-
}
420-
return null;
421-
});
414+
if (extElement) {
415+
const existing = this.nodes.get(extElement);
416+
if (existing) {
417+
this.clearChildren(extElement); // clear children cache
418+
return asPromise(() => this.dataProvider.getTreeItem(extElement))
419+
.then(extTreeItem => {
420+
if (extTreeItem) {
421+
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
422+
this.updateNodeCache(extElement, newNode, existing, existing.parent);
423+
return newNode;
424+
}
425+
return null;
426+
});
427+
}
428+
}
429+
return Promise.resolve(null);
422430
}
423431

424-
private createAndRegisterTreeNode(element: T, extTreeItem: vscode.TreeItem, parentNode: TreeNode): TreeNode {
432+
private createAndRegisterTreeNode(element: T, extTreeItem: vscode.TreeItem, parentNode: TreeNode | Root): TreeNode {
425433
const node = this.createTreeNode(element, extTreeItem, parentNode);
426434
if (extTreeItem.id && this.elements.has(node.item.handle)) {
427435
throw new Error(localize('treeView.duplicateElement', 'Element with id {0} is already registered', extTreeItem.id));
@@ -431,15 +439,15 @@ class ExtHostTreeView<T> extends Disposable {
431439
return node;
432440
}
433441

434-
private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode): TreeNode {
442+
private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): TreeNode {
435443
return {
436444
item: this.createTreeItem(element, extensionTreeItem, parent),
437445
parent,
438446
children: undefined
439447
};
440448
}
441449

442-
private createTreeItem(element: T, extensionTreeItem: vscode.TreeItem, parent?: TreeNode): ITreeItem {
450+
private createTreeItem(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): ITreeItem {
443451

444452
const handle = this.createHandle(element, extensionTreeItem, parent);
445453
const icon = this.getLightIconPath(extensionTreeItem);
@@ -461,7 +469,7 @@ class ExtHostTreeView<T> extends Disposable {
461469
return item;
462470
}
463471

464-
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode, returnFirst?: boolean): TreeItemHandle {
472+
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode | Root, returnFirst?: boolean): TreeItemHandle {
465473
if (id) {
466474
return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`;
467475
}
@@ -470,7 +478,7 @@ class ExtHostTreeView<T> extends Disposable {
470478
const prefix: string = parent ? parent.item.handle : ExtHostTreeView.LABEL_HANDLE_PREFIX;
471479
let elementId = treeItemLabel ? treeItemLabel.label : resourceUri ? basename(resourceUri) : '';
472480
elementId = elementId.indexOf('/') !== -1 ? elementId.replace('/', '//') : elementId;
473-
const existingHandle = this.nodes.has(element) ? this.nodes.get(element).item.handle : undefined;
481+
const existingHandle = this.nodes.has(element) ? this.nodes.get(element)!.item.handle : undefined;
474482
const childrenNodes = (this.getChildrenNodes(parent) || []);
475483

476484
let handle: TreeItemHandle;
@@ -489,7 +497,7 @@ class ExtHostTreeView<T> extends Disposable {
489497
return handle;
490498
}
491499

492-
private getLightIconPath(extensionTreeItem: vscode.TreeItem): URI {
500+
private getLightIconPath(extensionTreeItem: vscode.TreeItem): URI | undefined {
493501
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon)) {
494502
if (typeof extensionTreeItem.iconPath === 'string'
495503
|| extensionTreeItem.iconPath instanceof URI) {
@@ -500,7 +508,7 @@ class ExtHostTreeView<T> extends Disposable {
500508
return undefined;
501509
}
502510

503-
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): URI {
511+
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): URI | undefined {
504512
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon) && extensionTreeItem.iconPath['dark']) {
505513
return this.getIconPath(extensionTreeItem.iconPath['dark']);
506514
}
@@ -519,7 +527,7 @@ class ExtHostTreeView<T> extends Disposable {
519527
this.nodes.set(element, node);
520528
}
521529

522-
private updateNodeCache(element: T, newNode: TreeNode, existing: TreeNode, parentNode: TreeNode): void {
530+
private updateNodeCache(element: T, newNode: TreeNode, existing: TreeNode, parentNode: TreeNode | Root): void {
523531
// Remove from the cache
524532
this.elements.delete(newNode.item.handle);
525533
this.nodes.delete(element);
@@ -538,7 +546,7 @@ class ExtHostTreeView<T> extends Disposable {
538546
}
539547
}
540548

541-
private addNodeToParentCache(node: TreeNode, parentNode: TreeNode): void {
549+
private addNodeToParentCache(node: TreeNode, parentNode: TreeNode | Root): void {
542550
if (parentNode) {
543551
if (!parentNode.children) {
544552
parentNode.children = [];
@@ -555,32 +563,36 @@ class ExtHostTreeView<T> extends Disposable {
555563
private clearChildren(parentElement?: T): void {
556564
if (parentElement) {
557565
const node = this.nodes.get(parentElement);
558-
if (node.children) {
559-
for (const child of node.children) {
560-
const childEleement = this.elements.get(child.item.handle);
561-
if (childEleement) {
562-
this.clear(childEleement);
566+
if (node) {
567+
if (node.children) {
568+
for (const child of node.children) {
569+
const childEleement = this.elements.get(child.item.handle);
570+
if (childEleement) {
571+
this.clear(childEleement);
572+
}
563573
}
564574
}
575+
node.children = undefined;
565576
}
566-
node.children = undefined;
567577
} else {
568578
this.clearAll();
569579
}
570580
}
571581

572582
private clear(element: T): void {
573583
const node = this.nodes.get(element);
574-
if (node.children) {
575-
for (const child of node.children) {
576-
const childEleement = this.elements.get(child.item.handle);
577-
if (childEleement) {
578-
this.clear(childEleement);
584+
if (node) {
585+
if (node.children) {
586+
for (const child of node.children) {
587+
const childEleement = this.elements.get(child.item.handle);
588+
if (childEleement) {
589+
this.clear(childEleement);
590+
}
579591
}
580592
}
593+
this.nodes.delete(element);
594+
this.elements.delete(node.item.handle);
581595
}
582-
this.nodes.delete(element);
583-
this.elements.delete(node.item.handle);
584596
}
585597

586598
private clearAll(): void {

Diff for: src/vs/workbench/browser/parts/views/customView.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class TitleMenus implements IDisposable {
164164
class Root implements ITreeItem {
165165
label = { label: 'root' };
166166
handle = '0';
167-
parentHandle: string | null = null;
167+
parentHandle: string | undefined = undefined;
168168
collapsibleState = TreeItemCollapsibleState.Expanded;
169169
children: ITreeItem[] | undefined = undefined;
170170
}

Diff for: src/vs/workbench/common/views.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ export interface ITreeItem {
382382

383383
handle: string;
384384

385-
parentHandle: string | null;
385+
parentHandle?: string;
386386

387387
collapsibleState: TreeItemCollapsibleState;
388388

Diff for: src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ suite('ExtHostTreeView', function () {
2929
$registerTreeViewDataProvider(treeViewId: string): void {
3030
}
3131

32-
$refresh(viewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise<void> {
32+
$refresh(viewId: string, itemsToRefresh: { [treeItemHandle: string]: ITreeItem }): Promise<void> {
3333
return Promise.resolve(null).then(() => this.onRefresh.fire(itemsToRefresh));
3434
}
3535

3636
$reveal(): Promise<void> {
37-
return null;
37+
return Promise.resolve();
3838
}
3939

4040
}

0 commit comments

Comments
 (0)