Add fast path for synchronously resolved children (nested children)

ref #140883
This commit is contained in:
Jackson Kearl 2022-02-15 17:33:54 -08:00
parent 93a2a2fa12
commit 079a688663
No known key found for this signature in database
GPG key ID: DA09A59C409FC400
2 changed files with 58 additions and 50 deletions

View file

@ -87,14 +87,19 @@ export class ExplorerDataSource implements IAsyncDataSource<ExplorerItem | Explo
return Array.isArray(element) || element.hasChildren;
}
getChildren(element: ExplorerItem | ExplorerItem[]): Promise<ExplorerItem[]> {
getChildren(element: ExplorerItem | ExplorerItem[]): ExplorerItem[] | Promise<ExplorerItem[]> {
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) {

View file

@ -296,63 +296,66 @@ export class ExplorerItem {
return this.children.get(this.getPlatformAwareName(name));
}
async fetchChildren(sortOrder: SortOrder): Promise<ExplorerItem[]> {
fetchChildren(sortOrder: SortOrder): ExplorerItem[] | Promise<ExplorerItem[]> {
const nestingConfig = this.configService.getValue<IFilesConfiguration>().explorer.experimental.fileNesting;
// fast path when the children can be resolved sync
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;
}
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));
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 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);
}
items.push(fileEntryItem);
} else {
fileEntryItem.nestedChildren = undefined;
}
}
for (const [_, dirEntryItem] of dirChildren.values()) {
items.push(dirEntryItem);
}
} else {
this.children.forEach(child => {
items.push(child);
});
}
const nested = this.buildFileNester().nest(fileChildren.map(([name]) => name));
return items;
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;
}
}
for (const [_, dirEntryItem] of dirChildren.values()) {
items.push(dirEntryItem);
}
} else {
this.children.forEach(child => {
items.push(child);
});
}
return items;
})();
}
// TODO:@jkearl, share one nester across all explorer items and only build on config change