From d6eec5d24c16fb43032a05c0011f5773a2110cfd Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 26 Feb 2024 16:33:42 +0100 Subject: [PATCH] build: bump Langium to release candidate for 3.0 (#924) ### Summary of Changes Bump `langium` to the release candidate for v3.0 to prepare for upcoming API changes before we add more features. --- package-lock.json | 30 +-- packages/safe-ds-cli/package.json | 2 +- packages/safe-ds-cli/src/helpers/documents.ts | 7 +- packages/safe-ds-lang/package.json | 4 +- .../src/language/builtins/safe-ds-enums.ts | 8 +- .../builtins/safe-ds-module-members.ts | 4 +- .../safe-ds-documentation-provider.ts | 4 +- .../flow/safe-ds-call-graph-computer.ts | 16 +- .../generation/safe-ds-python-generator.ts | 34 ++- .../grammar/safe-ds-value-converter.ts | 4 +- .../src/language/helpers/astUtils.ts | 4 +- .../src/language/helpers/fileExtensions.ts | 8 +- .../src/language/helpers/nodeProperties.ts | 10 +- .../language/helpers/safe-ds-node-mapper.ts | 20 +- packages/safe-ds-lang/src/language/index.ts | 1 + .../lsp/safe-ds-call-hierarchy-provider.ts | 30 ++- .../lsp/safe-ds-document-symbol-provider.ts | 3 +- .../src/language/lsp/safe-ds-formatter.ts | 16 +- .../lsp/safe-ds-inlay-hint-provider.ts | 3 +- .../language/lsp/safe-ds-language-server.ts | 193 ------------------ .../lsp/safe-ds-node-kind-provider.ts | 5 +- .../language/lsp/safe-ds-rename-provider.ts | 18 +- .../lsp/safe-ds-semantic-token-provider.ts | 5 +- .../lsp/safe-ds-signature-help-provider.ts | 14 +- .../lsp/safe-ds-type-hierarchy-provider.ts | 179 ++-------------- packages/safe-ds-lang/src/language/main.ts | 2 +- .../safe-ds-partial-evaluator.ts | 6 +- .../purity/safe-ds-purity-computer.ts | 18 +- .../src/language/safe-ds-module.ts | 9 +- .../scoping/safe-ds-scope-computation.ts | 12 +- .../scoping/safe-ds-scope-provider.ts | 35 ++-- .../safe-ds-lang/src/language/typing/model.ts | 4 +- .../typing/safe-ds-class-hierarchy.ts | 4 +- .../language/typing/safe-ds-type-checker.ts | 4 +- .../language/typing/safe-ds-type-computer.ts | 21 +- .../validation/builtins/pythonCall.ts | 4 +- .../experimentalLanguageFeatures.ts | 6 +- .../src/language/validation/inheritance.ts | 9 +- .../src/language/validation/names.ts | 4 +- .../validation/other/argumentLists.ts | 4 +- .../other/declarations/annotationCalls.ts | 4 +- .../other/declarations/parameters.ts | 6 +- .../other/declarations/placeholders.ts | 8 +- .../other/declarations/typeParameters.ts | 22 +- .../other/statements/assignments.ts | 4 +- .../validation/other/types/callableTypes.ts | 6 +- .../validation/other/types/unionTypes.ts | 6 +- .../src/language/validation/types.ts | 4 +- .../workspace/safe-ds-package-manager.ts | 16 +- .../workspace/safe-ds-workspace-manager.ts | 5 +- .../safe-ds-lang/tests/helpers/diagnostics.ts | 7 +- .../safe-ds-lang/tests/helpers/nodeFinder.ts | 10 +- .../tests/helpers/testResources.ts | 4 +- .../builtins/builtinFilesCorrectness.test.ts | 3 +- .../safe-ds-documentation-provider.test.ts | 3 +- .../flow/safe-ds-call-graph-computer.test.ts | 6 +- .../tests/language/generation/creator.ts | 10 +- .../safe-ds-python-generator.test.ts | 6 +- .../safe-ds-call-hierarchy-provider.test.ts | 36 ++-- .../lsp/safe-ds-rename-provider.test.ts | 2 +- .../safe-ds-type-hierarchy-provider.test.ts | 32 +-- .../tests/language/scoping/scoping.test.ts | 11 +- .../type checker/isSubOrSupertypeOf.test.ts | 4 +- .../validation/safe-ds-validator.test.ts | 5 +- .../src/extension/mainClient.ts | 11 +- 65 files changed, 318 insertions(+), 677 deletions(-) delete mode 100644 packages/safe-ds-lang/src/language/lsp/safe-ds-language-server.ts diff --git a/package-lock.json b/package-lock.json index 0a660b826..407afeda6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6736,9 +6736,9 @@ "dev": true }, "node_modules/langium": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/langium/-/langium-2.1.3.tgz", - "integrity": "sha512-/WN1xHoNBg0mi1Jp9ydMFSHIv8Jhq7K+0stNVURdoG4NgZx4/06AfNeeixmmU8X842wBl9gFZJP5O93Ge5Oasw==", + "version": "3.0.0-next.e78aeba", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0-next.e78aeba.tgz", + "integrity": "sha512-aWBY6e2DarLHTYtO6+O5m+A8Dop1WRyMyFroAj5krkU59LOQCL68/Uaydxa4XD8mVjHoKa5JtT58ztgBHwvg8A==", "dependencies": { "chevrotain": "~11.0.3", "chevrotain-allstar": "~0.3.0", @@ -6751,17 +6751,17 @@ } }, "node_modules/langium-cli": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/langium-cli/-/langium-cli-2.1.0.tgz", - "integrity": "sha512-Gbj4CvfAc1gP/6ihxikd2Je95j1FWjXZu8bbji2/t2vQ6kEP+vs9Fx7kSGOM0AbU/hjZfy6E35bJPOdwsiyqTA==", + "version": "3.0.0-next.e78aeba", + "resolved": "https://registry.npmjs.org/langium-cli/-/langium-cli-3.0.0-next.e78aeba.tgz", + "integrity": "sha512-/JHu9zenrvSouTt3VS3AWFtrZsaqTVif3a5gFDgh6ddWvY97JKxuhQ7Z7m7lyZKCX4TazG2BcQKGAT0TRD2qpg==", "dev": true, "dependencies": { "chalk": "~5.3.0", "commander": "~11.0.0", "fs-extra": "~11.1.1", "jsonschema": "~1.4.1", - "langium": "~2.1.0", - "langium-railroad": "~2.1.0", + "langium": "3.0.0-next.e78aeba", + "langium-railroad": "3.0.0-next.e78aeba", "lodash": "~4.17.21" }, "bin": { @@ -6807,12 +6807,12 @@ } }, "node_modules/langium-railroad": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/langium-railroad/-/langium-railroad-2.1.0.tgz", - "integrity": "sha512-2IeAIUSTQzbDjNnJA+0ql8tyN/mhCSN4FS50Mo9LOtLj523qUEBwHflDmCiOGZzW9iZdni6NXJgh8nLqjhTlDw==", + "version": "3.0.0-next.e78aeba", + "resolved": "https://registry.npmjs.org/langium-railroad/-/langium-railroad-3.0.0-next.e78aeba.tgz", + "integrity": "sha512-4JvT+bNkSM1sL4y1BcCLUfY6MJd5PM58UrQn52U/AfChMdYr1vrte2DcNTVSXjeaR5/zkmRs9bvThS+gdaA6BQ==", "dev": true, "dependencies": { - "langium": "~2.1.0", + "langium": "3.0.0-next.e78aeba", "railroad-diagrams": "~1.0.0" } }, @@ -14107,7 +14107,7 @@ "chalk": "^5.3.0", "commander": "^11.1.0", "glob": "^10.3.10", - "langium": "^2.1.3", + "langium": "^3.0.0-next.e78aeba", "true-myth": "^7.1.0" }, "bin": { @@ -14652,7 +14652,7 @@ "dependencies": { "chevrotain": "^11.0.3", "glob": "^10.3.10", - "langium": "^2.1.3", + "langium": "^3.0.0-next.e78aeba", "semver": "^7.6.0", "source-map": "^0.7.4", "tree-kill": "^1.2.2", @@ -14663,7 +14663,7 @@ "devDependencies": { "@types/node": "^18.18.12", "@types/ws": "^8.5.10", - "langium-cli": "^2.1.0", + "langium-cli": "^3.0.0-next.e78aeba", "true-myth": "^7.1.0" }, "engines": { diff --git a/packages/safe-ds-cli/package.json b/packages/safe-ds-cli/package.json index ab6a0c5a1..b8b0ade49 100644 --- a/packages/safe-ds-cli/package.json +++ b/packages/safe-ds-cli/package.json @@ -44,7 +44,7 @@ "chalk": "^5.3.0", "commander": "^11.1.0", "glob": "^10.3.10", - "langium": "^2.1.3", + "langium": "^3.0.0-next.e78aeba", "true-myth": "^7.1.0" }, "devDependencies": { diff --git a/packages/safe-ds-cli/src/helpers/documents.ts b/packages/safe-ds-cli/src/helpers/documents.ts index 334b6cdfd..104527306 100644 --- a/packages/safe-ds-cli/src/helpers/documents.ts +++ b/packages/safe-ds-cli/src/helpers/documents.ts @@ -1,13 +1,14 @@ -import { LangiumDocument, LangiumServices, stream, URI } from 'langium'; +import { LangiumDocument, stream, URI } from 'langium'; import fs from 'node:fs'; import path from 'node:path'; import { ExitCode } from '../cli/exitCode.js'; import { globSync } from 'glob'; import { Result } from 'true-myth'; import chalk from 'chalk'; +import { type LangiumServices } from 'langium/lsp'; /** - * Extracts a document from a file name. + * Extracts documents at the given paths. */ export const extractDocuments = async function ( services: LangiumServices, @@ -24,7 +25,7 @@ export const extractDocuments = async function ( process.exit(uris.error.code); } - const documents = uris.value.map((uri) => langiumDocuments.getOrCreateDocument(uri)); + const documents = await Promise.all(uris.value.map((uri) => langiumDocuments.getOrCreateDocument(uri))); await documentBuilder.build(documents, { validation: true }); return documents; }; diff --git a/packages/safe-ds-lang/package.json b/packages/safe-ds-lang/package.json index c26f511c6..cc49fc1b4 100644 --- a/packages/safe-ds-lang/package.json +++ b/packages/safe-ds-lang/package.json @@ -45,7 +45,7 @@ "dependencies": { "chevrotain": "^11.0.3", "glob": "^10.3.10", - "langium": "^2.1.3", + "langium": "^3.0.0-next.e78aeba", "semver": "^7.6.0", "source-map": "^0.7.4", "tree-kill": "^1.2.2", @@ -56,7 +56,7 @@ "devDependencies": { "@types/node": "^18.18.12", "@types/ws": "^8.5.10", - "langium-cli": "^2.1.0", + "langium-cli": "^3.0.0-next.e78aeba", "true-myth": "^7.1.0" }, "engines": { diff --git a/packages/safe-ds-lang/src/language/builtins/safe-ds-enums.ts b/packages/safe-ds-lang/src/language/builtins/safe-ds-enums.ts index 2625e3e90..0d19c8c12 100644 --- a/packages/safe-ds-lang/src/language/builtins/safe-ds-enums.ts +++ b/packages/safe-ds-lang/src/language/builtins/safe-ds-enums.ts @@ -1,4 +1,4 @@ -import { getContainerOfType, URI } from 'langium'; +import { AstUtils, URI } from 'langium'; import { resourceNameToUri } from '../../helpers/resources.js'; import { isSdsEnum, SdsEnum, type SdsEnumVariant } from '../generated/ast.js'; import { getEnumVariants } from '../helpers/nodeProperties.js'; @@ -15,14 +15,16 @@ export class SafeDsEnums extends SafeDsModuleMembers { } isEvaluatedAnnotationTarget = (node: EvaluatedNode): node is EvaluatedEnumVariant => - node instanceof EvaluatedEnumVariant && getContainerOfType(node.variant, isSdsEnum) === this.AnnotationTarget; + node instanceof EvaluatedEnumVariant && + AstUtils.getContainerOfType(node.variant, isSdsEnum) === this.AnnotationTarget; get ImpurityReason(): SdsEnum | undefined { return this.getEnum(PURITY_URI, 'ImpurityReason'); } isEvaluatedImpurityReason = (node: EvaluatedNode): node is EvaluatedEnumVariant => - node instanceof EvaluatedEnumVariant && getContainerOfType(node.variant, isSdsEnum) === this.ImpurityReason; + node instanceof EvaluatedEnumVariant && + AstUtils.getContainerOfType(node.variant, isSdsEnum) === this.ImpurityReason; private getEnum(uri: URI, name: string): SdsEnum | undefined { return this.getModuleMember(uri, name, isSdsEnum); diff --git a/packages/safe-ds-lang/src/language/builtins/safe-ds-module-members.ts b/packages/safe-ds-lang/src/language/builtins/safe-ds-module-members.ts index 9a93bfa52..b7c3233d6 100644 --- a/packages/safe-ds-lang/src/language/builtins/safe-ds-module-members.ts +++ b/packages/safe-ds-lang/src/language/builtins/safe-ds-module-members.ts @@ -19,12 +19,12 @@ export abstract class SafeDsModuleMembers { return this.cache.get(key); } - if (!this.langiumDocuments.hasDocument(uri)) { + const document = this.langiumDocuments.getDocument(uri); + if (!document) { /* c8 ignore next 2 */ return undefined; } - const document = this.langiumDocuments.getOrCreateDocument(uri); const root = document.parseResult.value; if (!isSdsModule(root)) { /* c8 ignore next 2 */ diff --git a/packages/safe-ds-lang/src/language/documentation/safe-ds-documentation-provider.ts b/packages/safe-ds-lang/src/language/documentation/safe-ds-documentation-provider.ts index 27c1b774c..778aee1fb 100644 --- a/packages/safe-ds-lang/src/language/documentation/safe-ds-documentation-provider.ts +++ b/packages/safe-ds-lang/src/language/documentation/safe-ds-documentation-provider.ts @@ -1,6 +1,6 @@ import { AstNode, - getContainerOfType, + AstUtils, isJSDoc, JSDocComment, JSDocDocumentationProvider, @@ -25,7 +25,7 @@ const TYPE_PARAM_TAG = 'typeParam'; export class SafeDsDocumentationProvider extends JSDocDocumentationProvider { override getDocumentation(node: AstNode): string | undefined { if (isSdsParameter(node) || isSdsResult(node) || isSdsTypeParameter(node)) { - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); /* c8 ignore start */ if (!containingCallable) { return undefined; diff --git a/packages/safe-ds-lang/src/language/flow/safe-ds-call-graph-computer.ts b/packages/safe-ds-lang/src/language/flow/safe-ds-call-graph-computer.ts index 8995875ba..aaa5e98b9 100644 --- a/packages/safe-ds-lang/src/language/flow/safe-ds-call-graph-computer.ts +++ b/packages/safe-ds-lang/src/language/flow/safe-ds-call-graph-computer.ts @@ -1,12 +1,4 @@ -import { - AstNode, - type AstNodeLocator, - getContainerOfType, - getDocument, - stream, - streamAst, - WorkspaceCache, -} from 'langium'; +import { AstNode, type AstNodeLocator, AstUtils, stream, WorkspaceCache } from 'langium'; import { isSdsBlockLambda, isSdsCall, @@ -197,7 +189,7 @@ export class SafeDsCallGraphComputer { } return [...callsInDefaultValues, ...callsInBody] - .filter((it) => getContainerOfType(it, isSdsCallable) === callable) + .filter((it) => AstUtils.getContainerOfType(it, isSdsCallable) === callable) .map((it) => this.createSyntheticCallForCall(it, substitutions)); } @@ -334,11 +326,11 @@ export class SafeDsCallGraphComputer { } const key = this.getNodeId(node); - return this.callCache.get(key, () => streamAst(node).filter(isSdsCall).toArray()); + return this.callCache.get(key, () => AstUtils.streamAst(node).filter(isSdsCall).toArray()); } private getNodeId(node: AstNode) { - const documentUri = getDocument(node).uri.toString(); + const documentUri = AstUtils.getDocument(node).uri.toString(); const nodePath = this.astNodeLocator.getAstNodePath(node); return `${documentUri}~${nodePath}`; } diff --git a/packages/safe-ds-lang/src/language/generation/safe-ds-python-generator.ts b/packages/safe-ds-lang/src/language/generation/safe-ds-python-generator.ts index 7bd647ada..b283b12e3 100644 --- a/packages/safe-ds-lang/src/language/generation/safe-ds-python-generator.ts +++ b/packages/safe-ds-lang/src/language/generation/safe-ds-python-generator.ts @@ -1,21 +1,15 @@ +import { AstUtils, LangiumDocument, TreeStreamImpl, URI } from 'langium'; import { CompositeGeneratorNode, expandToNode, expandTracedToNode, - findRootNode, - getContainerOfType, - getDocument, joinToNode, joinTracedToNode, - LangiumDocument, NL, - streamAllContents, toStringAndTrace, TraceRegion, traceToNode, - TreeStreamImpl, - URI, -} from 'langium'; +} from 'langium/generate'; import path from 'path'; import { SourceMapGenerator, StartOfSourceMap } from 'source-map'; import { TextDocument } from 'vscode-languageserver-textdocument'; @@ -310,7 +304,7 @@ export class SafeDsPythonGenerator { if (currentName) { segments.unshift(currentName); } - current = getContainerOfType(current.$container, isSdsDeclaration); + current = AstUtils.getContainerOfType(current.$container, isSdsDeclaration); } return segments.join('.'); @@ -531,14 +525,14 @@ export class SafeDsPythonGenerator { statementsWithEffect: SdsStatement[], ): SdsStatement[] { // Find assignment of placeholder, to search used placeholders and impure dependencies - const assignment = getContainerOfType(targetPlaceholder, isSdsAssignment); + const assignment = AstUtils.getContainerOfType(targetPlaceholder, isSdsAssignment); if (!assignment || !assignment.expression) { /* c8 ignore next 2 */ throw new Error(`No assignment for placeholder: ${targetPlaceholder.name}`); } // All collected referenced placeholders that are needed for calculating the target placeholder. An expression in the assignment will always exist here const referencedPlaceholders = new Set( - streamAllContents(assignment.expression!) + AstUtils.streamAllContents(assignment.expression!) .filter(isSdsReference) .filter((reference) => isSdsPlaceholder(reference.target.ref)) .map((reference) => reference.target.ref!) @@ -569,7 +563,7 @@ export class SafeDsPythonGenerator { collectedStatements.push(prevStatement); // Collect all referenced placeholders if (isSdsExpressionStatement(prevStatement) || isSdsAssignment(prevStatement)) { - streamAllContents(prevStatement.expression!) + AstUtils.streamAllContents(prevStatement.expression!) .filter(isSdsReference) .filter((reference) => isSdsPlaceholder(reference.target.ref)) .map((reference) => reference.target.ref!) @@ -595,7 +589,7 @@ export class SafeDsPythonGenerator { const blockLambdaCode: CompositeGeneratorNode[] = []; if (isSdsAssignment(statement)) { if (statement.expression) { - for (const lambda of streamAllContents(statement.expression).filter(isSdsBlockLambda)) { + for (const lambda of AstUtils.streamAllContents(statement.expression).filter(isSdsBlockLambda)) { blockLambdaCode.push(this.generateBlockLambda(lambda, frame)); } } @@ -604,7 +598,7 @@ export class SafeDsPythonGenerator { separator: NL, })!; } else if (isSdsExpressionStatement(statement)) { - for (const lambda of streamAllContents(statement.expression).filter(isSdsBlockLambda)) { + for (const lambda of AstUtils.streamAllContents(statement.expression).filter(isSdsBlockLambda)) { blockLambdaCode.push(this.generateBlockLambda(lambda, frame)); } blockLambdaCode.push(this.generateExpression(statement.expression, frame)); @@ -874,7 +868,7 @@ export class SafeDsPythonGenerator { const suffix = isSdsCall(expression.$container) ? '' : '()'; return expandTracedToNode(expression)`${receiver}.${enumMember}${suffix}`; } else if (isSdsAbstractResult(member)) { - const resultList = getAbstractResults(getContainerOfType(member, isSdsCallable)); + const resultList = getAbstractResults(AstUtils.getContainerOfType(member, isSdsCallable)); if (resultList.length === 1) { return traceToNode(expression)(receiver); } @@ -1109,8 +1103,8 @@ export class SafeDsPythonGenerator { } // Root Node is always a module. - const currentModule = findRootNode(expression); - const targetModule = findRootNode(declaration); + const currentModule = AstUtils.findRootNode(expression); + const targetModule = AstUtils.findRootNode(declaration); for (const value of getImports(currentModule)) { // Verify same package if (value.package !== targetModule.name) { @@ -1150,8 +1144,8 @@ export class SafeDsPythonGenerator { declaration: SdsDeclaration, ): ImportData | undefined { // Root Node is always a module. - const currentModule = findRootNode(expression); - const targetModule = findRootNode(declaration); + const currentModule = AstUtils.findRootNode(expression); + const targetModule = AstUtils.findRootNode(declaration); if (currentModule !== targetModule && !isInStubFile(targetModule)) { return { importPath: `${this.getPythonModuleOrDefault(targetModule)}.${this.formatGeneratedFileName( @@ -1164,7 +1158,7 @@ export class SafeDsPythonGenerator { } private getModuleFileBaseName(module: SdsModule): string { - const filePath = getDocument(module).uri.fsPath; + const filePath = AstUtils.getDocument(module).uri.fsPath; return path.basename(filePath, path.extname(filePath)); } diff --git a/packages/safe-ds-lang/src/language/grammar/safe-ds-value-converter.ts b/packages/safe-ds-lang/src/language/grammar/safe-ds-value-converter.ts index a2a5dd6be..6e769d686 100644 --- a/packages/safe-ds-lang/src/language/grammar/safe-ds-value-converter.ts +++ b/packages/safe-ds-lang/src/language/grammar/safe-ds-value-converter.ts @@ -1,4 +1,4 @@ -import { convertBigint, CstNode, DefaultValueConverter, GrammarAST, ValueType } from 'langium'; +import { CstNode, DefaultValueConverter, GrammarAST, ValueConverter, ValueType } from 'langium'; export class SafeDsValueConverter extends DefaultValueConverter { protected override runConverter(rule: GrammarAST.AbstractRule, input: string, cstNode: CstNode): ValueType { @@ -6,7 +6,7 @@ export class SafeDsValueConverter extends DefaultValueConverter { case 'ID': return input.replaceAll('`', ''); case 'INT': - return convertBigint(input); + return ValueConverter.convertBigint(input); case 'STRING': return convertString(input, 1, 1); case 'TEMPLATE_STRING_START': diff --git a/packages/safe-ds-lang/src/language/helpers/astUtils.ts b/packages/safe-ds-lang/src/language/helpers/astUtils.ts index bc7add5da..db01bd5f9 100644 --- a/packages/safe-ds-lang/src/language/helpers/astUtils.ts +++ b/packages/safe-ds-lang/src/language/helpers/astUtils.ts @@ -1,8 +1,8 @@ -import { AstNode, hasContainerOfType } from 'langium'; +import { AstNode, AstUtils } from 'langium'; /** * Returns whether the inner node is contained in the outer node or equal to it. */ export const isContainedInOrEqual = (inner: AstNode | undefined, outer: AstNode | undefined): boolean => { - return hasContainerOfType(inner, (it) => it === outer); + return AstUtils.hasContainerOfType(inner, (it) => it === outer); }; diff --git a/packages/safe-ds-lang/src/language/helpers/fileExtensions.ts b/packages/safe-ds-lang/src/language/helpers/fileExtensions.ts index 0696f72c2..d485bc6fe 100644 --- a/packages/safe-ds-lang/src/language/helpers/fileExtensions.ts +++ b/packages/safe-ds-lang/src/language/helpers/fileExtensions.ts @@ -1,4 +1,4 @@ -import { AstNode, getDocument, LangiumDocument } from 'langium'; +import { AstNode, AstUtils, LangiumDocument } from 'langium'; /** * Marks the file as a pipeline file, which can be executed by our runtime component. @@ -38,17 +38,17 @@ export type SdSFileExtension = typeof PIPELINE_FILE_EXTENSION | typeof STUB_FILE /** * Returns whether the object is contained in a pipeline file. */ -export const isInPipelineFile = (node: AstNode) => isPipelineFile(getDocument(node)); +export const isInPipelineFile = (node: AstNode) => isPipelineFile(AstUtils.getDocument(node)); /** * Returns whether the object is contained in a stub file. */ -export const isInStubFile = (node: AstNode) => isStubFile(getDocument(node)); +export const isInStubFile = (node: AstNode) => isStubFile(AstUtils.getDocument(node)); /** * Returns whether the object is contained in a test file. */ -export const isInTestFile = (node: AstNode) => isTestFile(getDocument(node)); +export const isInTestFile = (node: AstNode) => isTestFile(AstUtils.getDocument(node)); /** * Returns whether the resource represents a pipeline file. diff --git a/packages/safe-ds-lang/src/language/helpers/nodeProperties.ts b/packages/safe-ds-lang/src/language/helpers/nodeProperties.ts index 89da3e815..b7ed4e19a 100644 --- a/packages/safe-ds-lang/src/language/helpers/nodeProperties.ts +++ b/packages/safe-ds-lang/src/language/helpers/nodeProperties.ts @@ -1,4 +1,4 @@ -import { AstNode, getContainerOfType, Stream, stream } from 'langium'; +import { AstNode, AstUtils, Stream, stream } from 'langium'; import { isSdsAnnotation, isSdsArgument, @@ -111,7 +111,7 @@ export namespace Parameter { return false; } - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); // In those cases, the const modifier is not applicable if (isSdsCallableType(containingCallable) || isSdsLambda(containingCallable)) { @@ -208,7 +208,7 @@ export const getAnnotationCalls = (node: SdsAnnotatedObject | undefined): SdsAnn }; export const getAnnotationCallTarget = (node: SdsAnnotationCall | undefined): SdsDeclaration | undefined => { - return getContainerOfType(node, isSdsDeclaration); + return AstUtils.getContainerOfType(node, isSdsDeclaration); }; export const findFirstAnnotationCallOf = ( @@ -283,7 +283,7 @@ export const getModuleMembers = (node: SdsModule | undefined): SdsModuleMember[] }; export const getPackageName = (node: AstNode | undefined): string | undefined => { - return getContainerOfType(node, isSdsModule)?.name; + return AstUtils.getContainerOfType(node, isSdsModule)?.name; }; export const getParameters = (node: SdsCallable | undefined): SdsParameter[] => { @@ -302,7 +302,7 @@ export const getQualifiedName = (node: SdsDeclaration | undefined): string | und if (current.name) { segments.unshift(current.name); } - current = getContainerOfType(current.$container, isSdsDeclaration); + current = AstUtils.getContainerOfType(current.$container, isSdsDeclaration); } return segments.join('.'); diff --git a/packages/safe-ds-lang/src/language/helpers/safe-ds-node-mapper.ts b/packages/safe-ds-lang/src/language/helpers/safe-ds-node-mapper.ts index 5e10d48da..97a1184ba 100644 --- a/packages/safe-ds-lang/src/language/helpers/safe-ds-node-mapper.ts +++ b/packages/safe-ds-lang/src/language/helpers/safe-ds-node-mapper.ts @@ -1,4 +1,4 @@ -import { EMPTY_STREAM, findLocalReferences, getContainerOfType, Stream } from 'langium'; +import { AstUtils, EMPTY_STREAM, Stream } from 'langium'; import { isSdsAbstractCall, isSdsAnnotationCall, @@ -63,7 +63,7 @@ export class SafeDsNodeMapper { } // Positional argument - const containingAbstractCall = getContainerOfType(node, isSdsAbstractCall)!; + const containingAbstractCall = AstUtils.getContainerOfType(node, isSdsAbstractCall)!; const args = getArguments(containingAbstractCall); const argumentPosition = node.$containerIndex ?? -1; @@ -93,7 +93,7 @@ export class SafeDsNodeMapper { return undefined; } - const containingAssignment = getContainerOfType(node, isSdsAssignment); + const containingAssignment = AstUtils.getContainerOfType(node, isSdsAssignment); /* c8 ignore start */ if (!containingAssignment) { return undefined; @@ -249,14 +249,14 @@ export class SafeDsNodeMapper { return EMPTY_STREAM; } - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); /* c8 ignore start */ if (!containingCallable) { return EMPTY_STREAM; } /* c8 ignore stop */ - return findLocalReferences(node, containingCallable) + return AstUtils.findLocalReferences(node, containingCallable) .map((it) => it.$refNode?.astNode) .filter(isSdsReference); } @@ -269,14 +269,14 @@ export class SafeDsNodeMapper { return EMPTY_STREAM; } - const containingBlock = getContainerOfType(node, isSdsBlock); + const containingBlock = AstUtils.getContainerOfType(node, isSdsBlock); /* c8 ignore start */ if (!containingBlock) { return EMPTY_STREAM; } /* c8 ignore stop */ - return findLocalReferences(node, containingBlock) + return AstUtils.findLocalReferences(node, containingBlock) .map((it) => it.$refNode?.astNode) .filter(isSdsReference); } @@ -289,12 +289,12 @@ export class SafeDsNodeMapper { return EMPTY_STREAM; } - const containingSegment = getContainerOfType(node, isSdsSegment); + const containingSegment = AstUtils.getContainerOfType(node, isSdsSegment); if (!containingSegment) { return EMPTY_STREAM; } - return findLocalReferences(node, containingSegment) + return AstUtils.findLocalReferences(node, containingSegment) .map((it) => it.$refNode?.astNode) .filter(isSdsYield); } @@ -314,7 +314,7 @@ export class SafeDsNodeMapper { } // Positional type argument - const containingType = getContainerOfType(node, isSdsType); + const containingType = AstUtils.getContainerOfType(node, isSdsType); if (!isSdsNamedType(containingType)) { return undefined; } diff --git a/packages/safe-ds-lang/src/language/index.ts b/packages/safe-ds-lang/src/language/index.ts index e895dd406..b6f7c4ede 100644 --- a/packages/safe-ds-lang/src/language/index.ts +++ b/packages/safe-ds-lang/src/language/index.ts @@ -15,4 +15,5 @@ export * from './helpers/nodeProperties.js'; // Location export { locationToString, positionToString, rangeToString } from '../helpers/locations.js'; +// Messages export * as messages from './runner/messages.js'; diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-call-hierarchy-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-call-hierarchy-provider.ts index 6a3e491ce..9d1007992 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-call-hierarchy-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-call-hierarchy-provider.ts @@ -1,14 +1,4 @@ -import { - AbstractCallHierarchyProvider, - type AstNode, - type CstNode, - findLeafNodeAtOffset, - getContainerOfType, - getDocument, - type NodeKindProvider, - type ReferenceDescription, - type Stream, -} from 'langium'; +import { type AstNode, AstUtils, type CstNode, CstUtils, type ReferenceDescription, type Stream } from 'langium'; import type { CallHierarchyIncomingCall, CallHierarchyOutgoingCall, @@ -27,6 +17,7 @@ import { import type { SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js'; import type { SafeDsServices } from '../safe-ds-module.js'; import type { SafeDsNodeInfoProvider } from './safe-ds-node-info-provider.js'; +import { AbstractCallHierarchyProvider, NodeKindProvider } from 'langium/lsp'; export class SafeDsCallHierarchyProvider extends AbstractCallHierarchyProvider { private readonly callGraphComputer: SafeDsCallGraphComputer; @@ -79,7 +70,7 @@ export class SafeDsCallHierarchyProvider extends AbstractCallHierarchyProvider { return; } - const callerDocumentUri = getDocument(caller).uri.toString(); + const callerDocumentUri = AstUtils.getDocument(caller).uri.toString(); result.push({ from: { @@ -107,23 +98,28 @@ export class SafeDsCallHierarchyProvider extends AbstractCallHierarchyProvider { private getUniquePotentialCallers(references: Stream): Stream { return references .map((it) => { - const document = this.documents.getOrCreateDocument(it.sourceUri); + const document = this.documents.getDocument(it.sourceUri); + if (!document) { + /* c8 ignore next 2 */ + return undefined; + } + const rootNode = document.parseResult.value; if (!rootNode.$cstNode) { /* c8 ignore next 2 */ return undefined; } - const targetCstNode = findLeafNodeAtOffset(rootNode.$cstNode, it.segment.offset); + const targetCstNode = CstUtils.findLeafNodeAtOffset(rootNode.$cstNode, it.segment.offset); if (!targetCstNode) { /* c8 ignore next 2 */ return undefined; } - const containingDeclaration = getContainerOfType(targetCstNode.astNode, isSdsDeclaration); + const containingDeclaration = AstUtils.getContainerOfType(targetCstNode.astNode, isSdsDeclaration); if (isSdsParameter(containingDeclaration)) { // For parameters, we return their containing callable instead - return getContainerOfType(containingDeclaration.$container, isSdsDeclaration); + return AstUtils.getContainerOfType(containingDeclaration.$container, isSdsDeclaration); } else { return containingDeclaration; } @@ -165,7 +161,7 @@ export class SafeDsCallHierarchyProvider extends AbstractCallHierarchyProvider { return; } - const callableDocumentUri = getDocument(callable).uri.toString(); + const callableDocumentUri = AstUtils.getDocument(callable).uri.toString(); const callableId = callableDocumentUri + '~' + callableNameCstNode.text; const previousFromRanges = callsGroupedByCallable.get(callableId)?.fromRanges ?? []; diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-document-symbol-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-document-symbol-provider.ts index b0dce7b7c..021591ee1 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-document-symbol-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-document-symbol-provider.ts @@ -1,4 +1,4 @@ -import { type AstNode, DefaultDocumentSymbolProvider, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import type { DocumentSymbol } from 'vscode-languageserver'; import { isSdsAnnotation, @@ -11,6 +11,7 @@ import { } from '../generated/ast.js'; import type { SafeDsServices } from '../safe-ds-module.js'; import type { SafeDsNodeInfoProvider } from './safe-ds-node-info-provider.js'; +import { DefaultDocumentSymbolProvider } from 'langium/lsp'; export class SafeDsDocumentSymbolProvider extends DefaultDocumentSymbolProvider { private readonly nodeInfoProvider: SafeDsNodeInfoProvider; diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-formatter.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-formatter.ts index 3e184ebf5..fc94c98c0 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-formatter.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-formatter.ts @@ -1,16 +1,8 @@ -import { - AbstractFormatter, - AstNode, - CstNode, - findCommentNode, - Formatting, - FormattingAction, - FormattingActionOptions, - isAstNode, -} from 'langium'; +import { AstNode, CstNode, CstUtils, isAstNode } from 'langium'; import { last } from '../../helpers/collections.js'; import * as ast from '../generated/ast.js'; import { getAnnotationCalls, getLiterals, getTypeArguments } from '../helpers/nodeProperties.js'; +import { AbstractFormatter, Formatting, FormattingAction, FormattingActionOptions } from 'langium/lsp'; import indent = Formatting.indent; import newLine = Formatting.newLine; import newLines = Formatting.newLines; @@ -1021,9 +1013,9 @@ export class SafeDsFormatter extends AbstractFormatter { const commentNames = ['ML_COMMENT', 'SL_COMMENT']; if (isAstNode(node)) { - return findCommentNode(node.$cstNode, commentNames); + return CstUtils.findCommentNode(node.$cstNode, commentNames); } else { - return findCommentNode(node, commentNames); + return CstUtils.findCommentNode(node, commentNames); } } } diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-inlay-hint-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-inlay-hint-provider.ts index 87bf22bfb..68ab4a7d8 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-inlay-hint-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-inlay-hint-provider.ts @@ -1,4 +1,4 @@ -import { AbstractInlayHintProvider, AstNode, DocumentationProvider, InlayHintAcceptor } from 'langium'; +import { AstNode, DocumentationProvider } from 'langium'; import { InlayHintKind, MarkupContent } from 'vscode-languageserver'; import { createMarkupContent } from '../documentation/safe-ds-comment-provider.js'; import { isSdsArgument, isSdsBlockLambdaResult, isSdsPlaceholder, isSdsYield } from '../generated/ast.js'; @@ -7,6 +7,7 @@ import { SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js'; import { SafeDsServices } from '../safe-ds-module.js'; import { NamedType } from '../typing/model.js'; import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js'; +import { AbstractInlayHintProvider, InlayHintAcceptor } from 'langium/lsp'; export class SafeDsInlayHintProvider extends AbstractInlayHintProvider { private readonly documentationProvider: DocumentationProvider; diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-language-server.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-language-server.ts deleted file mode 100644 index 2ac8bc6a6..000000000 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-language-server.ts +++ /dev/null @@ -1,193 +0,0 @@ -/* - * This file can be removed, once Langium supports the TypeHierarchyProvider directly. - */ - -/* c8 ignore start */ -import type { LangiumServices, LangiumSharedServices } from 'langium'; -import { - addCallHierarchyHandler, - addCodeActionHandler, - addCodeLensHandler, - addCompletionHandler, - addConfigurationChangeHandler, - addDiagnosticsHandler, - addDocumentHighlightsHandler, - addDocumentLinkHandler, - addDocumentsHandler, - addDocumentSymbolHandler, - addExecuteCommandHandler, - addFindReferencesHandler, - addFoldingRangeHandler, - addFormattingHandler, - addGoToDeclarationHandler, - addGotoDefinitionHandler, - addGoToImplementationHandler, - addGoToTypeDefinitionHandler, - addHoverHandler, - addInlayHintHandler, - addRenameHandler, - addSemanticTokenHandler, - addSignatureHelpHandler, - addWorkspaceSymbolHandler, - createServerRequestHandler, - DefaultLanguageServer, - isOperationCancelled, - URI, -} from 'langium'; -import { - type CancellationToken, - type Connection, - type HandlerResult, - type InitializeParams, - type InitializeResult, - LSPErrorCodes, - ResponseError, - type ServerRequestHandler, - type TypeHierarchySubtypesParams, - type TypeHierarchySupertypesParams, -} from 'vscode-languageserver'; -import { TypeHierarchyProvider } from './safe-ds-type-hierarchy-provider.js'; - -interface LangiumAddedServices { - lsp: { - TypeHierarchyProvider?: TypeHierarchyProvider; - }; -} - -export class SafeDsLanguageServer extends DefaultLanguageServer { - protected override hasService( - callback: (language: LangiumServices & LangiumAddedServices) => object | undefined, - ): boolean { - return this.services.ServiceRegistry.all.some((language) => callback(language) !== undefined); - } - - protected override buildInitializeResult(params: InitializeParams): InitializeResult { - const hasTypeHierarchyProvider = this.hasService((e) => e.lsp.TypeHierarchyProvider); - const otherCapabilities = super.buildInitializeResult(params).capabilities; - - return { - capabilities: { - ...otherCapabilities, - typeHierarchyProvider: hasTypeHierarchyProvider ? {} : undefined, - }, - }; - } -} - -export const startLanguageServer = (services: LangiumSharedServices): void => { - const connection = services.lsp.Connection; - if (!connection) { - throw new Error('Starting a language server requires the languageServer.Connection service to be set.'); - } - - addDocumentsHandler(connection, services); - addDiagnosticsHandler(connection, services); - addCompletionHandler(connection, services); - addFindReferencesHandler(connection, services); - addDocumentSymbolHandler(connection, services); - addGotoDefinitionHandler(connection, services); - addGoToTypeDefinitionHandler(connection, services); - addGoToImplementationHandler(connection, services); - addDocumentHighlightsHandler(connection, services); - addFoldingRangeHandler(connection, services); - addFormattingHandler(connection, services); - addCodeActionHandler(connection, services); - addRenameHandler(connection, services); - addHoverHandler(connection, services); - addInlayHintHandler(connection, services); - addSemanticTokenHandler(connection, services); - addExecuteCommandHandler(connection, services); - addSignatureHelpHandler(connection, services); - addCallHierarchyHandler(connection, services); - addTypeHierarchyHandler(connection, services); - addCodeLensHandler(connection, services); - addDocumentLinkHandler(connection, services); - addConfigurationChangeHandler(connection, services); - addGoToDeclarationHandler(connection, services); - addWorkspaceSymbolHandler(connection, services); - - connection.onInitialize((params) => { - return services.lsp.LanguageServer.initialize(params); - }); - connection.onInitialized((params) => { - return services.lsp.LanguageServer.initialized(params); - }); - - // Make the text document manager listen on the connection for open, change and close text document events. - const documents = services.workspace.TextDocuments; - documents.listen(connection); - - // Start listening for incoming messages from the client. - connection.listen(); -}; - -export const addTypeHierarchyHandler = function (connection: Connection, sharedServices: LangiumSharedServices): void { - connection.languages.typeHierarchy.onPrepare( - createServerRequestHandler((services, document, params, cancelToken) => { - const typeHierarchyProvider = (services).lsp.TypeHierarchyProvider; - if (typeHierarchyProvider) { - return typeHierarchyProvider.prepareTypeHierarchy(document, params, cancelToken) ?? null; - } - return null; - }, sharedServices), - ); - - connection.languages.typeHierarchy.onSupertypes( - createTypeHierarchyRequestHandler((services, params, cancelToken) => { - const typeHierarchyProvider = (services).lsp.TypeHierarchyProvider; - if (typeHierarchyProvider) { - return typeHierarchyProvider.supertypes(params, cancelToken) ?? null; - } - return null; - }, sharedServices), - ); - - connection.languages.typeHierarchy.onSubtypes( - createTypeHierarchyRequestHandler((services, params, cancelToken) => { - const typeHierarchyProvider = (services).lsp.TypeHierarchyProvider; - if (typeHierarchyProvider) { - return typeHierarchyProvider.subtypes(params, cancelToken) ?? null; - } - return null; - }, sharedServices), - ); -}; - -export const createTypeHierarchyRequestHandler = function < - P extends TypeHierarchySupertypesParams | TypeHierarchySubtypesParams, - R, - PR, - E = void, ->( - serviceCall: (services: LangiumServices, params: P, cancelToken: CancellationToken) => HandlerResult, - sharedServices: LangiumSharedServices, -): ServerRequestHandler { - const serviceRegistry = sharedServices.ServiceRegistry; - return async (params: P, cancelToken: CancellationToken) => { - const uri = URI.parse(params.item.uri); - const language = serviceRegistry.getServices(uri); - if (!language) { - const message = `Could not find service instance for uri: '${uri.toString()}'`; - // eslint-disable-next-line no-console - console.error(message); - throw new Error(message); - } - try { - // eslint-disable-next-line @typescript-eslint/return-await - return await serviceCall(language, params, cancelToken); - } catch (err) { - return responseError(err); - } - }; -}; - -const responseError = function (err: unknown): ResponseError { - if (isOperationCancelled(err)) { - return new ResponseError(LSPErrorCodes.RequestCancelled, 'The request has been cancelled.'); - } - if (err instanceof ResponseError) { - return err; - } - throw err; -}; -/* c8 ignore stop */ diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-node-kind-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-node-kind-provider.ts index 4de7e87bb..70e7a02e2 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-node-kind-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-node-kind-provider.ts @@ -1,4 +1,4 @@ -import { AstNode, AstNodeDescription, hasContainerOfType, isAstNode, NodeKindProvider } from 'langium'; +import { AstNode, AstNodeDescription, AstUtils, isAstNode } from 'langium'; import { CompletionItemKind, SymbolKind } from 'vscode-languageserver'; import { isSdsClass, @@ -19,12 +19,13 @@ import { SdsSegment, SdsTypeParameter, } from '../generated/ast.js'; +import { NodeKindProvider } from 'langium/lsp'; export class SafeDsNodeKindProvider implements NodeKindProvider { getSymbolKind(nodeOrDescription: AstNode | AstNodeDescription): SymbolKind { // The WorkspaceSymbolProvider only passes descriptions, where the node might be undefined const node = this.getNode(nodeOrDescription); - if (isSdsFunction(node) && hasContainerOfType(node, isSdsClass)) { + if (isSdsFunction(node) && AstUtils.hasContainerOfType(node, isSdsClass)) { return SymbolKind.Method; } diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-rename-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-rename-provider.ts index efb7a8251..27447afef 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-rename-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-rename-provider.ts @@ -1,9 +1,8 @@ import { AstNode, AstNodeLocator, - DefaultRenameProvider, - findDeclarationNodeAtOffset, - getContainerOfType, + AstUtils, + CstUtils, LangiumDocument, LangiumDocuments, ReferenceDescription, @@ -14,6 +13,7 @@ import { Position, RenameParams, TextEdit, WorkspaceEdit } from 'vscode-language import { SafeDsServices } from '../safe-ds-module.js'; import { isSdsImportedDeclaration, isSdsModule } from '../generated/ast.js'; import { getImportedDeclarations } from '../helpers/nodeProperties.js'; +import { DefaultRenameProvider } from 'langium/lsp'; export class SafeDsRenameProvider extends DefaultRenameProvider { private readonly astNodeLocator: AstNodeLocator; @@ -42,7 +42,7 @@ export class SafeDsRenameProvider extends DefaultRenameProvider { } const offset = document.textDocument.offsetAt(position); - const leafNode = findDeclarationNodeAtOffset(rootNode, offset, this.grammarConfig.nameRegexp); + const leafNode = CstUtils.findDeclarationNodeAtOffset(rootNode, offset, this.grammarConfig.nameRegexp); if (!leafNode) { /* c8 ignore next 2 */ return undefined; @@ -71,7 +71,7 @@ export class SafeDsRenameProvider extends DefaultRenameProvider { } // Other references must be renamed unless they are imported under an alias - const sourceModule = getContainerOfType(sourceNode, isSdsModule); + const sourceModule = AstUtils.getContainerOfType(sourceNode, isSdsModule); if (!sourceModule) { /* c8 ignore next 2 */ return false; @@ -90,22 +90,22 @@ export class SafeDsRenameProvider extends DefaultRenameProvider { } private getAstNode(uri: URI, path: string): AstNode | undefined { - if (!this.langiumDocuments.hasDocument(uri)) { + const document = this.langiumDocuments.getDocument(uri); + if (!document) { /* c8 ignore next 2 */ return undefined; } - const document = this.langiumDocuments.getOrCreateDocument(uri); return this.astNodeLocator.getAstNode(document.parseResult.value, path); } private getReferenceText(ref: ReferenceDescription): string | undefined { - if (!this.langiumDocuments.hasDocument(ref.sourceUri)) { + const document = this.langiumDocuments.getDocument(ref.sourceUri); + if (!document) { /* c8 ignore next 2 */ return undefined; } - const document = this.langiumDocuments.getOrCreateDocument(ref.sourceUri); return document.textDocument.getText(ref.segment.range); } diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-semantic-token-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-semantic-token-provider.ts index cd73b9cd3..a35a6bd1f 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-semantic-token-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-semantic-token-provider.ts @@ -1,4 +1,5 @@ -import { AbstractSemanticTokenProvider, AstNode, hasContainerOfType, SemanticTokenAcceptor } from 'langium'; +import { AbstractSemanticTokenProvider, SemanticTokenAcceptor } from 'langium/lsp'; +import { AstNode, AstUtils } from 'langium'; import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver'; import { SafeDsClasses } from '../builtins/safe-ds-classes.js'; import { @@ -168,7 +169,7 @@ export class SafeDsSemanticTokenProvider extends AbstractSemanticTokenProvider { modifier: additionalModifiers, }; } else if (isSdsFunction(node)) { - if (hasContainerOfType(node, isSdsClass)) { + if (AstUtils.hasContainerOfType(node, isSdsClass)) { return { type: SemanticTokenTypes.method, modifier: node.isStatic diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-signature-help-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-signature-help-provider.ts index 6d4cc4ecd..cc0505c51 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-signature-help-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-signature-help-provider.ts @@ -1,13 +1,12 @@ import { type AstNode, + AstUtils, + CstUtils, type DocumentationProvider, - findLeafNodeAtOffset, - findNodesForKeyword, - getContainerOfType, + GrammarUtils, isNamed, type LangiumDocument, type MaybePromise, - type SignatureHelpProvider, } from 'langium'; import type { CancellationToken, @@ -22,6 +21,7 @@ import { type SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js'; import type { SafeDsServices } from '../safe-ds-module.js'; import { type SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js'; import { CallableType, NamedType } from '../typing/model.js'; +import { SignatureHelpProvider } from 'langium/lsp'; export class SafeDsSignatureHelpProvider implements SignatureHelpProvider { private readonly documentationProvider: DocumentationProvider; @@ -47,7 +47,7 @@ export class SafeDsSignatureHelpProvider implements SignatureHelpProvider { /* c8 ignore stop */ const offset = document.textDocument.offsetAt(params.position); - const sourceCstNode = findLeafNodeAtOffset(rootCstNode, offset); + const sourceCstNode = CstUtils.findLeafNodeAtOffset(rootCstNode, offset); /* c8 ignore start */ if (!sourceCstNode) { return undefined; @@ -61,7 +61,7 @@ export class SafeDsSignatureHelpProvider implements SignatureHelpProvider { * Returns the signature help for the given node at the given offset. */ private getSignature(node: AstNode, offset: number): MaybePromise { - const containingAbstractCall = getContainerOfType(node, isSdsAbstractCall); + const containingAbstractCall = AstUtils.getContainerOfType(node, isSdsAbstractCall); if (!containingAbstractCall) { return undefined; } @@ -71,7 +71,7 @@ export class SafeDsSignatureHelpProvider implements SignatureHelpProvider { return undefined; } - const commas = findNodesForKeyword(containingAbstractCall.argumentList.$cstNode, ','); + const commas = GrammarUtils.findNodesForKeyword(containingAbstractCall.argumentList.$cstNode, ','); const activeParameter = commas.findLastIndex((comma) => comma.offset < offset) + 1; return { diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-type-hierarchy-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-type-hierarchy-provider.ts index d102b68ac..3b8bf8f33 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-type-hierarchy-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-type-hierarchy-provider.ts @@ -1,29 +1,5 @@ -import { - type AstNode, - findDeclarationNodeAtOffset, - findLeafNodeAtOffset, - getContainerOfType, - getDocument, - type GrammarConfig, - type LangiumDocument, - type LangiumDocuments, - type LangiumServices, - type NameProvider, - NodeKindProvider, - type ReferenceDescription, - type References, - stream, - type Stream, - URI, -} from 'langium'; -import { - type CancellationToken, - SymbolKind, - type TypeHierarchyItem, - type TypeHierarchyPrepareParams, - type TypeHierarchySubtypesParams, - type TypeHierarchySupertypesParams, -} from 'vscode-languageserver'; +import { type AstNode, AstUtils, CstUtils, type ReferenceDescription, stream, type Stream } from 'langium'; +import { type TypeHierarchyItem } from 'vscode-languageserver'; import { isSdsClass, isSdsEnum, @@ -37,128 +13,7 @@ import type { SafeDsServices } from '../safe-ds-module.js'; import { SafeDsClassHierarchy } from '../typing/safe-ds-class-hierarchy.js'; import { SafeDsNodeInfoProvider } from './safe-ds-node-info-provider.js'; import { getEnumVariants } from '../helpers/nodeProperties.js'; - -/* c8 ignore start */ -export interface TypeHierarchyProvider { - prepareTypeHierarchy( - document: LangiumDocument, - params: TypeHierarchyPrepareParams, - cancelToken?: CancellationToken, - ): TypeHierarchyItem[] | undefined; - - supertypes(params: TypeHierarchySupertypesParams, cancelToken?: CancellationToken): TypeHierarchyItem[] | undefined; - - subtypes(params: TypeHierarchySubtypesParams, cancelToken?: CancellationToken): TypeHierarchyItem[] | undefined; -} - -export abstract class AbstractTypeHierarchyProvider implements TypeHierarchyProvider { - protected readonly grammarConfig: GrammarConfig; - protected readonly nameProvider: NameProvider; - protected readonly documents: LangiumDocuments; - protected readonly references: References; - - protected constructor(services: LangiumServices) { - this.grammarConfig = services.parser.GrammarConfig; - this.nameProvider = services.references.NameProvider; - this.documents = services.shared.workspace.LangiumDocuments; - this.references = services.references.References; - } - - prepareTypeHierarchy( - document: LangiumDocument, - params: TypeHierarchyPrepareParams, - _cancelToken?: CancellationToken, - ): TypeHierarchyItem[] | undefined { - const rootNode = document.parseResult.value; - const targetNode = findDeclarationNodeAtOffset( - rootNode.$cstNode, - document.textDocument.offsetAt(params.position), - this.grammarConfig.nameRegexp, - ); - if (!targetNode) { - return undefined; - } - - const declarationNode = this.references.findDeclarationNode(targetNode); - if (!declarationNode) { - return undefined; - } - - return this.getTypeHierarchyItems(declarationNode.astNode, document); - } - - protected getTypeHierarchyItems(targetNode: AstNode, document: LangiumDocument): TypeHierarchyItem[] | undefined { - const nameNode = this.nameProvider.getNameNode(targetNode); - const name = this.nameProvider.getName(targetNode); - if (!nameNode || !targetNode.$cstNode || name === undefined) { - return undefined; - } - - return [ - { - kind: SymbolKind.Class, - name, - range: targetNode.$cstNode.range, - selectionRange: nameNode.range, - uri: document.uri.toString(), - ...this.getTypeHierarchyItem(targetNode), - }, - ]; - } - - protected getTypeHierarchyItem(_targetNode: AstNode): Partial | undefined { - return undefined; - } - - supertypes( - params: TypeHierarchySupertypesParams, - _cancelToken?: CancellationToken, - ): TypeHierarchyItem[] | undefined { - const document = this.documents.getOrCreateDocument(URI.parse(params.item.uri)); - const rootNode = document.parseResult.value; - const targetNode = findDeclarationNodeAtOffset( - rootNode.$cstNode, - document.textDocument.offsetAt(params.item.range.start), - this.grammarConfig.nameRegexp, - ); - if (!targetNode) { - return undefined; - } - return this.getSupertypes(targetNode.astNode); - } - - /** - * Override this method to collect the supertypes for your language. - */ - protected abstract getSupertypes(node: AstNode): TypeHierarchyItem[] | undefined; - - subtypes(params: TypeHierarchySubtypesParams, _cancelToken?: CancellationToken): TypeHierarchyItem[] | undefined { - const document = this.documents.getOrCreateDocument(URI.parse(params.item.uri)); - const rootNode = document.parseResult.value; - const targetNode = findDeclarationNodeAtOffset( - rootNode.$cstNode, - document.textDocument.offsetAt(params.item.range.start), - this.grammarConfig.nameRegexp, - ); - if (!targetNode) { - return undefined; - } - - const references = this.references.findReferences(targetNode.astNode, { - includeDeclaration: false, - }); - return this.getSubtypes(targetNode.astNode, references); - } - - /** - * Override this method to collect the subtypes for your language. - */ - protected abstract getSubtypes( - node: AstNode, - references: Stream, - ): TypeHierarchyItem[] | undefined; -} -/* c8 ignore stop */ +import { AbstractTypeHierarchyProvider, type NodeKindProvider } from 'langium/lsp'; export class SafeDsTypeHierarchyProvider extends AbstractTypeHierarchyProvider { private readonly classHierarchy: SafeDsClassHierarchy; @@ -199,23 +54,24 @@ export class SafeDsTypeHierarchyProvider extends AbstractTypeHierarchyProvider { return undefined; } - return this.getTypeHierarchyItems(parentClass, getDocument(parentClass)); + return this.getTypeHierarchyItems(parentClass, AstUtils.getDocument(parentClass)); } private getSupertypesOfEnumVariant(node: SdsEnumVariant): TypeHierarchyItem[] | undefined { - const containingEnum = getContainerOfType(node, isSdsEnum); + const containingEnum = AstUtils.getContainerOfType(node, isSdsEnum); if (!containingEnum) { /* c8 ignore next 2 */ return undefined; } - return this.getTypeHierarchyItems(containingEnum, getDocument(containingEnum)); + return this.getTypeHierarchyItems(containingEnum, AstUtils.getDocument(containingEnum)); } - protected override getSubtypes( - node: AstNode, - references: Stream, - ): TypeHierarchyItem[] | undefined { + protected override getSubtypes(node: AstNode): TypeHierarchyItem[] | undefined { + const references = this.references.findReferences(node, { + includeDeclaration: false, + }); + let items: TypeHierarchyItem[]; if (isSdsClass(node)) { @@ -232,14 +88,19 @@ export class SafeDsTypeHierarchyProvider extends AbstractTypeHierarchyProvider { private getSubtypesOfClass(references: Stream): TypeHierarchyItem[] { return references .flatMap((it) => { - const document = this.documents.getOrCreateDocument(it.sourceUri); + const document = this.documents.getDocument(it.sourceUri); + if (!document) { + /* c8 ignore next 2 */ + return []; + } + const rootNode = document.parseResult.value; if (!rootNode.$cstNode) { /* c8 ignore next 2 */ return []; } - const targetCstNode = findLeafNodeAtOffset(rootNode.$cstNode, it.segment.offset); + const targetCstNode = CstUtils.findLeafNodeAtOffset(rootNode.$cstNode, it.segment.offset); if (!targetCstNode) { /* c8 ignore next 2 */ return []; @@ -251,7 +112,7 @@ export class SafeDsTypeHierarchyProvider extends AbstractTypeHierarchyProvider { return []; } - const containingClass = getContainerOfType(targetNode, isSdsClass); + const containingClass = AstUtils.getContainerOfType(targetNode, isSdsClass); if (!containingClass) { /* c8 ignore next 2 */ return []; @@ -264,7 +125,7 @@ export class SafeDsTypeHierarchyProvider extends AbstractTypeHierarchyProvider { private getSubtypesOfEnum(node: SdsEnum): TypeHierarchyItem[] { const variants = getEnumVariants(node); - const document = getDocument(node); + const document = AstUtils.getDocument(node); return stream(variants) .flatMap((it) => this.getTypeHierarchyItems(it, document) ?? []) diff --git a/packages/safe-ds-lang/src/language/main.ts b/packages/safe-ds-lang/src/language/main.ts index c170d1c6a..e6a044fe3 100644 --- a/packages/safe-ds-lang/src/language/main.ts +++ b/packages/safe-ds-lang/src/language/main.ts @@ -1,6 +1,6 @@ import { NodeFileSystem } from 'langium/node'; +import { startLanguageServer as doStartLanguageServer } from 'langium/lsp'; import { createConnection, ProposedFeatures } from 'vscode-languageserver/node.js'; -import { startLanguageServer as doStartLanguageServer } from './lsp/safe-ds-language-server.js'; import { createSafeDsServices } from './safe-ds-module.js'; /* c8 ignore start */ diff --git a/packages/safe-ds-lang/src/language/partialEvaluation/safe-ds-partial-evaluator.ts b/packages/safe-ds-lang/src/language/partialEvaluation/safe-ds-partial-evaluator.ts index 9b5040cd5..f40269b31 100644 --- a/packages/safe-ds-lang/src/language/partialEvaluation/safe-ds-partial-evaluator.ts +++ b/packages/safe-ds-lang/src/language/partialEvaluation/safe-ds-partial-evaluator.ts @@ -1,4 +1,4 @@ -import { AstNode, AstNodeLocator, getContainerOfType, getDocument, WorkspaceCache } from 'langium'; +import { AstNode, AstNodeLocator, AstUtils, WorkspaceCache } from 'langium'; import { isEmpty } from '../../helpers/collections.js'; import { isSdsArgument, @@ -122,7 +122,7 @@ export class SafeDsPartialEvaluator { } private getNodeId(node: AstNode) { - const documentUri = getDocument(node).uri.toString(); + const documentUri = AstUtils.getDocument(node).uri.toString(); const nodePath = this.astNodeLocator.getAstNodePath(node); return `${documentUri}~${nodePath}`; } @@ -148,7 +148,7 @@ export class SafeDsPartialEvaluator { substitutions: ParameterSubstitutions, visited: VisitedState[], ): EvaluatedNode { - const containingAssignment = getContainerOfType(node, isSdsAssignment); + const containingAssignment = AstUtils.getContainerOfType(node, isSdsAssignment); if (!containingAssignment) { /* c8 ignore next 2 */ return UnknownEvaluatedNode; diff --git a/packages/safe-ds-lang/src/language/purity/safe-ds-purity-computer.ts b/packages/safe-ds-lang/src/language/purity/safe-ds-purity-computer.ts index 47d1e5b5d..3fb6b168f 100644 --- a/packages/safe-ds-lang/src/language/purity/safe-ds-purity-computer.ts +++ b/packages/safe-ds-lang/src/language/purity/safe-ds-purity-computer.ts @@ -1,12 +1,4 @@ -import { - type AstNode, - type AstNodeLocator, - EMPTY_STREAM, - getContainerOfType, - getDocument, - Stream, - WorkspaceCache, -} from 'langium'; +import { type AstNode, type AstNodeLocator, AstUtils, EMPTY_STREAM, Stream, WorkspaceCache } from 'langium'; import { isEmpty } from '../../helpers/collections.js'; import type { SafeDsCallGraphComputer } from '../flow/safe-ds-call-graph-computer.js'; import type { SafeDsServices } from '../safe-ds-module.js'; @@ -99,7 +91,7 @@ export class SafeDsPurityComputer { * The parameter to check. */ isPureParameter(node: SdsParameter | undefined): boolean { - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); if ( !containingCallable || isSdsAnnotation(containingCallable) || @@ -258,7 +250,7 @@ export class SafeDsPurityComputer { isSdsParameter(it) && // Leads to endless recursion if we don't check this // (see test case "should return the impurity reasons of a parameter call in a function") - !isSdsFunction(getContainerOfType(it, isSdsCallable)) && + !isSdsFunction(AstUtils.getContainerOfType(it, isSdsCallable)) && !this.isPureParameter(it) ) { return [new PotentiallyImpureParameterCall(it)]; @@ -273,7 +265,7 @@ export class SafeDsPurityComputer { private getExecutedCallsInExpression(expression: SdsExpression | undefined): SdsCall[] { return this.callGraphComputer.getAllContainedCalls(expression).filter((it) => { // Keep only calls that are not contained in a lambda inside the expression - const containingLambda = getContainerOfType(it, isSdsLambda); + const containingLambda = AstUtils.getContainerOfType(it, isSdsLambda); return !containingLambda || !isContainedInOrEqual(containingLambda, expression); }); } @@ -319,7 +311,7 @@ export class SafeDsPurityComputer { } private getNodeId(node: AstNode) { - const documentUri = getDocument(node).uri.toString(); + const documentUri = AstUtils.getDocument(node).uri.toString(); const nodePath = this.astNodeLocator.getAstNodePath(node); return `${documentUri}~${nodePath}`; } diff --git a/packages/safe-ds-lang/src/language/safe-ds-module.ts b/packages/safe-ds-lang/src/language/safe-ds-module.ts index 40288f5cd..4e12a62e7 100644 --- a/packages/safe-ds-lang/src/language/safe-ds-module.ts +++ b/packages/safe-ds-lang/src/language/safe-ds-module.ts @@ -1,14 +1,12 @@ +import { DeepPartial, inject, Module } from 'langium'; import { createDefaultModule, createDefaultSharedModule, - DeepPartial, DefaultSharedModuleContext, - inject, LangiumServices, LangiumSharedServices, - Module, PartialLangiumServices, -} from 'langium'; +} from 'langium/lsp'; import { SafeDsAnnotations } from './builtins/safe-ds-annotations.js'; import { SafeDsClasses } from './builtins/safe-ds-classes.js'; import { SafeDsEnums, SafeDsImpurityReasons } from './builtins/safe-ds-enums.js'; @@ -23,7 +21,6 @@ import { SafeDsCallHierarchyProvider } from './lsp/safe-ds-call-hierarchy-provid import { SafeDsDocumentSymbolProvider } from './lsp/safe-ds-document-symbol-provider.js'; import { SafeDsFormatter } from './lsp/safe-ds-formatter.js'; import { SafeDsInlayHintProvider } from './lsp/safe-ds-inlay-hint-provider.js'; -import { SafeDsLanguageServer } from './lsp/safe-ds-language-server.js'; import { SafeDsNodeInfoProvider } from './lsp/safe-ds-node-info-provider.js'; import { SafeDsNodeKindProvider } from './lsp/safe-ds-node-kind-provider.js'; import { SafeDsSemanticTokenProvider } from './lsp/safe-ds-semantic-token-provider.js'; @@ -70,7 +67,6 @@ export type SafeDsAddedServices = { }; lsp: { NodeInfoProvider: SafeDsNodeInfoProvider; - TypeHierarchyProvider: SafeDsTypeHierarchyProvider; }; purity: { PurityComputer: SafeDsPurityComputer; @@ -166,7 +162,6 @@ export type SafeDsSharedServices = LangiumSharedServices; export const SafeDsSharedModule: Module> = { lsp: { - LanguageServer: (services) => new SafeDsLanguageServer(services), NodeKindProvider: () => new SafeDsNodeKindProvider(), }, workspace: { diff --git a/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-computation.ts b/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-computation.ts index 68358ab96..c461c22fd 100644 --- a/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-computation.ts +++ b/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-computation.ts @@ -1,8 +1,8 @@ import { AstNode, AstNodeDescription, + AstUtils, DefaultScopeComputation, - getContainerOfType, LangiumDocument, PrecomputedScopes, } from 'langium'; @@ -35,7 +35,7 @@ export class SafeDsScopeComputation extends DefaultScopeComputation { } // Modules that don't state their package don't export anything - const containingModule = getContainerOfType(node, isSdsModule); + const containingModule = AstUtils.getContainerOfType(node, isSdsModule); if (!containingModule || !containingModule.name) { return; } @@ -72,7 +72,7 @@ export class SafeDsScopeComputation extends DefaultScopeComputation { this.addToScopesIfKeyIsDefined(scopes, node.constraintList, description); this.addToScopesIfKeyIsDefined(scopes, node.body, description); - const containingDeclaration = getContainerOfType(node.$container, isSdsDeclaration); + const containingDeclaration = AstUtils.getContainerOfType(node.$container, isSdsDeclaration); if (isSdsModule(containingDeclaration)) { this.addToScopesIfKeyIsDefined(scopes, containingDeclaration, description); } @@ -88,7 +88,7 @@ export class SafeDsScopeComputation extends DefaultScopeComputation { this.addToScopesIfKeyIsDefined(scopes, node.body, description); - const containingDeclaration = getContainerOfType(node.$container, isSdsDeclaration); + const containingDeclaration = AstUtils.getContainerOfType(node.$container, isSdsDeclaration); if (isSdsModule(containingDeclaration)) { this.addToScopesIfKeyIsDefined(scopes, containingDeclaration, description); } @@ -108,7 +108,7 @@ export class SafeDsScopeComputation extends DefaultScopeComputation { } private processSdsParameter(node: SdsParameter, document: LangiumDocument, scopes: PrecomputedScopes): void { - const containingCallable = getContainerOfType(node, isSdsParameterList)?.$container; + const containingCallable = AstUtils.getContainerOfType(node, isSdsParameterList)?.$container; if (!containingCallable) { /* c8 ignore next 2 */ return; @@ -140,7 +140,7 @@ export class SafeDsScopeComputation extends DefaultScopeComputation { document: LangiumDocument, scopes: PrecomputedScopes, ): void { - const containingDeclaration = getContainerOfType(node, isSdsTypeParameterList)?.$container; + const containingDeclaration = AstUtils.getContainerOfType(node, isSdsTypeParameterList)?.$container; if (!containingDeclaration) { /* c8 ignore next 2 */ return; diff --git a/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts b/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts index 518658ee8..893402d3d 100644 --- a/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts +++ b/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-provider.ts @@ -2,10 +2,9 @@ import { AstNode, AstNodeDescription, AstReflection, + AstUtils, DefaultScopeProvider, EMPTY_SCOPE, - getContainerOfType, - getDocument, ReferenceInfo, Scope, WorkspaceCache, @@ -134,7 +133,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { } private getScopeForArgumentParameter(node: SdsArgument): Scope { - const containingAbstractCall = getContainerOfType(node, isSdsAbstractCall); + const containingAbstractCall = AstUtils.getContainerOfType(node, isSdsAbstractCall); const callable = this.nodeMapper.callToCallable(containingAbstractCall); if (!callable) { return EMPTY_SCOPE; @@ -147,7 +146,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { private getScopeForImportedDeclarationDeclaration(node: SdsImportedDeclaration): Scope { const ownPackageName = getPackageName(node); - const containingQualifiedImport = getContainerOfType(node, isSdsQualifiedImport); + const containingQualifiedImport = AstUtils.getContainerOfType(node, isSdsQualifiedImport); if (!containingQualifiedImport) { /* c8 ignore next 2 */ return EMPTY_SCOPE; @@ -199,7 +198,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { let currentScope = this.coreDeclarations(SdsNamedTypeDeclaration, super.getScope(context)); // Type parameters (up to the containing type parameter) - const containingTypeParameter = getContainerOfType(node, isSdsTypeParameter); + const containingTypeParameter = AstUtils.getContainerOfType(node, isSdsTypeParameter); if (containingTypeParameter) { const allTypeParameters = getTypeParameters(containingTypeParameter?.$container); const priorTypeParameters = allTypeParameters.slice(0, containingTypeParameter.$containerIndex); @@ -301,7 +300,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { // Cannot reference the target of an annotation call from inside the annotation call let start: AstNode | undefined; - const containingAnnotationCall = getContainerOfType(node, isSdsAnnotationCall); + const containingAnnotationCall = AstUtils.getContainerOfType(node, isSdsAnnotationCall); if (containingAnnotationCall) { start = getAnnotationCallTarget(containingAnnotationCall)?.$container; } else { @@ -309,23 +308,23 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { } // Only containing classes, enums, and enum variants can be referenced - let current = getContainerOfType(start, isSdsNamedTypeDeclaration); + let current = AstUtils.getContainerOfType(start, isSdsNamedTypeDeclaration); while (current) { result.push(current); - current = getContainerOfType(current.$container, isSdsNamedTypeDeclaration); + current = AstUtils.getContainerOfType(current.$container, isSdsNamedTypeDeclaration); } return this.createScopeForNodes(result, outerScope); } private globalDeclarationsInSameFile(node: AstNode, outerScope: Scope): Scope { - const module = getContainerOfType(node, isSdsModule); + const module = AstUtils.getContainerOfType(node, isSdsModule); if (!module) { /* c8 ignore next 2 */ return outerScope; } - const precomputed = getDocument(module).precomputedScopes?.get(module); + const precomputed = AstUtils.getDocument(module).precomputedScopes?.get(module); if (!precomputed) { /* c8 ignore next 2 */ return outerScope; @@ -335,10 +334,10 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { } private localDeclarations(node: AstNode, outerScope: Scope): Scope { - const containingCallable = getContainerOfType(node.$container, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node.$container, isSdsCallable); // Parameters (up to the containing parameter) - const containingParameter = getContainerOfType(node, isSdsParameter); + const containingParameter = AstUtils.getContainerOfType(node, isSdsParameter); let parameters: Iterable; if (containingCallable && containingParameter) { @@ -348,7 +347,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { } // Placeholders up to the containing statement - const containingStatement = getContainerOfType(node.$container, isSdsStatement); + const containingStatement = AstUtils.getContainerOfType(node.$container, isSdsStatement); let placeholders: Iterable; if (!containingCallable || isContainedInOrEqual(containingStatement, containingCallable)) { @@ -389,7 +388,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { return; } - const containingBlock = getContainerOfType(statement, isSdsBlock); + const containingBlock = AstUtils.getContainerOfType(statement, isSdsBlock); for (const current of getStatements(containingBlock)) { if (current === statement) { return; @@ -402,7 +401,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { } private getScopeForTypeArgumentTypeParameter(node: SdsTypeArgument): Scope { - const containingNamedType = getContainerOfType(node, isSdsNamedType); + const containingNamedType = AstUtils.getContainerOfType(node, isSdsNamedType); if (!containingNamedType) { /* c8 ignore next 2 */ return EMPTY_SCOPE; @@ -418,7 +417,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { } private getScopeForYieldResult(node: SdsYield): Scope { - const containingSegment = getContainerOfType(node, isSdsSegment); + const containingSegment = AstUtils.getContainerOfType(node, isSdsSegment); if (!containingSegment) { return EMPTY_SCOPE; } @@ -428,7 +427,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { protected override getGlobalScope(referenceType: string, context: ReferenceInfo): Scope { const node = context.container; - const key = `${getDocument(node).uri}~${referenceType}`; + const key = `${AstUtils.getDocument(node).uri}~${referenceType}`; return this.globalScopeCache.get(key, () => this.getGlobalScopeForNode(referenceType, node)); } @@ -466,7 +465,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider { } private explicitlyImportedDeclarations(referenceType: string, node: AstNode): AstNodeDescription[] { - const containingModule = getContainerOfType(node, isSdsModule); + const containingModule = AstUtils.getContainerOfType(node, isSdsModule); const imports = getImports(containingModule); const result: AstNodeDescription[] = []; diff --git a/packages/safe-ds-lang/src/language/typing/model.ts b/packages/safe-ds-lang/src/language/typing/model.ts index 69ea8be14..7bd4f3f85 100644 --- a/packages/safe-ds-lang/src/language/typing/model.ts +++ b/packages/safe-ds-lang/src/language/typing/model.ts @@ -12,7 +12,7 @@ import { } from '../generated/ast.js'; import { getTypeParameters, Parameter } from '../helpers/nodeProperties.js'; import { Constant, NullConstant } from '../partialEvaluation/model.js'; -import { getContainerOfType, stream } from 'langium'; +import { AstUtils, stream } from 'langium'; import { SafeDsCoreTypes } from './safe-ds-core-types.js'; import { SafeDsServices } from '../safe-ds-module.js'; import { SafeDsTypeFactory } from './safe-ds-type-factory.js'; @@ -514,7 +514,7 @@ export class EnumVariantType extends NamedType { } override toString(): string { - const containingEnum = getContainerOfType(this.declaration, isSdsEnum); + const containingEnum = AstUtils.getContainerOfType(this.declaration, isSdsEnum); if (containingEnum) { return `${containingEnum.name}.${super.toString()}`; } else { diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts index 64c14c6ab..1dca69928 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts @@ -1,4 +1,4 @@ -import { EMPTY_STREAM, getContainerOfType, stream, Stream } from 'langium'; +import { AstUtils, EMPTY_STREAM, stream, Stream } from 'langium'; import { SafeDsClasses } from '../builtins/safe-ds-classes.js'; import { isSdsClass, isSdsNamedType, SdsClass, type SdsClassMember } from '../generated/ast.js'; import { getClassMembers, getParentTypes, isStatic } from '../helpers/nodeProperties.js'; @@ -91,7 +91,7 @@ export class SafeDsClassHierarchy { } // Don't consider members with the same name as a previous member - const containingClass = getContainerOfType(node, isSdsClass); + const containingClass = AstUtils.getContainerOfType(node, isSdsClass); if (!containingClass) { return undefined; } diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts index 6bf454334..ab910b340 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts @@ -1,4 +1,3 @@ -import { getContainerOfType } from 'langium'; import type { SafeDsClasses } from '../builtins/safe-ds-classes.js'; import { isSdsCallable, isSdsClass, isSdsEnum, SdsDeclaration } from '../generated/ast.js'; import { Enum, EnumVariant, getTypeParameters, Parameter, TypeParameter } from '../helpers/nodeProperties.js'; @@ -21,6 +20,7 @@ import { SafeDsClassHierarchy } from './safe-ds-class-hierarchy.js'; import { SafeDsCoreTypes } from './safe-ds-core-types.js'; import type { SafeDsTypeComputer } from './safe-ds-type-computer.js'; import { isEmpty } from '../../helpers/collections.js'; +import { AstUtils } from 'langium'; export class SafeDsTypeChecker { private readonly builtinClasses: SafeDsClasses; @@ -233,7 +233,7 @@ export class SafeDsTypeChecker { if (other instanceof ClassType) { return other.declaration === this.builtinClasses.Any; } else if (other instanceof EnumType) { - const containingEnum = getContainerOfType(type.declaration, isSdsEnum); + const containingEnum = AstUtils.getContainerOfType(type.declaration, isSdsEnum); return containingEnum === other.declaration; } else if (other instanceof EnumVariantType) { return type.declaration === other.declaration; diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts index 2af0fcb9c..5caa13305 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts @@ -1,13 +1,4 @@ -import { - AstNode, - AstNodeLocator, - EMPTY_STREAM, - getContainerOfType, - getDocument, - Stream, - stream, - WorkspaceCache, -} from 'langium'; +import { AstNode, AstNodeLocator, AstUtils, EMPTY_STREAM, Stream, stream, WorkspaceCache } from 'langium'; import { isEmpty } from '../../helpers/collections.js'; import { isSdsAnnotation, @@ -171,7 +162,7 @@ export class SafeDsTypeComputer { } private getNodeId(node: AstNode) { - const documentUri = getDocument(node).uri.toString(); + const documentUri = AstUtils.getDocument(node).uri.toString(); const nodePath = this.astNodeLocator.getAstNodePath(node); return `${documentUri}~${nodePath}`; } @@ -193,7 +184,7 @@ export class SafeDsTypeComputer { } private computeTypeOfAssignee(node: SdsAssignee): Type { - const containingAssignment = getContainerOfType(node, isSdsAssignment); + const containingAssignment = AstUtils.getContainerOfType(node, isSdsAssignment); if (!containingAssignment) { /* c8 ignore next 2 */ return UnknownType; @@ -284,7 +275,7 @@ export class SafeDsTypeComputer { } private computeTypeOfParameterContext(node: SdsParameter): Type { - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); if (!isSdsLambda(containingCallable)) { return UnknownType; } @@ -562,7 +553,7 @@ export class SafeDsTypeComputer { // Substitute type parameters (must also work for inherited members) if (receiverType instanceof ClassType) { - const classContainingMember = getContainerOfType(node.member?.target.ref, isSdsClass); + const classContainingMember = AstUtils.getContainerOfType(node.member?.target.ref, isSdsClass); const typeContainingMember = this.computeMatchingSupertype(receiverType, classContainingMember); if (typeContainingMember) { @@ -1221,7 +1212,7 @@ export class SafeDsTypeComputer { } else if (!isEmpty(enumVariantTypes)) { candidates.push(enumVariantTypes[0]!.withExplicitNullability(isNullable)); - const containingEnum = getContainerOfType(enumVariantTypes[0]!.declaration, isSdsEnum); + const containingEnum = AstUtils.getContainerOfType(enumVariantTypes[0]!.declaration, isSdsEnum); if (containingEnum) { candidates.push(this.factory.createEnumType(containingEnum, isNullable)); } diff --git a/packages/safe-ds-lang/src/language/validation/builtins/pythonCall.ts b/packages/safe-ds-lang/src/language/validation/builtins/pythonCall.ts index 4810cf5c6..29d8e4b07 100644 --- a/packages/safe-ds-lang/src/language/validation/builtins/pythonCall.ts +++ b/packages/safe-ds-lang/src/language/validation/builtins/pythonCall.ts @@ -1,4 +1,4 @@ -import { hasContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { isSdsClass, SdsFunction } from '../../generated/ast.js'; import { SafeDsServices } from '../../safe-ds-module.js'; import { findFirstAnnotationCallOf, getParameters } from '../../helpers/nodeProperties.js'; @@ -21,7 +21,7 @@ export const pythonCallMustOnlyContainValidTemplateExpressions = (services: Safe // Compute valid template expressions const validTemplateExpressions = new Set(getParameters(node).map((it) => `\$${it.name}`)); - if (hasContainerOfType(node, isSdsClass)) { + if (AstUtils.hasContainerOfType(node, isSdsClass)) { validTemplateExpressions.add('$this'); } diff --git a/packages/safe-ds-lang/src/language/validation/experimentalLanguageFeatures.ts b/packages/safe-ds-lang/src/language/validation/experimentalLanguageFeatures.ts index e137b0ce3..773df37e8 100644 --- a/packages/safe-ds-lang/src/language/validation/experimentalLanguageFeatures.ts +++ b/packages/safe-ds-lang/src/language/validation/experimentalLanguageFeatures.ts @@ -1,4 +1,4 @@ -import { hasContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { isSdsMap, isSdsUnionType, @@ -55,7 +55,7 @@ export const mapsShouldBeUsedWithCaution = (services: SafeDsServices) => { } // There's already a warning on the container - if (hasContainerOfType(node.$container, isSdsMap)) { + if (AstUtils.hasContainerOfType(node.$container, isSdsMap)) { return; } @@ -76,7 +76,7 @@ export const unionTypesShouldBeUsedWithCaution = (services: SafeDsServices) => { } // There's already a warning on the container - if (hasContainerOfType(node.$container, isSdsUnionType)) { + if (AstUtils.hasContainerOfType(node.$container, isSdsUnionType)) { return; } diff --git a/packages/safe-ds-lang/src/language/validation/inheritance.ts b/packages/safe-ds-lang/src/language/validation/inheritance.ts index d2e21cffd..dbe607080 100644 --- a/packages/safe-ds-lang/src/language/validation/inheritance.ts +++ b/packages/safe-ds-lang/src/language/validation/inheritance.ts @@ -1,10 +1,11 @@ -import { expandToStringWithNL, getContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { isEmpty, isEqualSet } from '../../helpers/collections.js'; import { isSdsClass, isSdsFunction, SdsClass, type SdsClassMember } from '../generated/ast.js'; import { getParentTypes, getQualifiedName } from '../helpers/nodeProperties.js'; import { SafeDsServices } from '../safe-ds-module.js'; import { ClassType, Type, UnknownType } from '../typing/model.js'; import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js'; +import { expandToStringWithNL } from 'langium/generate'; export const CODE_INHERITANCE_CYCLE = 'inheritance/cycle'; export const CODE_INHERITANCE_MULTIPLE_INHERITANCE = 'inheritance/multiple-inheritance'; @@ -88,11 +89,11 @@ const computeMemberTypes = ( let overriddenMemberType = typeComputer.computeType(overriddenMember); // Substitute type parameters of class containing the overridden member - const classContainingOwnMember = getContainerOfType(ownMember, isSdsClass); + const classContainingOwnMember = AstUtils.getContainerOfType(ownMember, isSdsClass); const typeContainingOwnMember = typeComputer.computeType(classContainingOwnMember); if (typeContainingOwnMember instanceof ClassType) { - const classContainingOverriddenMember = getContainerOfType(overriddenMember, isSdsClass); + const classContainingOverriddenMember = AstUtils.getContainerOfType(overriddenMember, isSdsClass); const typeContainingOverriddenMember = typeComputer.computeMatchingSupertype( typeContainingOwnMember, classContainingOverriddenMember, @@ -142,7 +143,7 @@ interface ComputeMemberTypesResult { } const isInSafedsLangAnyClass = (services: SafeDsServices, node: SdsClassMember): boolean => { - const containingClass = getContainerOfType(node, isSdsClass); + const containingClass = AstUtils.getContainerOfType(node, isSdsClass); return ( isSdsClass(containingClass) && getQualifiedName(containingClass) === getQualifiedName(services.builtins.Classes.Any) diff --git a/packages/safe-ds-lang/src/language/validation/names.ts b/packages/safe-ds-lang/src/language/validation/names.ts index 3a40d4572..6dbe7471b 100644 --- a/packages/safe-ds-lang/src/language/validation/names.ts +++ b/packages/safe-ds-lang/src/language/validation/names.ts @@ -1,4 +1,4 @@ -import { AstNodeDescription, getDocument, ValidationAcceptor } from 'langium'; +import { AstNodeDescription, AstUtils, ValidationAcceptor } from 'langium'; import { duplicatesBy } from '../../helpers/collections.js'; import { listBuiltinFiles } from '../builtins/fileFinder.js'; import { BUILTINS_LANG_PACKAGE, BUILTINS_ROOT_PACKAGE } from '../builtins/packageNames.js'; @@ -260,7 +260,7 @@ export const moduleMemberMustHaveNameThatIsUniqueInPackage = (services: SafeDsSe const builtinUris = new Set(listBuiltinFiles().map((it) => it.toString())); return (node: SdsModule, accept: ValidationAcceptor): void => { - const moduleUri = getDocument(node).uri?.toString(); + const moduleUri = AstUtils.getDocument(node).uri?.toString(); if (builtinUris.has(moduleUri)) { return; } diff --git a/packages/safe-ds-lang/src/language/validation/other/argumentLists.ts b/packages/safe-ds-lang/src/language/validation/other/argumentLists.ts index ca6794f87..b3cd68843 100644 --- a/packages/safe-ds-lang/src/language/validation/other/argumentLists.ts +++ b/packages/safe-ds-lang/src/language/validation/other/argumentLists.ts @@ -1,4 +1,4 @@ -import { getContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { duplicatesBy, isEmpty } from '../../../helpers/collections.js'; import { pluralize } from '../../../helpers/strings.js'; import { isSdsAnnotation, isSdsCall, SdsAbstractCall, SdsArgumentList } from '../../generated/ast.js'; @@ -80,7 +80,7 @@ export const argumentListMustNotSetParameterMultipleTimes = (services: SafeDsSer return (node: SdsArgumentList, accept: ValidationAcceptor): void => { // We already report other errors in this case - const containingCall = getContainerOfType(node, isSdsCall); + const containingCall = AstUtils.getContainerOfType(node, isSdsCall); const callable = nodeMapper.callToCallable(containingCall); if (isSdsAnnotation(callable)) { return; diff --git a/packages/safe-ds-lang/src/language/validation/other/declarations/annotationCalls.ts b/packages/safe-ds-lang/src/language/validation/other/declarations/annotationCalls.ts index 4d321d529..af36262fc 100644 --- a/packages/safe-ds-lang/src/language/validation/other/declarations/annotationCalls.ts +++ b/packages/safe-ds-lang/src/language/validation/other/declarations/annotationCalls.ts @@ -1,4 +1,4 @@ -import { getContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { isEmpty } from '../../../../helpers/collections.js'; import { isSdsCallable, @@ -92,6 +92,6 @@ export const lambdaParametersMustNotBeAnnotated = (node: SdsLambda, accept: Vali }; export const parameterCanBeAnnotated = (node: SdsParameter) => { - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); return !isSdsCallableType(containingCallable) && !isSdsLambda(containingCallable); }; diff --git a/packages/safe-ds-lang/src/language/validation/other/declarations/parameters.ts b/packages/safe-ds-lang/src/language/validation/other/declarations/parameters.ts index e83a901ed..b33d3bd4a 100644 --- a/packages/safe-ds-lang/src/language/validation/other/declarations/parameters.ts +++ b/packages/safe-ds-lang/src/language/validation/other/declarations/parameters.ts @@ -1,4 +1,4 @@ -import { getContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { isSdsAnnotation, isSdsCallable, SdsParameter } from '../../../generated/ast.js'; import { Parameter } from '../../../helpers/nodeProperties.js'; import { SafeDsServices } from '../../../safe-ds-module.js'; @@ -15,7 +15,7 @@ export const constantParameterMustHaveConstantDefaultValue = (services: SafeDsSe } if (!partialEvaluator.canBeValueOfConstantParameter(node.defaultValue)) { - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); const kind = isSdsAnnotation(containingCallable) ? 'annotation' : 'constant'; accept('error', `Default values of ${kind} parameters must be constant.`, { @@ -38,7 +38,7 @@ export const constantParameterMustHaveTypeThatCanBeEvaluatedToConstant = (servic const type = typeComputer.computeType(node); if (!typeChecker.canBeTypeOfConstantParameter(type)) { - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); const kind = isSdsAnnotation(containingCallable) ? 'An annotation' : 'A constant'; accept('error', `${kind} parameter cannot have type '${type.toString()}'.`, { diff --git a/packages/safe-ds-lang/src/language/validation/other/declarations/placeholders.ts b/packages/safe-ds-lang/src/language/validation/other/declarations/placeholders.ts index 05249a5ac..10f16f36f 100644 --- a/packages/safe-ds-lang/src/language/validation/other/declarations/placeholders.ts +++ b/packages/safe-ds-lang/src/language/validation/other/declarations/placeholders.ts @@ -7,7 +7,7 @@ import { isSdsStatement, SdsPlaceholder, } from '../../../generated/ast.js'; -import { getContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { SafeDsServices } from '../../../safe-ds-module.js'; import { getStatements } from '../../../helpers/nodeProperties.js'; import { DiagnosticTag } from 'vscode-languageserver'; @@ -21,7 +21,7 @@ export const placeholdersMustNotBeAnAlias = (node: SdsPlaceholder, accept: Valid return; } - const containingAssignment = getContainerOfType(node, isSdsAssignment); + const containingAssignment = AstUtils.getContainerOfType(node, isSdsAssignment); const rhs = containingAssignment?.expression; if (!isSdsReference(rhs)) { return; @@ -45,8 +45,8 @@ export const placeholderShouldBeUsed = } // Don't show a warning if the placeholder is declared in the last statement in the block - const containingStatement = getContainerOfType(node, isSdsStatement); - const containingBlock = getContainerOfType(containingStatement, isSdsBlock); + const containingStatement = AstUtils.getContainerOfType(node, isSdsStatement); + const containingBlock = AstUtils.getContainerOfType(containingStatement, isSdsBlock); const allStatementsInBlock = getStatements(containingBlock); if (last(allStatementsInBlock) === containingStatement) { return; diff --git a/packages/safe-ds-lang/src/language/validation/other/declarations/typeParameters.ts b/packages/safe-ds-lang/src/language/validation/other/declarations/typeParameters.ts index 0f2184879..a9f0b58ac 100644 --- a/packages/safe-ds-lang/src/language/validation/other/declarations/typeParameters.ts +++ b/packages/safe-ds-lang/src/language/validation/other/declarations/typeParameters.ts @@ -1,4 +1,4 @@ -import { AstNode, findLocalReferences, getContainerOfType, hasContainerOfType, ValidationAcceptor } from 'langium'; +import { AstNode, AstUtils, ValidationAcceptor } from 'langium'; import { isSdsCallable, isSdsClass, @@ -24,7 +24,7 @@ export const CODE_TYPE_PARAMETER_USAGE = 'type-parameter/usage'; export const CODE_TYPE_PARAMETER_VARIANCE = 'type-parameter/variance'; export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, accept: ValidationAcceptor) => { - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); /* c8 ignore start */ if (!containingCallable) { return; @@ -39,14 +39,14 @@ export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, a // A type parameter must be referenced in the parameter list of the containing callable... let typeParameterHasInsufficientContext = !containingCallable.parameterList || - findLocalReferences(node, containingCallable.parameterList) + AstUtils.findLocalReferences(node, containingCallable.parameterList) // ...but references in a union type or in the parameter list of a callable type don't count .filter((reference) => { const referenceNode = reference.$refNode?.astNode; - const containingParameterList = getContainerOfType(referenceNode, isSdsParameterList); + const containingParameterList = AstUtils.getContainerOfType(referenceNode, isSdsParameterList); return ( - !hasContainerOfType(referenceNode, isSdsUnionType) && + !AstUtils.hasContainerOfType(referenceNode, isSdsUnionType) && containingParameterList === containingCallable.parameterList ); }) @@ -81,7 +81,7 @@ export const typeParameterMustBeUsedInCorrectPosition = (services: SafeDsService const nodeMapper = services.helpers.NodeMapper; return (node: SdsTypeParameter, accept: ValidationAcceptor) => { - const declarationWithTypeParameter = getContainerOfType(node.$container, isSdsDeclaration); + const declarationWithTypeParameter = AstUtils.getContainerOfType(node.$container, isSdsDeclaration); // Early exit if ( @@ -91,7 +91,7 @@ export const typeParameterMustBeUsedInCorrectPosition = (services: SafeDsService return; } - findLocalReferences(node).forEach((it) => { + AstUtils.findLocalReferences(node).forEach((it) => { const reference = it.$refNode?.astNode; if (!reference) { /* c8 ignore next 2 */ @@ -141,12 +141,12 @@ export const typeParameterMustBeUsedInCorrectPosition = (services: SafeDsService }; const isInConstructor = (node: AstNode) => { - const parameterList = getContainerOfType(node, isSdsParameterList); + const parameterList = AstUtils.getContainerOfType(node, isSdsParameterList); return isSdsClass(parameterList?.$container); }; const classTypeParameterIsUsedInCorrectPosition = (classWithTypeParameter: SdsClass, reference: AstNode) => { - const containingClassMember = getContainerOfType(reference, isSdsClassMember); + const containingClassMember = AstUtils.getContainerOfType(reference, isSdsClassMember); // Handle usage in constructor if (!containingClassMember || containingClassMember === classWithTypeParameter) { @@ -159,7 +159,7 @@ const classTypeParameterIsUsedInCorrectPosition = (classWithTypeParameter: SdsCl } // Handle usage inside nested enums and classes (could be an instance attribute/function) - const containingNamedTypeDeclaration = getContainerOfType(reference, isSdsNamedTypeDeclaration); + const containingNamedTypeDeclaration = AstUtils.getContainerOfType(reference, isSdsNamedTypeDeclaration); return !containingNamedTypeDeclaration || containingNamedTypeDeclaration === classWithTypeParameter; }; @@ -222,7 +222,7 @@ export const typeParameterMustOnlyBeVariantOnClass = (node: SdsTypeParameter, ac return; } - const declarationWithTypeParameter = getContainerOfType(node.$container, isSdsDeclaration); + const declarationWithTypeParameter = AstUtils.getContainerOfType(node.$container, isSdsDeclaration); if (declarationWithTypeParameter && !isSdsClass(declarationWithTypeParameter)) { accept('error', 'Only type parameters of classes can be variant.', { node, diff --git a/packages/safe-ds-lang/src/language/validation/other/statements/assignments.ts b/packages/safe-ds-lang/src/language/validation/other/statements/assignments.ts index 32584e282..dc2661442 100644 --- a/packages/safe-ds-lang/src/language/validation/other/statements/assignments.ts +++ b/packages/safe-ds-lang/src/language/validation/other/statements/assignments.ts @@ -1,5 +1,5 @@ import { isSdsCall, isSdsPipeline, SdsAssignment, SdsYield } from '../../../generated/ast.js'; -import { getContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { SafeDsServices } from '../../../safe-ds-module.js'; import { getAbstractResults, getAssignees } from '../../../helpers/nodeProperties.js'; import { pluralize } from '../../../../helpers/strings.js'; @@ -50,7 +50,7 @@ export const assignmentShouldNotImplicitlyIgnoreResult = (services: SafeDsServic }; export const yieldMustNotBeUsedInPipeline = (node: SdsYield, accept: ValidationAcceptor): void => { - const containingPipeline = getContainerOfType(node, isSdsPipeline); + const containingPipeline = AstUtils.getContainerOfType(node, isSdsPipeline); if (containingPipeline) { accept('error', 'Yield must not be used in a pipeline.', { diff --git a/packages/safe-ds-lang/src/language/validation/other/types/callableTypes.ts b/packages/safe-ds-lang/src/language/validation/other/types/callableTypes.ts index cf50d9397..98da98ce2 100644 --- a/packages/safe-ds-lang/src/language/validation/other/types/callableTypes.ts +++ b/packages/safe-ds-lang/src/language/validation/other/types/callableTypes.ts @@ -1,4 +1,4 @@ -import { getContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { isSdsCallableType, isSdsParameter, SdsCallableType } from '../../../generated/ast.js'; import { getParameters, Parameter } from '../../../helpers/nodeProperties.js'; @@ -37,12 +37,12 @@ const contextIsCorrect = (node: SdsCallableType): boolean => { } // Check whether we already show an error on a containing callable type - let containingCallableType = getContainerOfType(node.$container, isSdsCallableType); + let containingCallableType = AstUtils.getContainerOfType(node.$container, isSdsCallableType); while (containingCallableType) { if (!isSdsParameter(containingCallableType.$container)) { return true; // Container already has wrong context } - containingCallableType = getContainerOfType(containingCallableType.$container, isSdsCallableType); + containingCallableType = AstUtils.getContainerOfType(containingCallableType.$container, isSdsCallableType); } return false; diff --git a/packages/safe-ds-lang/src/language/validation/other/types/unionTypes.ts b/packages/safe-ds-lang/src/language/validation/other/types/unionTypes.ts index 0aecd470d..fa4b61735 100644 --- a/packages/safe-ds-lang/src/language/validation/other/types/unionTypes.ts +++ b/packages/safe-ds-lang/src/language/validation/other/types/unionTypes.ts @@ -7,7 +7,7 @@ import { isSdsUnionType, SdsUnionType, } from '../../../generated/ast.js'; -import { getContainerOfType, hasContainerOfType, ValidationAcceptor } from 'langium'; +import { AstUtils, ValidationAcceptor } from 'langium'; import { getTypeArguments } from '../../../helpers/nodeProperties.js'; import { SafeDsServices } from '../../../safe-ds-module.js'; import { Type } from '../../../typing/model.js'; @@ -27,7 +27,7 @@ export const unionTypeMustBeUsedInCorrectContext = (node: SdsUnionType, accept: }; const contextIsCorrect = (node: SdsUnionType): boolean => { - if (hasContainerOfType(node.$container, isSdsUnionType)) { + if (AstUtils.hasContainerOfType(node.$container, isSdsUnionType)) { return true; } @@ -36,7 +36,7 @@ const contextIsCorrect = (node: SdsUnionType): boolean => { return false; } - const containingCallable = getContainerOfType(container, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(container, isSdsCallable); return ( !containingCallable || isSdsAnnotation(containingCallable) || diff --git a/packages/safe-ds-lang/src/language/validation/types.ts b/packages/safe-ds-lang/src/language/validation/types.ts index 3d5e139e6..c4be233ff 100644 --- a/packages/safe-ds-lang/src/language/validation/types.ts +++ b/packages/safe-ds-lang/src/language/validation/types.ts @@ -1,4 +1,4 @@ -import { AstNode, getContainerOfType, ValidationAcceptor } from 'langium'; +import { AstNode, AstUtils, ValidationAcceptor } from 'langium'; import { isEmpty } from '../../helpers/collections.js'; import { pluralize } from '../../helpers/strings.js'; import { @@ -469,7 +469,7 @@ export const attributeMustHaveTypeHint = (node: SdsAttribute, accept: Validation export const parameterMustHaveTypeHint = (node: SdsParameter, accept: ValidationAcceptor): void => { if (!node.type) { - const containingCallable = getContainerOfType(node, isSdsCallable); + const containingCallable = AstUtils.getContainerOfType(node, isSdsCallable); if (!isSdsLambda(containingCallable)) { accept('error', 'A parameter must have a type hint.', { diff --git a/packages/safe-ds-lang/src/language/workspace/safe-ds-package-manager.ts b/packages/safe-ds-lang/src/language/workspace/safe-ds-package-manager.ts index f46dcf4fe..f8ce92376 100644 --- a/packages/safe-ds-lang/src/language/workspace/safe-ds-package-manager.ts +++ b/packages/safe-ds-lang/src/language/workspace/safe-ds-package-manager.ts @@ -9,7 +9,7 @@ import { LangiumDocuments, } from 'langium'; import { isSdsSegment } from '../generated/ast.js'; -import { isInternal, getPackageName } from '../helpers/nodeProperties.js'; +import { getPackageName, isInternal } from '../helpers/nodeProperties.js'; export class SafeDsPackageManager { private readonly astNodeLocator: AstNodeLocator; @@ -142,16 +142,16 @@ export class SafeDsPackageManager { private loadAstNode(nodeDescription: AstNodeDescription): AstNode | undefined { if (nodeDescription.node) { - /* c8 ignore next 2 */ return nodeDescription.node; } - - if (this.langiumDocuments.hasDocument(nodeDescription.documentUri)) { - const document = this.langiumDocuments.getOrCreateDocument(nodeDescription.documentUri); - return this.astNodeLocator.getAstNode(document.parseResult.value, nodeDescription.path); - } /* c8 ignore start */ else { + /* c8 ignore start */ + const document = this.langiumDocuments.getDocument(nodeDescription.documentUri); + if (!document) { return undefined; - } /* c8 ignore stop */ + } + + return this.astNodeLocator.getAstNode(document.parseResult.value, nodeDescription.path); + /* c8 ignore stop */ } /** diff --git a/packages/safe-ds-lang/src/language/workspace/safe-ds-workspace-manager.ts b/packages/safe-ds-lang/src/language/workspace/safe-ds-workspace-manager.ts index 6aa2767f7..c13f9b045 100644 --- a/packages/safe-ds-lang/src/language/workspace/safe-ds-workspace-manager.ts +++ b/packages/safe-ds-lang/src/language/workspace/safe-ds-workspace-manager.ts @@ -1,6 +1,7 @@ -import { DefaultWorkspaceManager, LangiumDocument, LangiumDocumentFactory, LangiumSharedServices } from 'langium'; +import { DefaultWorkspaceManager, LangiumDocument, LangiumDocumentFactory } from 'langium'; import { WorkspaceFolder } from 'vscode-languageserver'; import { listBuiltinFiles } from '../builtins/fileFinder.js'; +import { type LangiumSharedServices } from 'langium/lsp'; export class SafeDsWorkspaceManager extends DefaultWorkspaceManager { private documentFactory: LangiumDocumentFactory; @@ -18,7 +19,7 @@ export class SafeDsWorkspaceManager extends DefaultWorkspaceManager { // Load builtin files for (const uri of listBuiltinFiles()) { - collector(this.documentFactory.create(uri)); + collector(await this.documentFactory.fromUri(uri)); } } } diff --git a/packages/safe-ds-lang/tests/helpers/diagnostics.ts b/packages/safe-ds-lang/tests/helpers/diagnostics.ts index 78b54f709..916b40466 100644 --- a/packages/safe-ds-lang/tests/helpers/diagnostics.ts +++ b/packages/safe-ds-lang/tests/helpers/diagnostics.ts @@ -1,5 +1,6 @@ import { parseHelper } from 'langium/test'; -import { LangiumServices, URI } from 'langium'; +import { URI } from 'langium'; +import { type LangiumServices } from 'langium/lsp'; import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver'; import { TestDescriptionError } from './testDescription.js'; @@ -77,8 +78,8 @@ const getDiagnostics = async (services: LangiumServices, code: string): Promise< * @returns The diagnostics. */ const getDiagnosticsAtURI = (services: LangiumServices, uri: URI): Diagnostic[] => { - const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri); - return document.diagnostics ?? []; + const document = services.shared.workspace.LangiumDocuments.getDocument(uri); + return document?.diagnostics ?? []; }; /** diff --git a/packages/safe-ds-lang/tests/helpers/nodeFinder.ts b/packages/safe-ds-lang/tests/helpers/nodeFinder.ts index f5acf98e7..4804194b9 100644 --- a/packages/safe-ds-lang/tests/helpers/nodeFinder.ts +++ b/packages/safe-ds-lang/tests/helpers/nodeFinder.ts @@ -1,7 +1,7 @@ import { Location, Range } from 'vscode-languageserver'; import { isRangeEqual, parseHelper } from 'langium/test'; import { SafeDsServices } from '../../src/language/index.js'; -import { AstNode, streamAllContents, streamAst, URI } from 'langium'; +import { AstNode, AstUtils, URI } from 'langium'; import { SdsModule } from '../../src/language/generated/ast.js'; import { AssertionError } from 'assert'; import { locationToString } from '../../src/helpers/locations.js'; @@ -18,16 +18,16 @@ export const getNodeByLocation = (services: SafeDsServices, location: Location): const langiumDocuments = services.shared.workspace.LangiumDocuments; const uri = URI.parse(location.uri); - if (!langiumDocuments.hasDocument(uri)) { + const document = langiumDocuments.getDocument(uri); + if (!document) { throw new AssertionError({ message: `No document found at ${location.uri}`, }); } - const document = langiumDocuments.getOrCreateDocument(URI.parse(location.uri)); const module = document.parseResult.value as SdsModule; - for (const node of streamAllContents(module)) { + for (const node of AstUtils.streamAllContents(module)) { // Entire node matches the range const actualRange = node.$cstNode?.range; if (actualRange && isRangeEqual(actualRange, location.range)) { @@ -67,7 +67,7 @@ export const getNodeOfType = async ( ): Promise => { const document = await parseHelper(services)(code); const module = document.parseResult.value as SdsModule; - const candidates = streamAst(module).filter(predicate).toArray(); + const candidates = AstUtils.streamAst(module).filter(predicate).toArray(); if (candidates.length === 0) { throw new AssertionError({ message: `Expected to find a matching node but found none.` }); diff --git a/packages/safe-ds-lang/tests/helpers/testResources.ts b/packages/safe-ds-lang/tests/helpers/testResources.ts index 8d3236cde..e4fe532eb 100644 --- a/packages/safe-ds-lang/tests/helpers/testResources.ts +++ b/packages/safe-ds-lang/tests/helpers/testResources.ts @@ -107,7 +107,9 @@ export const loadDocuments = async ( uris: URI[], options: BuildOptions = {}, ): Promise => { - const documents = uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); + const documents = await Promise.all( + uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)), + ); await services.shared.workspace.DocumentBuilder.build(documents, options); return documents; }; diff --git a/packages/safe-ds-lang/tests/language/builtins/builtinFilesCorrectness.test.ts b/packages/safe-ds-lang/tests/language/builtins/builtinFilesCorrectness.test.ts index 614a54767..8db0b8d97 100644 --- a/packages/safe-ds-lang/tests/language/builtins/builtinFilesCorrectness.test.ts +++ b/packages/safe-ds-lang/tests/language/builtins/builtinFilesCorrectness.test.ts @@ -13,6 +13,7 @@ import { locationToString } from '../../../src/helpers/locations.js'; import { loadDocuments } from '../../helpers/testResources.js'; const services = createSafeDsServices(NodeFileSystem).SafeDs; +const langiumDocuments = services.shared.workspace.LangiumDocuments; const builtinFiles = listBuiltinFiles(); const ignoredWarnings: (number | string | undefined)[] = [ @@ -31,7 +32,7 @@ describe('builtin files', () => { })); it.each(testCases)('[$shortenedResourceName] should have no errors or warnings', async ({ uri }) => { - const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri); + const document = langiumDocuments.getDocument(uri)!; const errorsOrWarnings = document.diagnostics?.filter( diff --git a/packages/safe-ds-lang/tests/language/documentation/safe-ds-documentation-provider.test.ts b/packages/safe-ds-lang/tests/language/documentation/safe-ds-documentation-provider.test.ts index ebe139587..333761d09 100644 --- a/packages/safe-ds-lang/tests/language/documentation/safe-ds-documentation-provider.test.ts +++ b/packages/safe-ds-lang/tests/language/documentation/safe-ds-documentation-provider.test.ts @@ -1,4 +1,4 @@ -import { AstNode, EmptyFileSystem, expandToString } from 'langium'; +import { AstNode, EmptyFileSystem } from 'langium'; import { describe, expect, it } from 'vitest'; import { normalizeLineBreaks } from '../../../src/helpers/strings.js'; import { @@ -10,6 +10,7 @@ import { } from '../../../src/language/generated/ast.js'; import { createSafeDsServices } from '../../../src/language/index.js'; import { getNodeOfType } from '../../helpers/nodeFinder.js'; +import { expandToString } from 'langium/generate'; const services = createSafeDsServices(EmptyFileSystem).SafeDs; const documentationProvider = services.documentation.DocumentationProvider; diff --git a/packages/safe-ds-lang/tests/language/flow/safe-ds-call-graph-computer.test.ts b/packages/safe-ds-lang/tests/language/flow/safe-ds-call-graph-computer.test.ts index e1df18040..a6063e36f 100644 --- a/packages/safe-ds-lang/tests/language/flow/safe-ds-call-graph-computer.test.ts +++ b/packages/safe-ds-lang/tests/language/flow/safe-ds-call-graph-computer.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { isNamed, streamAst } from 'langium'; +import { AstUtils, isNamed } from 'langium'; import { isSdsBlockLambda, isSdsCall, @@ -31,7 +31,9 @@ describe('SafeDsCallGraphComputer', () => { const module = await getNodeOfType(services, test.code, isSdsModule); for (const { location, expectedCallables } of test.expectedCallGraphs) { - const node = streamAst(module).find((call) => isRangeEqual(call.$cstNode!.range, location.range)); + const node = AstUtils.streamAst(module).find((call) => + isRangeEqual(call.$cstNode!.range, location.range), + ); if (!node || (!isSdsCall(node) && !isSdsCallable(node))) { throw new Error(`Could not find call/callable at ${locationToString(location)}`); } diff --git a/packages/safe-ds-lang/tests/language/generation/creator.ts b/packages/safe-ds-lang/tests/language/generation/creator.ts index 68aa1663b..9f5d5e559 100644 --- a/packages/safe-ds-lang/tests/language/generation/creator.ts +++ b/packages/safe-ds-lang/tests/language/generation/creator.ts @@ -5,7 +5,7 @@ import { uriToShortenedTestResourceName, } from '../../helpers/testResources.js'; import path from 'path'; -import { createSafeDsServices } from '../../../src/language/index.js'; +import { createSafeDsServicesWithBuiltins } from '../../../src/language/index.js'; import { ErrorsInCodeError, getErrorsAtURI } from '../../helpers/diagnostics.js'; import { findTestChecks } from '../../helpers/testChecks.js'; import { Location } from 'vscode-languageserver'; @@ -14,8 +14,9 @@ import { TestDescription, TestDescriptionError } from '../../helpers/testDescrip import { locationToString } from '../../../src/helpers/locations.js'; import { URI } from 'langium'; -const services = createSafeDsServices(NodeFileSystem).SafeDs; -await services.shared.workspace.WorkspaceManager.initializeWorkspace([]); +const services = (await createSafeDsServicesWithBuiltins(NodeFileSystem)).SafeDs; +const langiumDocuments = services.shared.workspace.LangiumDocuments; + const rootResourceName = 'generation'; const runnerIntegration = 'runner integration'; @@ -35,7 +36,8 @@ const createGenerationTest = async (parentDirectory: URI, inputUris: URI[]): Pro await loadDocuments(services, inputUris, { validation: true }); for (const uri of inputUris) { - const code = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri).textDocument.getText(); + const document = langiumDocuments.getDocument(uri)!; + const code = document.textDocument.getText(); // File must not contain any errors const errors = getErrorsAtURI(services, uri); diff --git a/packages/safe-ds-lang/tests/language/generation/safe-ds-python-generator.test.ts b/packages/safe-ds-lang/tests/language/generation/safe-ds-python-generator.test.ts index 340a37318..8b43d9747 100644 --- a/packages/safe-ds-lang/tests/language/generation/safe-ds-python-generator.test.ts +++ b/packages/safe-ds-lang/tests/language/generation/safe-ds-python-generator.test.ts @@ -6,7 +6,9 @@ import { loadDocuments } from '../../helpers/testResources.js'; import { stream, URI } from 'langium'; const services = (await createSafeDsServicesWithBuiltins(NodeFileSystem)).SafeDs; +const langiumDocuments = services.shared.workspace.LangiumDocuments; const pythonGenerator = services.generation.PythonGenerator; + const generationTests = createGenerationTests(); describe('generation', async () => { @@ -22,9 +24,7 @@ describe('generation', async () => { // Get target placeholder name for "run until" let runUntilPlaceholderName: string | undefined = undefined; if (test.runUntil) { - const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument( - URI.parse(test.runUntil.uri), - ); + const document = langiumDocuments.getDocument(URI.parse(test.runUntil.uri))!; runUntilPlaceholderName = document.textDocument.getText(test.runUntil.range); } diff --git a/packages/safe-ds-lang/tests/language/lsp/safe-ds-call-hierarchy-provider.test.ts b/packages/safe-ds-lang/tests/language/lsp/safe-ds-call-hierarchy-provider.test.ts index 22e56c178..e364324a0 100644 --- a/packages/safe-ds-lang/tests/language/lsp/safe-ds-call-hierarchy-provider.test.ts +++ b/packages/safe-ds-lang/tests/language/lsp/safe-ds-call-hierarchy-provider.test.ts @@ -224,25 +224,25 @@ describe('SafeDsCallHierarchyProvider', async () => { }); const getActualSimpleIncomingCalls = async (code: string): Promise => { - return callHierarchyProvider - .incomingCalls({ - item: await getUniqueCallHierarchyItem(code), - }) - ?.map((call) => ({ - fromName: call.from.name, - fromRangesLength: call.fromRanges.length, - })); + const result = await callHierarchyProvider.incomingCalls({ + item: await getUniqueCallHierarchyItem(code), + }); + + return result?.map((call) => ({ + fromName: call.from.name, + fromRangesLength: call.fromRanges.length, + })); }; const getActualSimpleOutgoingCalls = async (code: string): Promise => { - return callHierarchyProvider - .outgoingCalls({ - item: await getUniqueCallHierarchyItem(code), - }) - ?.map((call) => ({ - toName: call.to.name, - fromRangesLength: call.fromRanges.length, - })); + const result = await callHierarchyProvider.outgoingCalls({ + item: await getUniqueCallHierarchyItem(code), + }); + + return result?.map((call) => ({ + toName: call.to.name, + fromRangesLength: call.fromRanges.length, + })); }; const getUniqueCallHierarchyItem = async (code: string): Promise => { @@ -257,7 +257,7 @@ const getUniqueCallHierarchyItem = async (code: string): Promise { // Check the results for (const description of descriptions) { - const document = langiumDocuments.getOrCreateDocument(URI.parse(description.uri)); + const document = langiumDocuments.getDocument(URI.parse(description.uri))!; const edits = changes[description.uri] ?? []; const actualOutput = TextDocument.applyEdits(document.textDocument, edits); diff --git a/packages/safe-ds-lang/tests/language/lsp/safe-ds-type-hierarchy-provider.test.ts b/packages/safe-ds-lang/tests/language/lsp/safe-ds-type-hierarchy-provider.test.ts index 0996a6125..c005939ad 100644 --- a/packages/safe-ds-lang/tests/language/lsp/safe-ds-type-hierarchy-provider.test.ts +++ b/packages/safe-ds-lang/tests/language/lsp/safe-ds-type-hierarchy-provider.test.ts @@ -128,23 +128,23 @@ describe('SafeDsTypeHierarchyProvider', async () => { }); const getActualSimpleSupertypes = async (code: string): Promise => { - return typeHierarchyProvider - .supertypes({ - item: await getUniqueTypeHierarchyItem(code), - }) - ?.map((type) => ({ - name: type.name, - })); + const result = await typeHierarchyProvider.supertypes({ + item: await getUniqueTypeHierarchyItem(code), + }); + + return result?.map((type) => ({ + name: type.name, + })); }; const getActualSimpleSubtypes = async (code: string): Promise => { - return typeHierarchyProvider - .subtypes({ - item: await getUniqueTypeHierarchyItem(code), - }) - ?.map((type) => ({ - name: type.name, - })); + const result = await typeHierarchyProvider.subtypes({ + item: await getUniqueTypeHierarchyItem(code), + }); + + return result?.map((type) => ({ + name: type.name, + })); }; const getUniqueTypeHierarchyItem = async (code: string): Promise => { @@ -159,7 +159,7 @@ const getUniqueTypeHierarchyItem = async (code: string): Promise { beforeEach(async () => { @@ -134,9 +135,7 @@ describe('scoping', async () => { * @throws AssertionError If no matching actual reference was found. */ const findActualTargetLocation = (expectedReference: ExpectedReference): Location | undefined => { - const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument( - URI.parse(expectedReference.location.uri), - ); + const document = langiumDocuments.getDocument(URI.parse(expectedReference.location.uri))!; const actualReference = findActualReference(document, expectedReference); @@ -190,8 +189,8 @@ const expectSameDocument = (node1: AstNode | undefined, node2: AstNode | undefin throw new AssertionError({ message: `node2 is undefined.` }); } - const document1 = getDocument(node1); - const document2 = getDocument(node2); + const document1 = AstUtils.getDocument(node1); + const document2 = AstUtils.getDocument(node2); expect(document1.uri.toString()).toStrictEqual(document2.uri.toString()); }; diff --git a/packages/safe-ds-lang/tests/language/typing/type checker/isSubOrSupertypeOf.test.ts b/packages/safe-ds-lang/tests/language/typing/type checker/isSubOrSupertypeOf.test.ts index ac2842f86..0d81b4a6f 100644 --- a/packages/safe-ds-lang/tests/language/typing/type checker/isSubOrSupertypeOf.test.ts +++ b/packages/safe-ds-lang/tests/language/typing/type checker/isSubOrSupertypeOf.test.ts @@ -1,4 +1,3 @@ -import { streamAllContents } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { describe, expect, it } from 'vitest'; import { @@ -33,6 +32,7 @@ import { UnknownType, } from '../../../../src/language/typing/model.js'; import { getNodeOfType } from '../../../helpers/nodeFinder.js'; +import { AstUtils } from 'langium'; const services = (await createSafeDsServicesWithBuiltins(NodeFileSystem)).SafeDs; const coreTypes = services.types.CoreTypes; @@ -96,7 +96,7 @@ const basic = async (): Promise => { const enumType1 = typeComputer.computeType(enum1) as EnumType; const enumType2 = typeComputer.computeType(enum2) as EnumType; - const enumVariants = streamAllContents(module).filter(isSdsEnumVariant).toArray(); + const enumVariants = AstUtils.streamAllContents(module).filter(isSdsEnumVariant).toArray(); const enumVariant1 = enumVariants[0]; const enumVariant2 = enumVariants[1]; const enumVariantType1 = typeComputer.computeType(enumVariant1) as EnumVariantType; diff --git a/packages/safe-ds-lang/tests/language/validation/safe-ds-validator.test.ts b/packages/safe-ds-lang/tests/language/validation/safe-ds-validator.test.ts index 05738a830..33c1980c1 100644 --- a/packages/safe-ds-lang/tests/language/validation/safe-ds-validator.test.ts +++ b/packages/safe-ds-lang/tests/language/validation/safe-ds-validator.test.ts @@ -9,6 +9,7 @@ import { loadDocuments } from '../../helpers/testResources.js'; import { createValidationTests, ExpectedIssue } from './creator.js'; const services = (await createSafeDsServicesWithBuiltins(NodeFileSystem)).SafeDs; +const langiumDocuments = services.shared.workspace.LangiumDocuments; describe('validation', async () => { it.each(await createValidationTests())('$testName', async (test) => { @@ -63,8 +64,8 @@ describe('validation', async () => { * @param expectedIssue The expected issue. */ const getMatchingActualIssues = (expectedIssue: ExpectedIssue): Diagnostic[] => { - const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument(expectedIssue.uri); - let result = document.diagnostics ?? []; + const document = langiumDocuments.getDocument(expectedIssue.uri); + let result = document?.diagnostics ?? []; // Filter by severity switch (expectedIssue.severity) { diff --git a/packages/safe-ds-vscode/src/extension/mainClient.ts b/packages/safe-ds-vscode/src/extension/mainClient.ts index 876c2251a..801985e29 100644 --- a/packages/safe-ds-vscode/src/extension/mainClient.ts +++ b/packages/safe-ds-vscode/src/extension/mainClient.ts @@ -316,8 +316,10 @@ const runPipelineFile = async function (filePath: vscode.Uri | undefined, pipeli }); const workspaceSdsFiles = await vscode.workspace.findFiles('**/*.{sdspipe,sdsstub,sdstest}'); // Load all documents - const unvalidatedSdsDocuments = workspaceSdsFiles.map((newDocumentUri) => - services.shared.workspace.LangiumDocuments.getOrCreateDocument(newDocumentUri), + const unvalidatedSdsDocuments = await Promise.all( + workspaceSdsFiles.map((newDocumentUri) => + services.shared.workspace.LangiumDocuments.getOrCreateDocument(newDocumentUri), + ), ); // Validate them const validationErrorMessage = await validateDocuments(services, unvalidatedSdsDocuments); @@ -327,16 +329,17 @@ const runPipelineFile = async function (filePath: vscode.Uri | undefined, pipeli } // Run it printOutputMessage(`Launching Pipeline (${pipelineId}): ${pipelinePath}`); + let mainDocument; if (!services.shared.workspace.LangiumDocuments.hasDocument(pipelinePath)) { - mainDocument = services.shared.workspace.LangiumDocuments.getOrCreateDocument(pipelinePath); + mainDocument = await services.shared.workspace.LangiumDocuments.getOrCreateDocument(pipelinePath); const mainDocumentValidationErrorMessage = await validateDocuments(services, [mainDocument]); if (mainDocumentValidationErrorMessage) { vscode.window.showErrorMessage(mainDocumentValidationErrorMessage); return; } } else { - mainDocument = services.shared.workspace.LangiumDocuments.getOrCreateDocument(pipelinePath); + mainDocument = await services.shared.workspace.LangiumDocuments.getOrCreateDocument(pipelinePath); } await services.runtime.Runner.executePipeline(mainDocument, pipelineId); };