From 324fa41c4cb91b330565cf6d5af092324c9975df Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Mon, 13 Jan 2025 12:41:18 +0100 Subject: [PATCH] introduced dedicated interface `PrecomputedScopes`, updated implementations of `DefaultScopeComputation` and `DefaultScopeProvider` * added factory method to `DefaultScopeComputation` * added `getStream()` to new interface in addition to existing `add`, `addAll`, and `get` resembling the methods `MultiMap` in collection.ts --- .../src/language-server/domain-model-scope.ts | 10 +++++----- .../langium/src/references/scope-computation.ts | 16 +++++++++++++++- .../langium/src/references/scope-provider.ts | 10 +++++----- packages/langium/src/workspace/documents.ts | 10 +++++++--- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/examples/domainmodel/src/language-server/domain-model-scope.ts b/examples/domainmodel/src/language-server/domain-model-scope.ts index 9e067afab..cd3c8be9c 100644 --- a/examples/domainmodel/src/language-server/domain-model-scope.ts +++ b/examples/domainmodel/src/language-server/domain-model-scope.ts @@ -4,11 +4,11 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import type { AstNode, AstNodeDescription, LangiumDocument, PrecomputedScopes } from 'langium'; +import type { AstNodeDescription, LangiumDocument, PrecomputedScopes } from 'langium'; import type { DomainModelServices } from './domain-model-module.js'; import type { QualifiedNameProvider } from './domain-model-naming.js'; import type { Domainmodel, PackageDeclaration } from './generated/ast.js'; -import { AstUtils, Cancellation, DefaultScopeComputation, interruptAndCheck, MultiMap } from 'langium'; +import { AstUtils, Cancellation, DefaultScopeComputation, interruptAndCheck } from 'langium'; import { isType, isPackageDeclaration } from './generated/ast.js'; export class DomainModelScopeComputation extends DefaultScopeComputation { @@ -40,9 +40,9 @@ export class DomainModelScopeComputation extends DefaultScopeComputation { return descr; } - override async computeLocalScopes(document: LangiumDocument, cancelToken = Cancellation.CancellationToken.None): Promise { - const model = document.parseResult.value as Domainmodel; - const scopes = new MultiMap(); + override async computeLocalScopes(document: LangiumDocument, cancelToken = Cancellation.CancellationToken.None): Promise { + const model = document.parseResult.value; + const scopes = this.newPrecomputedScopes(document); await this.processContainer(model, scopes, document, cancelToken); return scopes; } diff --git a/packages/langium/src/references/scope-computation.ts b/packages/langium/src/references/scope-computation.ts index e95662b4f..8efa54e61 100644 --- a/packages/langium/src/references/scope-computation.ts +++ b/packages/langium/src/references/scope-computation.ts @@ -13,6 +13,7 @@ import { CancellationToken } from '../utils/cancellation.js'; import { streamAllContents, streamContents } from '../utils/ast-utils.js'; import { MultiMap } from '../utils/collections.js'; import { interruptAndCheck } from '../utils/promise-utils.js'; +import { stream } from '../utils/stream.js'; /** * Language-specific service for precomputing global and local scopes. The service methods are executed @@ -113,7 +114,7 @@ export class DefaultScopeComputation implements ScopeComputation { async computeLocalScopes(document: LangiumDocument, cancelToken = CancellationToken.None): Promise { const rootNode = document.parseResult.value; - const scopes = new MultiMap(); + const scopes = this.newPrecomputedScopes(document); // Here we navigate the full AST - local scopes shall be available in the whole document for (const node of streamAllContents(rootNode)) { await interruptAndCheck(cancelToken); @@ -122,6 +123,19 @@ export class DefaultScopeComputation implements ScopeComputation { return scopes; } + /** + * @returns A new precomputed scopes container instance for the given document. + */ + protected newPrecomputedScopes(_document: LangiumDocument): PrecomputedScopes { + const map = new MultiMap(); + return { + add: map.add.bind(map), + addAll: map.addAll.bind(map), + get: map.get.bind(map), + getStream: (key: AstNode) => stream(map.get(key)) + }; + } + /** * Process a single node during scopes computation. The default implementation makes the node visible * in the subtree of its container (if the node has a name). Override this method to change this, diff --git a/packages/langium/src/references/scope-provider.ts b/packages/langium/src/references/scope-provider.ts index 1dd6573a4..e43937002 100644 --- a/packages/langium/src/references/scope-provider.ts +++ b/packages/langium/src/references/scope-provider.ts @@ -56,11 +56,11 @@ export class DefaultScopeProvider implements ScopeProvider { if (precomputed) { let currentNode: AstNode | undefined = context.container; do { - const allDescriptions = precomputed.get(currentNode); - if (allDescriptions.length > 0) { - scopes.push(stream(allDescriptions).filter( - desc => this.reflection.isSubtype(desc.type, referenceType))); - } + scopes.push( + precomputed.getStream(currentNode).filter( + desc => this.reflection.isSubtype(desc.type, referenceType) + ) + ); currentNode = currentNode.$container; } while (currentNode); } diff --git a/packages/langium/src/workspace/documents.ts b/packages/langium/src/workspace/documents.ts index a94dc3563..094664ea2 100644 --- a/packages/langium/src/workspace/documents.ts +++ b/packages/langium/src/workspace/documents.ts @@ -19,7 +19,6 @@ import type { ParseResult, ParserOptions } from '../parser/langium-parser.js'; import type { ServiceRegistry } from '../service-registry.js'; import type { LangiumSharedCoreServices } from '../services.js'; import type { AstNode, AstNodeDescription, Mutable, Reference } from '../syntax-tree.js'; -import type { MultiMap } from '../utils/collections.js'; import type { Stream } from '../utils/stream.js'; import { TextDocument } from './documents.js'; import { CancellationToken } from '../utils/cancellation.js'; @@ -95,10 +94,15 @@ export enum DocumentState { } /** - * Result of the scope precomputation phase (`ScopeComputation` service). + * Result of the scope pre-computation phase (`ScopeComputation` service). * It maps every AST node to the set of symbols that are visible in the subtree of that node. */ -export type PrecomputedScopes = MultiMap +export interface PrecomputedScopes { + add(key: AstNode, value: AstNodeDescription): void + addAll(key: AstNode, values: AstNodeDescription[]): void + get(key: AstNode): readonly AstNodeDescription[] + getStream(key: AstNode): Stream +} export interface DocumentSegment { readonly range: Range