diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 7d8a01c841fdc..f67e14bc80a8f 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -87,14 +87,19 @@ export class ExplorerDataSource implements IAsyncDataSource { + getChildren(element: ExplorerItem | ExplorerItem[]): ExplorerItem[] | Promise { if (Array.isArray(element)) { - return Promise.resolve(element); + return element; } const wasError = element.isError; const sortOrder = this.explorerService.sortOrderConfiguration.sortOrder; - const promise = element.fetchChildren(sortOrder).then( + const children = element.fetchChildren(sortOrder); + if (Array.isArray(children)) { + // fast path when children are known sync (i.e. nested children) + return children; + } + const promise = children.then( children => { // Clear previous error decoration on root folder if (element instanceof ExplorerItem && element.isRoot && !element.isError && wasError && this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER) { diff --git a/src/vs/workbench/contrib/files/common/explorerModel.ts b/src/vs/workbench/contrib/files/common/explorerModel.ts index 03a1fab01a8de..8b284ddcb1d0b 100644 --- a/src/vs/workbench/contrib/files/common/explorerModel.ts +++ b/src/vs/workbench/contrib/files/common/explorerModel.ts @@ -296,63 +296,66 @@ export class ExplorerItem { return this.children.get(this.getPlatformAwareName(name)); } - async fetchChildren(sortOrder: SortOrder): Promise { + fetchChildren(sortOrder: SortOrder): ExplorerItem[] | Promise { const nestingConfig = this.configService.getValue().explorer.experimental.fileNesting; - if (nestingConfig.enabled && this.nestedChildren) { return this.nestedChildren; } - if (!this._isDirectoryResolved) { - // Resolve metadata only when the mtime is needed since this can be expensive - // Mtime is only used when the sort order is 'modified' - const resolveMetadata = sortOrder === SortOrder.Modified; - this.isError = false; - try { - const stat = await this.fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata }); - const resolved = ExplorerItem.create(this.fileService, this.configService, stat, this); - ExplorerItem.mergeLocalWithDisk(resolved, this); - } catch (e) { - this.isError = true; - throw e; - } - this._isDirectoryResolved = true; - } + // fast path when the children can be resolved sync + if (nestingConfig.enabled && this.nestedChildren) { return this.nestedChildren; } - const items: ExplorerItem[] = []; - if (nestingConfig.enabled) { - const fileChildren: [string, ExplorerItem][] = []; - const dirChildren: [string, ExplorerItem][] = []; - for (const child of this.children.entries()) { - if (child[1].isDirectory) { - dirChildren.push(child); - } else { - fileChildren.push(child); + return (async () => { + if (!this._isDirectoryResolved) { + // Resolve metadata only when the mtime is needed since this can be expensive + // Mtime is only used when the sort order is 'modified' + const resolveMetadata = sortOrder === SortOrder.Modified; + this.isError = false; + try { + const stat = await this.fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata }); + const resolved = ExplorerItem.create(this.fileService, this.configService, stat, this); + ExplorerItem.mergeLocalWithDisk(resolved, this); + } catch (e) { + this.isError = true; + throw e; } + this._isDirectoryResolved = true; } - const nested = this.buildFileNester().nest(fileChildren.map(([name]) => name)); + const items: ExplorerItem[] = []; + if (nestingConfig.enabled) { + const fileChildren: [string, ExplorerItem][] = []; + const dirChildren: [string, ExplorerItem][] = []; + for (const child of this.children.entries()) { + if (child[1].isDirectory) { + dirChildren.push(child); + } else { + fileChildren.push(child); + } + } - for (const [fileEntryName, fileEntryItem] of fileChildren) { - const nestedItems = nested.get(fileEntryName); - if (nestedItems !== undefined) { - fileEntryItem.nestedChildren = []; - for (const name of nestedItems.keys()) { - fileEntryItem.nestedChildren.push(assertIsDefined(this.children.get(name))); + const nested = this.buildFileNester().nest(fileChildren.map(([name]) => name)); + + for (const [fileEntryName, fileEntryItem] of fileChildren) { + const nestedItems = nested.get(fileEntryName); + if (nestedItems !== undefined) { + fileEntryItem.nestedChildren = []; + for (const name of nestedItems.keys()) { + fileEntryItem.nestedChildren.push(assertIsDefined(this.children.get(name))); + } + items.push(fileEntryItem); + } else { + fileEntryItem.nestedChildren = undefined; } - items.push(fileEntryItem); - } else { - fileEntryItem.nestedChildren = undefined; } - } - for (const [_, dirEntryItem] of dirChildren.values()) { - items.push(dirEntryItem); + for (const [_, dirEntryItem] of dirChildren.values()) { + items.push(dirEntryItem); + } + } else { + this.children.forEach(child => { + items.push(child); + }); } - } else { - this.children.forEach(child => { - items.push(child); - }); - } - - return items; + return items; + })(); } // TODO:@jkearl, share one nester across all explorer items and only build on config change