From cfe7ef4af80207643fbb1b9b51eb2019d6e66572 Mon Sep 17 00:00:00 2001 From: Matt Jackson Date: Sat, 27 Jul 2024 10:34:58 -0400 Subject: [PATCH] optimize document sorting and update documentation --- .../src/references/scope-computation.ts | 7 ++- .../langium/src/workspace/document-builder.ts | 56 ++++++++++++++----- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/packages/langium/src/references/scope-computation.ts b/packages/langium/src/references/scope-computation.ts index f2dc81cd8..b84333099 100644 --- a/packages/langium/src/references/scope-computation.ts +++ b/packages/langium/src/references/scope-computation.ts @@ -73,8 +73,9 @@ export class DefaultScopeComputation implements ScopeComputation { this.descriptions = services.workspace.AstNodeDescriptionProvider; } - async computeExports(document: LangiumDocument, cancelToken = CancellationToken.None): Promise { - return this.computeExportsForNode(document.parseResult.value, document, undefined, cancelToken); + async computeExports(document: LangiumDocument, cancelToken?: CancellationToken): Promise { + const token = cancelToken ?? CancellationToken.None; + return this.computeExportsForNode(document.parseResult.value, document, undefined, token); } /** @@ -86,7 +87,7 @@ export class DefaultScopeComputation implements ScopeComputation { * @param document The document containing the AST node to be exported. * @param children A function called with {@link parentNode} as single argument and returning an {@link Iterable} supplying the children to be visited, which must be directly or transitively contained in {@link parentNode}. * @param cancelToken Indicates when to cancel the current operation. - * @throws `OperationCanceled` if a user action occurs during execution. + * @throws `OperationCancelled` if a user action occurs during execution. * @returns A list of {@link AstNodeDescription AstNodeDescriptions} to be published to index. */ async computeExportsForNode(parentNode: AstNode, document: LangiumDocument, children: (root: AstNode) => Iterable = streamContents, cancelToken: CancellationToken = CancellationToken.None): Promise { diff --git a/packages/langium/src/workspace/document-builder.ts b/packages/langium/src/workspace/document-builder.ts index ff98ed7c9..0331febb9 100644 --- a/packages/langium/src/workspace/document-builder.ts +++ b/packages/langium/src/workspace/document-builder.ts @@ -251,21 +251,28 @@ export class DefaultDocumentBuilder implements DocumentBuilder { * in files that are currently not opened in the editor. */ protected sortDocuments(documents: LangiumDocument[]): LangiumDocument[] { - const hasTextDocument = new Map(); - for (const doc of documents) { - hasTextDocument.set(doc, Boolean(this.textDocuments?.get(doc.uri.toString()))); - } - return documents.sort((a, b) => { - const aHasDoc = hasTextDocument.get(a); - const bHasDoc = hasTextDocument.get(b); - if (aHasDoc && !bHasDoc) { - return -1; - } else if (!aHasDoc && bHasDoc) { - return 1; - } else { - return 0; + let left = 0; + let right = documents.length - 1; + + while (left < right) { + while (left < documents.length && this.hasTextDocument(documents[left])) { + left++; } - }); + + while (right >= 0 && !this.hasTextDocument(documents[right])) { + right--; + } + + if (left < right) { + [documents[left], documents[right]] = [documents[right], documents[left]]; + } + } + + return documents; + } + + private hasTextDocument(doc: LangiumDocument): boolean { + return Boolean(this.textDocuments?.get(doc.uri.toString())); } /** @@ -293,6 +300,11 @@ export class DefaultDocumentBuilder implements DocumentBuilder { /** * Build the given documents by stepping through all build phases. If a document's state indicates * that a certain build phase is already done, the phase is skipped for that document. + * + * @param documents The documents to build. + * @param options the {@link BuildOptions} to use. + * @param cancelToken A cancellation token that can be used to cancel the build. + * @returns A promise that resolves when the build is done. */ protected async buildDocuments(documents: LangiumDocument[], options: BuildOptions, cancelToken: CancellationToken): Promise { this.prepareBuild(documents, options); @@ -333,6 +345,12 @@ export class DefaultDocumentBuilder implements DocumentBuilder { } } + /** + * Runs prior to beginning the build process to update the {@link DocumentBuildState} for each document + * + * @param documents collection of documents to be built + * @param options the {@link BuildOptions} to use + */ protected prepareBuild(documents: LangiumDocument[], options: BuildOptions): void { for (const doc of documents) { const key = doc.uri.toString(); @@ -350,6 +368,16 @@ export class DefaultDocumentBuilder implements DocumentBuilder { } } + /** + * Runs a cancelable operation on a set of documents to bring them to a specified {@link DocumentState}. + * + * @param documents The array of documents to process. + * @param targetState The target {@link DocumentState} to bring the documents to. + * @param cancelToken A token that can be used to cancel the operation. + * @param callback A function to be called for each document. + * @returns A promise that resolves when all documents have been processed or the operation is canceled. + * @throws Will throw `OperationCancelled` if the operation is canceled via a `CancellationToken`. + */ protected async runCancelable(documents: LangiumDocument[], targetState: DocumentState, cancelToken: CancellationToken, callback: (document: LangiumDocument) => MaybePromise): Promise { const filtered = documents.filter(doc => doc.state < targetState);