From 78706d254e9978783daa0e9069b069d51c18e9ff Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Wed, 22 May 2024 10:59:34 +0200 Subject: [PATCH 01/19] Introduce `publish` keyword and adapt scoping --- .../test-extension/TestBlockTypes.jv | 6 +-- .../test/test-extension/TestBlockTypes.jv | 2 +- .../test/test-extension/TestBlockTypes.jv | 8 ++-- .../test/test-extension/TestBlockTypes.jv | 16 +++---- .../test-extension/TestBlockTypes.jv | 6 +-- .../test-extension/TestBlockTypes.jv | 2 +- .../src/grammar/block-type.langium | 4 +- .../src/grammar/constraint.langium | 6 +-- .../src/grammar/io-type.langium | 2 +- libs/language-server/src/grammar/main.langium | 16 +++++++ .../src/grammar/transform.langium | 2 +- .../src/grammar/value-type.langium | 4 +- .../src/lib/builtin-library/stdlib.ts | 6 +-- .../src/lib/lsp/jayvee-scope-computation.ts | 40 +++++++--------- .../src/stdlib/CSVExtractor.jv | 2 +- .../src/stdlib/CSVFileInterpreter.jv | 2 +- .../builtin-block-types/ArchiveInterpreter.jv | 2 +- .../builtin-block-types/CellRangeSelector.jv | 2 +- .../stdlib/builtin-block-types/CellWriter.jv | 2 +- .../builtin-block-types/ColumnDeleter.jv | 2 +- .../builtin-block-types/CsvInterpreter.jv | 2 +- .../stdlib/builtin-block-types/FilePicker.jv | 2 +- .../builtin-block-types/GtfsRtInterpreter.jv | 2 +- .../builtin-block-types/HttpExtractor.jv | 2 +- .../builtin-block-types/LocalFileExtractor.jv | 2 +- .../builtin-block-types/PostgresLoader.jv | 2 +- .../stdlib/builtin-block-types/RowDeleter.jv | 2 +- .../stdlib/builtin-block-types/SheetPicker.jv | 2 +- .../builtin-block-types/SqliteLoader.jv | 2 +- .../builtin-block-types/TableInterpreter.jv | 2 +- .../builtin-block-types/TableTransformer.jv | 2 +- .../TextFileInterpreter.jv | 2 +- .../builtin-block-types/TextLineDeleter.jv | 2 +- .../builtin-block-types/TextRangeSelector.jv | 2 +- .../builtin-block-types/XlsInterpreter.jv | 2 +- .../AllowlistConstraint.jv | 4 +- .../DenylistConstraint.jv | 4 +- .../LengthConstraint.jv | 6 +-- .../RangeConstraint.jv | 6 +-- .../RegexConstraint.jv | 4 +- .../domain/mobility/GTFSAgencyInterpreter.jv | 2 +- .../mobility/GTFSCalendarDatesInterpreter.jv | 2 +- .../mobility/GTFSCalendarInterpreter.jv | 2 +- .../stdlib/domain/mobility/GTFSExtractor.jv | 2 +- .../mobility/GTFSFareAttributesInterpreter.jv | 2 +- .../mobility/GTFSFareRulesInterpreter.jv | 2 +- .../mobility/GTFSFrequenciesInterpreter.jv | 2 +- .../domain/mobility/GTFSRoutesInterpreter.jv | 2 +- .../domain/mobility/GTFSShapesInterpreter.jv | 2 +- .../mobility/GTFSStopTimesInterpreter.jv | 2 +- .../domain/mobility/GTFSStopsInterpreter.jv | 2 +- .../domain/mobility/GTFSTripsInterpreter.jv | 2 +- .../stdlib/domain/mobility/GTFSValueTypes.jv | 46 +++++++++---------- libs/language-server/src/stdlib/percent.jv | 4 +- .../import-published-references/publishing.jv | 2 +- 55 files changed, 135 insertions(+), 127 deletions(-) diff --git a/libs/execution/test/assets/transform-executor/test-extension/TestBlockTypes.jv b/libs/execution/test/assets/transform-executor/test-extension/TestBlockTypes.jv index bf5bb4357..593135475 100644 --- a/libs/execution/test/assets/transform-executor/test-extension/TestBlockTypes.jv +++ b/libs/execution/test/assets/transform-executor/test-extension/TestBlockTypes.jv @@ -2,17 +2,17 @@ // // SPDX-License-Identifier: AGPL-3.0-only -builtin blocktype TestFileExtractor { +publish builtin blocktype TestFileExtractor { input inPort oftype None; output outPort oftype File; } -builtin blocktype TestFileLoader { +publish builtin blocktype TestFileLoader { input inPort oftype File; output outPort oftype None; } -builtin blocktype TestTableLoader { +publish builtin blocktype TestTableLoader { input inPort oftype Table; output outPort oftype None; } diff --git a/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv b/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv index e050777aa..89f6fef78 100644 --- a/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv +++ b/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: AGPL-3.0-only -builtin blocktype TestTableExtractor { +publish builtin blocktype TestTableExtractor { input inPort oftype None; output outPort oftype Table; } diff --git a/libs/extensions/std/exec/test/test-extension/TestBlockTypes.jv b/libs/extensions/std/exec/test/test-extension/TestBlockTypes.jv index 3abfdb4e2..39921f8fe 100644 --- a/libs/extensions/std/exec/test/test-extension/TestBlockTypes.jv +++ b/libs/extensions/std/exec/test/test-extension/TestBlockTypes.jv @@ -2,22 +2,22 @@ // // SPDX-License-Identifier: AGPL-3.0-only -builtin blocktype TestFileExtractor { +publish builtin blocktype TestFileExtractor { input inPort oftype None; output outPort oftype File; } -builtin blocktype TestFileLoader { +publish builtin blocktype TestFileLoader { input inPort oftype File; output outPort oftype None; } -builtin blocktype TestSheetLoader { +publish builtin blocktype TestSheetLoader { input inPort oftype Sheet; output outPort oftype None; } -builtin blocktype TestTextFileLoader { +publish builtin blocktype TestTextFileLoader { input inPort oftype TextFile; output outPort oftype None; } diff --git a/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv b/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv index ba08556c4..c8fcba9ee 100644 --- a/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv +++ b/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv @@ -2,42 +2,42 @@ // // SPDX-License-Identifier: AGPL-3.0-only -builtin blocktype TestSheetExtractor { +publish builtin blocktype TestSheetExtractor { input inPort oftype None; output outPort oftype Sheet; } -builtin blocktype TestSheetLoader { +publish builtin blocktype TestSheetLoader { input inPort oftype Sheet; output outPort oftype None; } -builtin blocktype TestTextFileExtractor { +publish builtin blocktype TestTextFileExtractor { input inPort oftype None; output outPort oftype TextFile; } -builtin blocktype TestFileExtractor { +publish builtin blocktype TestFileExtractor { input inPort oftype None; output outPort oftype File; } -builtin blocktype TestWorkbookExtractor { +publish builtin blocktype TestWorkbookExtractor { input inPort oftype None; output outPort oftype Workbook; } -builtin blocktype TestWorkbookLoader { +publish builtin blocktype TestWorkbookLoader { input inPort oftype Workbook; output outPort oftype None; } -builtin blocktype TestTableExtractor { +publish builtin blocktype TestTableExtractor { input inPort oftype None; output outPort oftype Table; } -builtin blocktype TestTableLoader { +publish builtin blocktype TestTableLoader { input inPort oftype Table; output outPort oftype None; } diff --git a/libs/interpreter-lib/test/assets/parsing-util/test-extension/TestBlockTypes.jv b/libs/interpreter-lib/test/assets/parsing-util/test-extension/TestBlockTypes.jv index bf5bb4357..593135475 100644 --- a/libs/interpreter-lib/test/assets/parsing-util/test-extension/TestBlockTypes.jv +++ b/libs/interpreter-lib/test/assets/parsing-util/test-extension/TestBlockTypes.jv @@ -2,17 +2,17 @@ // // SPDX-License-Identifier: AGPL-3.0-only -builtin blocktype TestFileExtractor { +publish builtin blocktype TestFileExtractor { input inPort oftype None; output outPort oftype File; } -builtin blocktype TestFileLoader { +publish builtin blocktype TestFileLoader { input inPort oftype File; output outPort oftype None; } -builtin blocktype TestTableLoader { +publish builtin blocktype TestTableLoader { input inPort oftype Table; output outPort oftype None; } diff --git a/libs/interpreter-lib/test/assets/runtime-parameter-literal/test-extension/TestBlockTypes.jv b/libs/interpreter-lib/test/assets/runtime-parameter-literal/test-extension/TestBlockTypes.jv index 75e51a92f..a70b379a6 100644 --- a/libs/interpreter-lib/test/assets/runtime-parameter-literal/test-extension/TestBlockTypes.jv +++ b/libs/interpreter-lib/test/assets/runtime-parameter-literal/test-extension/TestBlockTypes.jv @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: AGPL-3.0-only -builtin blocktype TestProperty { +publish builtin blocktype TestProperty { input inPort oftype File; output outPort oftype Table; diff --git a/libs/language-server/src/grammar/block-type.langium b/libs/language-server/src/grammar/block-type.langium index 11281327b..15d1a311c 100644 --- a/libs/language-server/src/grammar/block-type.langium +++ b/libs/language-server/src/grammar/block-type.langium @@ -14,12 +14,12 @@ ReferenceableBlockTypeDefinition: BuiltinBlockTypeDefinition | CompositeBlockTypeDefinition; BuiltinBlockTypeDefinition: - 'builtin' 'blocktype' name=ID '{' + (isPublished?='publish')? 'builtin' 'blocktype' name=ID '{' (inputs+=BlockTypeInput | outputs+=BlockTypeOutput | properties+=BlockTypeProperty)* '}'; CompositeBlockTypeDefinition: - 'composite' 'blocktype' name=ID '{' + (isPublished?='publish')? 'composite' 'blocktype' name=ID '{' ( inputs+=BlockTypeInput | outputs+=BlockTypeOutput diff --git a/libs/language-server/src/grammar/constraint.langium b/libs/language-server/src/grammar/constraint.langium index 375c2f560..497c35381 100644 --- a/libs/language-server/src/grammar/constraint.langium +++ b/libs/language-server/src/grammar/constraint.langium @@ -11,13 +11,13 @@ ConstraintDefinition: TypedConstraintDefinition | ExpressionConstraintDefinition; TypedConstraintDefinition: - 'constraint' name=ID 'oftype' type=[BuiltinConstrainttypeDefinition] body=PropertyBody; + (isPublished?='publish')? 'constraint' name=ID 'oftype' type=[BuiltinConstrainttypeDefinition] body=PropertyBody; ExpressionConstraintDefinition: - 'constraint' name=ID 'on' valueType=ValueTypeReference ':' expression=Expression ';'; + (isPublished?='publish')? 'constraint' name=ID 'on' valueType=ValueTypeReference ':' expression=Expression ';'; BuiltinConstrainttypeDefinition: - 'builtin' 'constrainttype' name=ID 'on' valueType=ValueTypeReference '{' + (isPublished?='publish')? 'builtin' 'constrainttype' name=ID 'on' valueType=ValueTypeReference '{' (properties+=ConstrainttypeProperty)* '}'; diff --git a/libs/language-server/src/grammar/io-type.langium b/libs/language-server/src/grammar/io-type.langium index 5b962709f..41542532e 100644 --- a/libs/language-server/src/grammar/io-type.langium +++ b/libs/language-server/src/grammar/io-type.langium @@ -6,4 +6,4 @@ import './expression' import './terminal' IotypeDefinition: - 'builtin' 'iotype' name=ID ';'; + (isPublished?='publish')? 'builtin' 'iotype' name=ID ';'; diff --git a/libs/language-server/src/grammar/main.langium b/libs/language-server/src/grammar/main.langium index eb58f07ff..6617443b8 100644 --- a/libs/language-server/src/grammar/main.langium +++ b/libs/language-server/src/grammar/main.langium @@ -16,6 +16,7 @@ import './io-type' entry JayveeModel: ( imports+=ImportDefinition + | exports+=ExportDefinition | pipelines+=PipelineDefinition | valueTypes+=(CustomValuetypeDefinition | BuiltinValuetypeDefinition) | constraints+=ConstraintDefinition @@ -25,6 +26,21 @@ entry JayveeModel: | iotypes+=IotypeDefinition )*; +// When adding an element here, make sure the element has the following prependix in its rule +// (isPublished?='publish')? +// TypeScript lets us then infer that there is a field `isPublished` after using the type guard `isExportableElement`. +ExportableElement: + // Must ref rules where isPublished is defined + // So don't use aggregates like ReferenceableBlockTypeDefinition + // Otherwise it doesn't work to resolve the refenerces + (CustomValuetypeDefinition | BuiltinValuetypeDefinition) + | (TypedConstraintDefinition | ExpressionConstraintDefinition | BuiltinConstrainttypeDefinition) + | TransformDefinition + | (BuiltinBlockTypeDefinition | CompositeBlockTypeDefinition) + | IotypeDefinition; + +ExportDefinition: + 'publish' element=[ExportableElement] ';'; ImportDefinition: 'use' '*' 'from' path=STRING ';'; diff --git a/libs/language-server/src/grammar/transform.langium b/libs/language-server/src/grammar/transform.langium index d58f5a322..b8b2e5f11 100644 --- a/libs/language-server/src/grammar/transform.langium +++ b/libs/language-server/src/grammar/transform.langium @@ -7,7 +7,7 @@ import 'value-type' import 'expression' TransformDefinition: - 'transform' name=ID body=TransformBody; + (isPublished?='publish')? 'transform' name=ID body=TransformBody; TransformBody: '{' (ports+=TransformPortDefinition)* (outputAssignments+=TransformOutputAssignment)* '}'; diff --git a/libs/language-server/src/grammar/value-type.langium b/libs/language-server/src/grammar/value-type.langium index f861b1c79..cb9faad2a 100644 --- a/libs/language-server/src/grammar/value-type.langium +++ b/libs/language-server/src/grammar/value-type.langium @@ -6,12 +6,12 @@ import './expression' import './terminal' BuiltinValuetypeDefinition infers ValuetypeDefinition: - isBuiltin?='builtin' 'valuetype' name=ID + (isPublished?='publish')? isBuiltin?='builtin' 'valuetype' name=ID (genericDefinition=ValuetypeGenericsDefinition)? ';'; CustomValuetypeDefinition infers ValuetypeDefinition: - 'valuetype' name=ID + (isPublished?='publish')? 'valuetype' name=ID (genericDefinition=ValuetypeGenericsDefinition)? 'oftype' type=ValueTypeReference '{' 'constraints' ':' constraints=CollectionLiteral ';' diff --git a/libs/language-server/src/lib/builtin-library/stdlib.ts b/libs/language-server/src/lib/builtin-library/stdlib.ts index ccbfd14c8..375259d01 100644 --- a/libs/language-server/src/lib/builtin-library/stdlib.ts +++ b/libs/language-server/src/lib/builtin-library/stdlib.ts @@ -12,7 +12,7 @@ export function getBuiltinValuetypesLib() { .map(parseBuiltinValuetypeToJayvee); const collectionValuetype = `${parseAsComment('For internal use only.')} -builtin valuetype Collection;`; +publish builtin valuetype Collection;`; return { 'builtin:///stdlib/builtin-value-types.jv': [ @@ -24,7 +24,7 @@ builtin valuetype Collection;`; export const IOtypesLib = { 'builtin:///stdlib/io-types.jv': Object.values(IOType) - .map((iotype) => `builtin iotype ${iotype};`) + .map((iotype) => `publish builtin iotype ${iotype};`) .join('\n\n'), }; @@ -46,7 +46,7 @@ function parseBuiltinValuetypeToJayvee(valueType: PrimitiveValueType): string { if (!valueType.isReferenceableByUser()) { lines.push(parseAsComment('For internal use only.')); } - lines.push(`builtin valuetype ${valueType.getName()};`); + lines.push(`publish builtin valuetype ${valueType.getName()};`); return lines.join('\n'); } diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-computation.ts b/libs/language-server/src/lib/lsp/jayvee-scope-computation.ts index 0acce4f77..03ebff7e4 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-computation.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-computation.ts @@ -10,14 +10,7 @@ import { type LangiumDocument, } from 'langium'; -import { - isBuiltinConstrainttypeDefinition, - isConstraintDefinition, - isIotypeDefinition, - isReferenceableBlockTypeDefinition, - isTransformDefinition, - isValuetypeDefinition, -} from '../ast'; +import { isExportDefinition, isExportableElement } from '../ast'; export class JayveeScopeComputation extends DefaultScopeComputation { constructor(services: LangiumCoreServices) { @@ -29,24 +22,23 @@ export class JayveeScopeComputation extends DefaultScopeComputation { exports: AstNodeDescription[], document: LangiumDocument, ): void { - // export the exportable top-level elements - if (!this.isExportable(node)) { - return; + const isExportingElementDefinition = + isExportableElement(node) && node.isPublished; + + if (isExportingElementDefinition) { + return super.exportNode(node, exports, document); } - super.exportNode(node, exports, document); - } + const isDelayedExportDefinition = isExportDefinition(node); + if (isDelayedExportDefinition) { + const exportedNode = node.element.ref; + if (exportedNode === undefined) { + return; + } + + return super.exportNode(exportedNode, exports, document); + } - isExportable(node: AstNode) { - // pipelines are not exported - - return ( - isValuetypeDefinition(node) || - isConstraintDefinition(node) || - isTransformDefinition(node) || - isReferenceableBlockTypeDefinition(node) || - isBuiltinConstrainttypeDefinition(node) || - isIotypeDefinition(node) - ); + // We only export elements that are explicitly published! } } diff --git a/libs/language-server/src/stdlib/CSVExtractor.jv b/libs/language-server/src/stdlib/CSVExtractor.jv index 066a1bbff..c4b1557f6 100644 --- a/libs/language-server/src/stdlib/CSVExtractor.jv +++ b/libs/language-server/src/stdlib/CSVExtractor.jv @@ -5,7 +5,7 @@ /** * A CSVExtractor extracts a file from a given URL and interprets it as CSV */ -composite blocktype CSVExtractor { +publish composite blocktype CSVExtractor { property url oftype text; property delimiter oftype text: ','; property enclosing oftype text: ''; diff --git a/libs/language-server/src/stdlib/CSVFileInterpreter.jv b/libs/language-server/src/stdlib/CSVFileInterpreter.jv index 4bedc3ef6..4d7521b98 100644 --- a/libs/language-server/src/stdlib/CSVFileInterpreter.jv +++ b/libs/language-server/src/stdlib/CSVFileInterpreter.jv @@ -5,7 +5,7 @@ /** * A CSVFileInterpreter interprets a file as CSV */ -composite blocktype CSVFileInterpreter { +publish composite blocktype CSVFileInterpreter { property delimiter oftype text: ','; property enclosing oftype text: ''; property enclosingEscape oftype text: ''; diff --git a/libs/language-server/src/stdlib/builtin-block-types/ArchiveInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/ArchiveInterpreter.jv index d490c5a76..f7e1ba78d 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/ArchiveInterpreter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/ArchiveInterpreter.jv @@ -10,7 +10,7 @@ * archiveType: "zip"; * } */ -builtin blocktype ArchiveInterpreter { +publish builtin blocktype ArchiveInterpreter { input default oftype File; output default oftype FileSystem; diff --git a/libs/language-server/src/stdlib/builtin-block-types/CellRangeSelector.jv b/libs/language-server/src/stdlib/builtin-block-types/CellRangeSelector.jv index 87182fa4c..04cd78a14 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/CellRangeSelector.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/CellRangeSelector.jv @@ -10,7 +10,7 @@ * select: range A1:E*; * } */ -builtin blocktype CellRangeSelector { +publish builtin blocktype CellRangeSelector { input default oftype Sheet; output default oftype Sheet; diff --git a/libs/language-server/src/stdlib/builtin-block-types/CellWriter.jv b/libs/language-server/src/stdlib/builtin-block-types/CellWriter.jv index 6ff64a4e1..f2c26101e 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/CellWriter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/CellWriter.jv @@ -17,7 +17,7 @@ * write: ["Name", "Age"]; * } */ -builtin blocktype CellWriter { +publish builtin blocktype CellWriter { input default oftype Sheet; output default oftype Sheet; diff --git a/libs/language-server/src/stdlib/builtin-block-types/ColumnDeleter.jv b/libs/language-server/src/stdlib/builtin-block-types/ColumnDeleter.jv index 1c3f18ec6..a4acd7d97 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/ColumnDeleter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/ColumnDeleter.jv @@ -10,7 +10,7 @@ * delete: [column B]; * } */ -builtin blocktype ColumnDeleter { +publish builtin blocktype ColumnDeleter { input default oftype Sheet; output default oftype Sheet; diff --git a/libs/language-server/src/stdlib/builtin-block-types/CsvInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/CsvInterpreter.jv index fb654e4c3..911db64a3 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/CsvInterpreter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/CsvInterpreter.jv @@ -10,7 +10,7 @@ * delimiter: ";"; * } */ -builtin blocktype CSVInterpreter { +publish builtin blocktype CSVInterpreter { input default oftype TextFile; output default oftype Sheet; diff --git a/libs/language-server/src/stdlib/builtin-block-types/FilePicker.jv b/libs/language-server/src/stdlib/builtin-block-types/FilePicker.jv index 0b0cb8093..bd7f62217 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/FilePicker.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/FilePicker.jv @@ -10,7 +10,7 @@ * path: "./agency.txt"; * } */ -builtin blocktype FilePicker { +publish builtin blocktype FilePicker { input default oftype FileSystem; output default oftype File; diff --git a/libs/language-server/src/stdlib/builtin-block-types/GtfsRtInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/GtfsRtInterpreter.jv index 9caf39017..df0f4de78 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/GtfsRtInterpreter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/GtfsRtInterpreter.jv @@ -10,7 +10,7 @@ * entity: "trip_update"; * } */ -builtin blocktype GtfsRTInterpreter { +publish builtin blocktype GtfsRTInterpreter { input default oftype File; output default oftype Sheet; diff --git a/libs/language-server/src/stdlib/builtin-block-types/HttpExtractor.jv b/libs/language-server/src/stdlib/builtin-block-types/HttpExtractor.jv index 014328c96..a85f7bcba 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/HttpExtractor.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/HttpExtractor.jv @@ -10,7 +10,7 @@ * url: "tinyurl.com/4ub9spwz"; * } */ -builtin blocktype HttpExtractor { +publish builtin blocktype HttpExtractor { input default oftype None; output default oftype File; diff --git a/libs/language-server/src/stdlib/builtin-block-types/LocalFileExtractor.jv b/libs/language-server/src/stdlib/builtin-block-types/LocalFileExtractor.jv index a4f7a3c5b..0221e6560 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/LocalFileExtractor.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/LocalFileExtractor.jv @@ -10,7 +10,7 @@ * filePath: "cars.csv"; * } */ -builtin blocktype LocalFileExtractor { +publish builtin blocktype LocalFileExtractor { input default oftype None; output default oftype File; diff --git a/libs/language-server/src/stdlib/builtin-block-types/PostgresLoader.jv b/libs/language-server/src/stdlib/builtin-block-types/PostgresLoader.jv index 2b2a824ab..1d37d8ded 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/PostgresLoader.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/PostgresLoader.jv @@ -15,7 +15,7 @@ * table: "Cars"; * } */ -builtin blocktype PostgresLoader { +publish builtin blocktype PostgresLoader { input default oftype Table; output default oftype None; diff --git a/libs/language-server/src/stdlib/builtin-block-types/RowDeleter.jv b/libs/language-server/src/stdlib/builtin-block-types/RowDeleter.jv index 9835a2326..79211a05d 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/RowDeleter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/RowDeleter.jv @@ -10,7 +10,7 @@ * delete: [row 2]; * } */ -builtin blocktype RowDeleter { +publish builtin blocktype RowDeleter { input default oftype Sheet; output default oftype Sheet; diff --git a/libs/language-server/src/stdlib/builtin-block-types/SheetPicker.jv b/libs/language-server/src/stdlib/builtin-block-types/SheetPicker.jv index e9591c888..79a4e696c 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/SheetPicker.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/SheetPicker.jv @@ -10,7 +10,7 @@ * sheetName: "AgencyNames"; * } */ -builtin blocktype SheetPicker { +publish builtin blocktype SheetPicker { input default oftype Workbook; output default oftype Sheet; diff --git a/libs/language-server/src/stdlib/builtin-block-types/SqliteLoader.jv b/libs/language-server/src/stdlib/builtin-block-types/SqliteLoader.jv index 3a69197e1..de3bacfc1 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/SqliteLoader.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/SqliteLoader.jv @@ -11,7 +11,7 @@ * file: "./cars.db"; * } */ -builtin blocktype SQLiteLoader { +publish builtin blocktype SQLiteLoader { input default oftype Table; output default oftype None; diff --git a/libs/language-server/src/stdlib/builtin-block-types/TableInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/TableInterpreter.jv index b2e66012d..866eacecd 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/TableInterpreter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/TableInterpreter.jv @@ -25,7 +25,7 @@ * ]; * } */ -builtin blocktype TableInterpreter { +publish builtin blocktype TableInterpreter { input default oftype Sheet; output default oftype Table; diff --git a/libs/language-server/src/stdlib/builtin-block-types/TableTransformer.jv b/libs/language-server/src/stdlib/builtin-block-types/TableTransformer.jv index 6f45a933e..5c3406a57 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/TableTransformer.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/TableTransformer.jv @@ -35,7 +35,7 @@ * uses: CelsiusToFahrenheit; * } */ -builtin blocktype TableTransformer { +publish builtin blocktype TableTransformer { input default oftype Table; output default oftype Table; diff --git a/libs/language-server/src/stdlib/builtin-block-types/TextFileInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/TextFileInterpreter.jv index 32e1a46c7..2b04556ac 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/TextFileInterpreter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/TextFileInterpreter.jv @@ -5,7 +5,7 @@ /** * Interprets a `File` as a `TextFile`. */ -builtin blocktype TextFileInterpreter { +publish builtin blocktype TextFileInterpreter { input default oftype File; output default oftype TextFile; diff --git a/libs/language-server/src/stdlib/builtin-block-types/TextLineDeleter.jv b/libs/language-server/src/stdlib/builtin-block-types/TextLineDeleter.jv index 226996221..30309d332 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/TextLineDeleter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/TextLineDeleter.jv @@ -5,7 +5,7 @@ /** * Deletes individual lines from a `TextFile`. */ -builtin blocktype TextLineDeleter { +publish builtin blocktype TextLineDeleter { input default oftype TextFile; output default oftype TextFile; diff --git a/libs/language-server/src/stdlib/builtin-block-types/TextRangeSelector.jv b/libs/language-server/src/stdlib/builtin-block-types/TextRangeSelector.jv index df0cda4a2..05ca216e5 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/TextRangeSelector.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/TextRangeSelector.jv @@ -5,7 +5,7 @@ /** * Selects a range of lines from a `TextFile`. */ -builtin blocktype TextRangeSelector { +publish builtin blocktype TextRangeSelector { input default oftype TextFile; output default oftype TextFile; diff --git a/libs/language-server/src/stdlib/builtin-block-types/XlsInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/XlsInterpreter.jv index 40c3fcfcc..0d2dd4f7a 100644 --- a/libs/language-server/src/stdlib/builtin-block-types/XlsInterpreter.jv +++ b/libs/language-server/src/stdlib/builtin-block-types/XlsInterpreter.jv @@ -8,7 +8,7 @@ * @example Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. * block AgencyXLSXInterpreter oftype XLSXInterpreter { } */ -builtin blocktype XLSXInterpreter { +publish builtin blocktype XLSXInterpreter { input default oftype File; output default oftype Workbook; } \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/AllowlistConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/AllowlistConstraint.jv index a6c25b917..167b35333 100644 --- a/libs/language-server/src/stdlib/builtin-constrainttypes/AllowlistConstraint.jv +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/AllowlistConstraint.jv @@ -6,10 +6,10 @@ * Limits the values to a defined a set of allowed values. Only values in the list are valid. * * @example Only allows the common abbreviations for millisecond, second, minute, etc.. -* constraint TimeUnitString oftype AllowlistConstraint { +* publish constraint TimeUnitString oftype AllowlistConstraint { * allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; * } */ -builtin constrainttype AllowlistConstraint on text { +publish builtin constrainttype AllowlistConstraint on text { property allowlist oftype Collection ; } \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/DenylistConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/DenylistConstraint.jv index fc554b2f7..4f0311a89 100644 --- a/libs/language-server/src/stdlib/builtin-constrainttypes/DenylistConstraint.jv +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/DenylistConstraint.jv @@ -6,10 +6,10 @@ * Defines a set of forbidden values. All values in the list are considered invalid. * * @example Denies all primary colors. -* constraint NoPrimaryColors oftype DenylistConstraint { +* publish constraint NoPrimaryColors oftype DenylistConstraint { * denylist: ["red", "blue", "yellow"]; * } */ -builtin constrainttype DenylistConstraint on text { +publish builtin constrainttype DenylistConstraint on text { property denylist oftype Collection ; } \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/LengthConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/LengthConstraint.jv index a51f69aa4..7fade58eb 100644 --- a/libs/language-server/src/stdlib/builtin-constrainttypes/LengthConstraint.jv +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/LengthConstraint.jv @@ -6,13 +6,13 @@ * Limits the length of a string with an upper and/or lower boundary. * Only values with a length within the given range are valid. * -* @example A text constraint with 0 to 20 characters. -* constraint ShortAnswerConstraint oftype LengthConstraint { +* @example A text publish constraint with 0 to 20 characters. +* publish constraint ShortAnswerConstraint oftype LengthConstraint { * minLength: 0; * maxLength: 20; * } */ -builtin constrainttype LengthConstraint on text { +publish builtin constrainttype LengthConstraint on text { /** * Inclusive minimum of the valid text length. */ diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/RangeConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/RangeConstraint.jv index fa74774a4..03d4184f4 100644 --- a/libs/language-server/src/stdlib/builtin-constrainttypes/RangeConstraint.jv +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/RangeConstraint.jv @@ -6,19 +6,19 @@ * Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. * * @example A scale between (and including) 1 and 100. -* constraint HundredScale oftype RangeConstraint { +* publish constraint HundredScale oftype RangeConstraint { * lowerBound: 1; * upperBound: 100; * } * * @example A scale for numbers strictly larger than 1 and less or equal to 100. -* constraint HundredScale oftype RangeConstraint { +* publish constraint HundredScale oftype RangeConstraint { * lowerBound: 1; * lowerBoundInclusive: false; * upperBound: 100; * } */ -builtin constrainttype RangeConstraint on decimal { +publish builtin constrainttype RangeConstraint on decimal { /** * Lower bound for the valid value range. * @see lowerBoundInclusive whether inclusive or exclusive. diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/RegexConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/RegexConstraint.jv index 17ef42702..a577eeca0 100644 --- a/libs/language-server/src/stdlib/builtin-constrainttypes/RegexConstraint.jv +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/RegexConstraint.jv @@ -7,10 +7,10 @@ * Only values that comply with the regex are considered valid. * * @example Text that complies with the IPv4 address format. -* constraint IPv4Format oftype RegexConstraint { +* publish constraint IPv4Format oftype RegexConstraint { * regex: /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/; * } */ -builtin constrainttype RegexConstraint on text { +publish builtin constrainttype RegexConstraint on text { property regex oftype Regex ; } \ No newline at end of file diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSAgencyInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSAgencyInterpreter.jv index 61edadc7e..afafbe0b3 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSAgencyInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSAgencyInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSAgencyInterpreter interprets a agency.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#agencytxt */ -composite blocktype GTFSAgencyInterpreter { +publish composite blocktype GTFSAgencyInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarDatesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarDatesInterpreter.jv index b08b0f34f..2e25dcb8d 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarDatesInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarDatesInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSCalendarDatesInterpreter interprets a calendar_dates.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#calendar_datestxt */ -composite blocktype GTFSCalendarDatesInterpreter { +publish composite blocktype GTFSCalendarDatesInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarInterpreter.jv index be8b32a7c..a6176a102 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSCalendarInterpreter interprets a calendar.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#calendartxt */ -composite blocktype GTFSCalendarInterpreter { +publish composite blocktype GTFSCalendarInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSExtractor.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSExtractor.jv index d5699c36c..7dd76bd36 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSExtractor.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSExtractor.jv @@ -5,7 +5,7 @@ /** * A GTFSExtractor extracts a file from a given URL, interprets it as ZIP and extracts it */ -composite blocktype GTFSExtractor { +publish composite blocktype GTFSExtractor { property url oftype text; input inputPort oftype None; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSFareAttributesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSFareAttributesInterpreter.jv index 40c2cb25d..abfba17f9 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSFareAttributesInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSFareAttributesInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSFareAttributesInterpreter interprets a fare_attributes.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#fare_attributestxt */ -composite blocktype GTFSFareAttributesInterpreter { +publish composite blocktype GTFSFareAttributesInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSFareRulesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSFareRulesInterpreter.jv index 0cd5f9958..ce42b3d5f 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSFareRulesInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSFareRulesInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSFareRulesInterpreter interprets a fare_rules.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#fare_rulestxt */ -composite blocktype GTFSFareRulesInterpreter { +publish composite blocktype GTFSFareRulesInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSFrequenciesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSFrequenciesInterpreter.jv index 5d812090c..a851292bf 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSFrequenciesInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSFrequenciesInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSFrequenciesInterpreter interprets a frequencies.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#frequenciestxt */ -composite blocktype GTFSFrequenciesInterpreter { +publish composite blocktype GTFSFrequenciesInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSRoutesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSRoutesInterpreter.jv index 45a8e9ca5..36cc6227e 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSRoutesInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSRoutesInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSRoutesInterpreter interprets a routes.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#routestxt */ -composite blocktype GTFSRoutesInterpreter { +publish composite blocktype GTFSRoutesInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSShapesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSShapesInterpreter.jv index bf830d44f..de2382829 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSShapesInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSShapesInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSShapesInterpreter interprets a shapes.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#shapestxt */ -composite blocktype GTFSShapesInterpreter { +publish composite blocktype GTFSShapesInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSStopTimesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSStopTimesInterpreter.jv index 23862c2b7..289e61522 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSStopTimesInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSStopTimesInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSStopTimesInterpreter interprets a stop_times.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#stop_timestxt */ -composite blocktype GTFSStopTimesInterpreter { +publish composite blocktype GTFSStopTimesInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSStopsInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSStopsInterpreter.jv index 3d634491a..9e9ea8323 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSStopsInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSStopsInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSStopsInterpreter interprets a stops.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#stopstxt */ -composite blocktype GTFSStopsInterpreter { +publish composite blocktype GTFSStopsInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSTripsInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSTripsInterpreter.jv index 163e5dcf2..11998662b 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSTripsInterpreter.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSTripsInterpreter.jv @@ -6,7 +6,7 @@ * A GTFSTripsInterpreter interprets a trips.txt file from an extracted ZIP file according to the GTFS standard * See https://gtfs.org/schedule/reference/#tripstxt */ -composite blocktype GTFSTripsInterpreter { +publish composite blocktype GTFSTripsInterpreter { input inputPort oftype FileSystem; output outputPort oftype Table; diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSValueTypes.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSValueTypes.jv index 3fd76f4db..651afba57 100644 --- a/libs/language-server/src/stdlib/domain/mobility/GTFSValueTypes.jv +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSValueTypes.jv @@ -10,8 +10,8 @@ * Color - A color encoded as a six-digit hexadecimal number. Refer to https://htmlcolorcodes.com to generate a valid value (the leading "#" must not be included). * Example: FFFFFF for white, 000000 for black or 0039A6 for the A,C,E lines in NYMTA. */ -constraint GTFSColorConstraint on text: value matches /^[A-F]{6}$/; -valuetype GTFSColor oftype text { +publish constraint GTFSColorConstraint on text: value matches /^[A-F]{6}$/; +publish valuetype GTFSColor oftype text { constraints: [GTFSColorConstraint]; } @@ -19,8 +19,8 @@ valuetype GTFSColor oftype text { * Date - Service day in the YYYYMMDD format. Since time within a service day may be above 24:00:00, a service day may contain information for the subsequent day(s). * Example: 20180913 for September 13th, 2018. */ -constraint DateYYYYMMDD on text: value matches /^[0-9]{4}[0-9]{2}[0-9]{2}$/; -valuetype GTFSDate oftype text { +publish constraint DateYYYYMMDD on text: value matches /^[0-9]{4}[0-9]{2}[0-9]{2}$/; +publish valuetype GTFSDate oftype text { constraints: [DateYYYYMMDD]; } @@ -28,8 +28,8 @@ valuetype GTFSDate oftype text { * Currency code - An ISO 4217 alphabetical currency code. For the list of current currency, refer to https://en.wikipedia.org/wiki/ISO_4217#Active_codes. * Example: CAD for Canadian dollars, EUR for euros or JPY for Japanese yen. */ -constraint CurrencyConstraint on text: value matches /^[A-Z]{3}$/; -valuetype GTFSCurrency oftype text { +publish constraint CurrencyConstraint on text: value matches /^[A-Z]{3}$/; +publish valuetype GTFSCurrency oftype text { constraints: [CurrencyConstraint]; } @@ -37,25 +37,25 @@ valuetype GTFSCurrency oftype text { * Time - Time in the HH:MM:SS format (H:MM:SS is also accepted). The time is measured from "noon minus 12h" of the service day (effectively midnight except for days on which daylight savings time changes occur). For times occurring after midnight on the service day, enter the time as a value greater than 24:00:00 in HH:MM:SS. * Example: 14:30:00 for 2:30PM or 25:35:00 for 1:35AM on the next day. */ -constraint TimeHHMMSS on text: value matches /^[0-9]{1,2}:{1}[0-9]{2}:{1}[0-9]{2}$/; -valuetype GTFSTime oftype text { +publish constraint TimeHHMMSS on text: value matches /^[0-9]{1,2}:{1}[0-9]{2}:{1}[0-9]{2}$/; +publish valuetype GTFSTime oftype text { constraints: [TimeHHMMSS]; } // Placeholders -constraint EnumTwo on integer: value in [0, 1]; -constraint EnumOneOrTwo on integer: value in [1, 2]; -constraint EnumThree on integer: value in [0, 1, 2]; +publish constraint EnumTwo on integer: value in [0, 1]; +publish constraint EnumOneOrTwo on integer: value in [1, 2]; +publish constraint EnumThree on integer: value in [0, 1, 2]; -valuetype GTFSEnumTwo oftype integer { +publish valuetype GTFSEnumTwo oftype integer { constraints: [EnumTwo]; } -valuetype GTFSEnumOneOrTwo oftype integer { +publish valuetype GTFSEnumOneOrTwo oftype integer { constraints: [EnumOneOrTwo]; } -valuetype GTFSEnumThree oftype integer { +publish valuetype GTFSEnumThree oftype integer { constraints: [EnumThree]; } @@ -65,8 +65,8 @@ valuetype GTFSEnumThree oftype integer { * Latitude - WGS84 latitude in decimal degrees. The value must be greater than or equal to -90.0 and less than or equal to 90.0. * Example: 41.890169 for the Colosseum in Rome. */ -constraint Latitude on decimal: value >= -90 and value <= 90; -valuetype GTFSLatitude oftype decimal { +publish constraint Latitude on decimal: value >= -90 and value <= 90; +publish valuetype GTFSLatitude oftype decimal { constraints: [Latitude]; } @@ -74,24 +74,24 @@ valuetype GTFSLatitude oftype decimal { * Longitude - WGS84 longitude in decimal degrees. The value must be greater than or equal to -180.0 and less than or equal to 180.0. * Example: 12.492269 for the Colosseum in Rome. */ -constraint Longitude on decimal: value >= -180 and value <= 180; -valuetype GTFSLongitude oftype decimal { +publish constraint Longitude on decimal: value >= -180 and value <= 180; +publish valuetype GTFSLongitude oftype decimal { constraints: [Longitude]; } -constraint NonNegativeNumber on decimal: value >= 0; -valuetype GTFSNonNegativeDecimal oftype decimal { +publish constraint NonNegativeNumber on decimal: value >= 0; +publish valuetype GTFSNonNegativeDecimal oftype decimal { constraints: [NonNegativeNumber]; } -valuetype GTFSNonNegativeInteger oftype integer { +publish valuetype GTFSNonNegativeInteger oftype integer { constraints: [NonNegativeNumber]; } /* * URL - A fully qualified URL that includes http:// or https://, and any special characters in the URL must be correctly escaped. See the following http://www.w3.org/Addressing/URL/4_URI_Recommentations.html for a description of how to create fully qualified URL values. */ -constraint URLIncludingSchema on text: value matches /^(http)s?(:\/\/)/; -valuetype GTFSUrl oftype text { +publish constraint URLIncludingSchema on text: value matches /^(http)s?(:\/\/)/; +publish valuetype GTFSUrl oftype text { constraints: [URLIncludingSchema]; } diff --git a/libs/language-server/src/stdlib/percent.jv b/libs/language-server/src/stdlib/percent.jv index a4ab856ee..864e9de1c 100644 --- a/libs/language-server/src/stdlib/percent.jv +++ b/libs/language-server/src/stdlib/percent.jv @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: AGPL-3.0-only -valuetype Percent oftype decimal { +publish valuetype Percent oftype decimal { constraints: [ZeroToHundredDecimal]; } -constraint ZeroToHundredDecimal on decimal: value >= 0 and value <= 100; \ No newline at end of file +publish constraint ZeroToHundredDecimal on decimal: value >= 0 and value <= 100; \ No newline at end of file diff --git a/libs/language-server/src/test/assets/import-published-references/publishing.jv b/libs/language-server/src/test/assets/import-published-references/publishing.jv index 8230e38db..884de6157 100644 --- a/libs/language-server/src/test/assets/import-published-references/publishing.jv +++ b/libs/language-server/src/test/assets/import-published-references/publishing.jv @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: AGPL-3.0-only -builtin blocktype PublishedBlockType { +publish builtin blocktype PublishedBlockType { input inPort oftype None; output outPort oftype None; } From ca8cee600583419241751aa1c2e01becae906720 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 17:27:09 +0200 Subject: [PATCH 02/19] Refactor scoping to only import exported elements --- libs/language-server/src/lib/jayvee-module.ts | 1 - .../src/lib/lsp/import-reference.spec.ts | 4 + libs/language-server/src/lib/lsp/index.ts | 1 - .../src/lib/lsp/jayvee-scope-computation.ts | 44 --------- .../src/lib/lsp/jayvee-scope-provider.ts | 95 ++++++++++++++++--- ...ence-to-element-transitive-no-republish.jv | 9 ++ .../not-publishing-transitive.jv | 7 ++ .../publishing-transitive.jv | 3 +- 8 files changed, 104 insertions(+), 60 deletions(-) delete mode 100644 libs/language-server/src/lib/lsp/jayvee-scope-computation.ts create mode 100644 libs/language-server/src/test/assets/import-published-references/invalid-reference-to-element-transitive-no-republish.jv create mode 100644 libs/language-server/src/test/assets/import-published-references/not-publishing-transitive.jv diff --git a/libs/language-server/src/lib/jayvee-module.ts b/libs/language-server/src/lib/jayvee-module.ts index 2ca8d8f79..55d82b767 100644 --- a/libs/language-server/src/lib/jayvee-module.ts +++ b/libs/language-server/src/lib/jayvee-module.ts @@ -91,7 +91,6 @@ export const JayveeModule: Module< }, references: { ScopeProvider: (services) => new JayveeScopeProvider(services), - ScopeComputation: (service) => new JayveeScopeComputation(service), }, RuntimeParameterProvider: () => new RuntimeParameterProvider(), operators: { diff --git a/libs/language-server/src/lib/lsp/import-reference.spec.ts b/libs/language-server/src/lib/lsp/import-reference.spec.ts index 226044271..2b7810fb3 100644 --- a/libs/language-server/src/lib/lsp/import-reference.spec.ts +++ b/libs/language-server/src/lib/lsp/import-reference.spec.ts @@ -75,6 +75,10 @@ describe('References to imported elements', () => { 'should not resolve reference to non-imported element', 'import-published-references/invalid-reference-to-not-imported-element.jv', ], + [ + 'should not resolve reference to not re-published transitive element', + 'import-published-references/invalid-reference-to-element-transitive-no-republish.jv', + ], ]; test.each(invalidCases)('%s', async (_, relativeTestFilePath) => { const model = await parseModel(relativeTestFilePath); diff --git a/libs/language-server/src/lib/lsp/index.ts b/libs/language-server/src/lib/lsp/index.ts index 1907e2f15..46df76128 100644 --- a/libs/language-server/src/lib/lsp/index.ts +++ b/libs/language-server/src/lib/lsp/index.ts @@ -5,7 +5,6 @@ export * from './jayvee-completion-provider'; export * from './jayvee-formatter'; export * from './jayvee-hover-provider'; -export * from './jayvee-scope-computation'; export * from './jayvee-scope-provider'; export * from './jayvee-definition-provider'; export * from './jayvee-code-action-provider'; diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-computation.ts b/libs/language-server/src/lib/lsp/jayvee-scope-computation.ts deleted file mode 100644 index 03ebff7e4..000000000 --- a/libs/language-server/src/lib/lsp/jayvee-scope-computation.ts +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg -// -// SPDX-License-Identifier: AGPL-3.0-only - -import { - type AstNode, - type AstNodeDescription, - DefaultScopeComputation, - type LangiumCoreServices, - type LangiumDocument, -} from 'langium'; - -import { isExportDefinition, isExportableElement } from '../ast'; - -export class JayveeScopeComputation extends DefaultScopeComputation { - constructor(services: LangiumCoreServices) { - super(services); - } - - protected override exportNode( - node: AstNode, - exports: AstNodeDescription[], - document: LangiumDocument, - ): void { - const isExportingElementDefinition = - isExportableElement(node) && node.isPublished; - - if (isExportingElementDefinition) { - return super.exportNode(node, exports, document); - } - - const isDelayedExportDefinition = isExportDefinition(node); - if (isDelayedExportDefinition) { - const exportedNode = node.element.ref; - if (exportedNode === undefined) { - return; - } - - return super.exportNode(exportedNode, exports, document); - } - - // We only export elements that are explicitly published! - } -} diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index fda62b3ad..91a852f58 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -2,10 +2,15 @@ // // SPDX-License-Identifier: AGPL-3.0-only +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + import { + type AstNodeDescription, AstUtils, DefaultScopeProvider, EMPTY_SCOPE, + type LangiumDocument, type LangiumDocuments, MapScope, type ReferenceInfo, @@ -13,7 +18,14 @@ import { URI, } from 'langium'; -import { type JayveeModel, isJayveeModel } from '../ast'; +import { + type ExportDefinition, + type ExportableElement, + type JayveeModel, + isExportDefinition, + isExportableElement, + isJayveeModel, +} from '../ast'; import { getStdLib } from '../builtin-library'; import { type JayveeServices } from '../jayvee-module'; import { type JayveeImportResolver } from '../services/import-resolver'; @@ -40,20 +52,83 @@ export class JayveeScopeProvider extends DefaultScopeProvider { return EMPTY_SCOPE; } - const importedUris = new Set(); + // TODO: add caching to avoid performance issues + + const importedUris = new Set(); // TODO: refactor to Set this.gatherImports(jayveeModel, importedUris); this.gatherBuiltins(importedUris); - const importedElements = this.indexManager.allElements( - referenceType, - importedUris, + const importedDocuments = [...importedUris].map((importedUri) => + this.langiumDocuments.getDocument(URI.parse(importedUri)), ); + const importedElements: AstNodeDescription[] = []; + for (const importedDocument of importedDocuments) { + if (importedDocument === undefined) { + continue; + } + + const publishedElements = this.getExportedElements(importedDocument); + importedElements.push( + ...publishedElements.map((e) => + this.descriptions.createDescription(e, e.name), + ), + ); + } + return new MapScope(importedElements); } /** - * Add all builtins URIs to @param importedUris + * Gets all exported elements from a document. + * This logic cannot reside in a {@link ScopeComputationProvider} but should be handled here: + * https://github.com/eclipse-langium/langium/discussions/1508#discussioncomment-9524544 + */ + protected getExportedElements( + document: LangiumDocument, + ): ExportableElement[] { + const model = document.parseResult.value as JayveeModel; + const exportedElements: ExportableElement[] = []; + + for (const node of AstUtils.streamAllContents(model)) { + if (isExportableElement(node) && node.isPublished) { + exportedElements.push(node); + } + + if (isExportDefinition(node)) { + const originalDefinition = this.followExportDefinitionChain(node); + if (originalDefinition !== undefined) { + exportedElements.push(originalDefinition); + } + } + } + return exportedElements; + } + + /** + * Follow an export statement to its original definition recursively. + */ + protected followExportDefinitionChain( + exportDefinition: ExportDefinition, + ): ExportableElement | undefined { + const referenced = exportDefinition.element.ref; + if (referenced === undefined) { + return undefined; // Cannot follow reference to original definition + } + + if (isExportableElement(referenced) && referenced.isPublished) { + return referenced; // Reached original definition + } + + assert( + isExportDefinition(referenced), // TODO: check if that actually can happen + 'Export referenced some non-exportable element', + ); + return this.followExportDefinitionChain(referenced); // TODO: avoid dependency cycles + } + + /** + * Add all builtins' URIs to @param importedUris */ private gatherBuiltins(importedUris: Set) { const builtins = getStdLib(); @@ -67,7 +142,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { } /** - * Recursively add all imported URIs to @param importedUris + * Add all imported URIs of the given @jayveeModel to @param importedUris */ private gatherImports( jayveeModel: JayveeModel, @@ -88,12 +163,6 @@ export class JayveeScopeProvider extends DefaultScopeProvider { if (importedDocument === undefined) { continue; } - - const rootNode = importedDocument.parseResult.value; - if (!isJayveeModel(rootNode)) { - continue; - } - this.gatherImports(rootNode, importedUris); } } } diff --git a/libs/language-server/src/test/assets/import-published-references/invalid-reference-to-element-transitive-no-republish.jv b/libs/language-server/src/test/assets/import-published-references/invalid-reference-to-element-transitive-no-republish.jv new file mode 100644 index 000000000..f7a65e214 --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/invalid-reference-to-element-transitive-no-republish.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +use * from './not-publishing-transitive.jv'; + +pipeline TestPipeline { + block UsingBlock oftype PublishedBlockType {} +} diff --git a/libs/language-server/src/test/assets/import-published-references/not-publishing-transitive.jv b/libs/language-server/src/test/assets/import-published-references/not-publishing-transitive.jv new file mode 100644 index 000000000..f8308c3be --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/not-publishing-transitive.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +use * from './publishing.jv'; + +// automatically, no elements are re-published diff --git a/libs/language-server/src/test/assets/import-published-references/publishing-transitive.jv b/libs/language-server/src/test/assets/import-published-references/publishing-transitive.jv index 8fb024e25..b951b5202 100644 --- a/libs/language-server/src/test/assets/import-published-references/publishing-transitive.jv +++ b/libs/language-server/src/test/assets/import-published-references/publishing-transitive.jv @@ -3,4 +3,5 @@ // SPDX-License-Identifier: AGPL-3.0-only use * from './publishing.jv'; -// automatically re-publishes all elements + +publish PublishedBlockType; // re-publish From 01ca888cba64805dee7fec11dc63a686b9363540 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 18:01:16 +0200 Subject: [PATCH 03/19] Add scoping behavior for delayed export statements --- libs/language-server/src/lib/jayvee-module.ts | 1 - .../src/lib/lsp/import-reference.spec.ts | 152 ++++++++++++------ .../src/lib/lsp/jayvee-scope-provider.ts | 28 +++- ...ence-to-element-transitive-no-republish.jv | 0 ...valid-reference-to-not-existing-element.jv | 0 ...valid-reference-to-not-imported-element.jv | 0 .../not-publishing-transitive.jv | 0 .../publishing-transitive.jv | 0 .../publishing.jv | 0 .../valid-reference-to-element-transitive.jv | 0 .../valid-reference-to-imported-element.jv | 0 ...ence-to-element-transitive-no-republish.jv | 9 ++ ...valid-reference-to-not-existing-element.jv | 9 ++ ...valid-reference-to-not-imported-element.jv | 7 + .../not-publishing-transitive.jv | 7 + .../publishing-transitive.jv | 7 + .../publishing.jv | 10 ++ .../valid-reference-to-element-transitive.jv | 9 ++ .../valid-reference-to-imported-element.jv | 9 ++ 19 files changed, 196 insertions(+), 52 deletions(-) rename libs/language-server/src/test/assets/import-published-references/{ => published-via-element-definition}/invalid-reference-to-element-transitive-no-republish.jv (100%) rename libs/language-server/src/test/assets/import-published-references/{ => published-via-element-definition}/invalid-reference-to-not-existing-element.jv (100%) rename libs/language-server/src/test/assets/import-published-references/{ => published-via-element-definition}/invalid-reference-to-not-imported-element.jv (100%) rename libs/language-server/src/test/assets/import-published-references/{ => published-via-element-definition}/not-publishing-transitive.jv (100%) rename libs/language-server/src/test/assets/import-published-references/{ => published-via-element-definition}/publishing-transitive.jv (100%) rename libs/language-server/src/test/assets/import-published-references/{ => published-via-element-definition}/publishing.jv (100%) rename libs/language-server/src/test/assets/import-published-references/{ => published-via-element-definition}/valid-reference-to-element-transitive.jv (100%) rename libs/language-server/src/test/assets/import-published-references/{ => published-via-element-definition}/valid-reference-to-imported-element.jv (100%) create mode 100644 libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-element-transitive-no-republish.jv create mode 100644 libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-not-existing-element.jv create mode 100644 libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-not-imported-element.jv create mode 100644 libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/not-publishing-transitive.jv create mode 100644 libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/publishing-transitive.jv create mode 100644 libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/publishing.jv create mode 100644 libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/valid-reference-to-element-transitive.jv create mode 100644 libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/valid-reference-to-imported-element.jv diff --git a/libs/language-server/src/lib/jayvee-module.ts b/libs/language-server/src/lib/jayvee-module.ts index 55d82b767..2079f6e6e 100644 --- a/libs/language-server/src/lib/jayvee-module.ts +++ b/libs/language-server/src/lib/jayvee-module.ts @@ -32,7 +32,6 @@ import { JayveeDefinitionProvider, JayveeFormatter, JayveeHoverProvider, - JayveeScopeComputation, JayveeScopeProvider, } from './lsp'; import { JayveeImportResolver } from './services/import-resolver'; diff --git a/libs/language-server/src/lib/lsp/import-reference.spec.ts b/libs/language-server/src/lib/lsp/import-reference.spec.ts index 2b7810fb3..ed7051280 100644 --- a/libs/language-server/src/lib/lsp/import-reference.spec.ts +++ b/libs/language-server/src/lib/lsp/import-reference.spec.ts @@ -41,55 +41,111 @@ describe('References to imported elements', () => { services = createJayveeServices(NodeFileSystem).Jayvee; }); - const validCases: [string, string][] = [ - // [test name, test file path] - [ - 'should resolve reference to imported element', - 'import-published-references/valid-reference-to-imported-element.jv', - ], - [ - 'should resolve reference to transitively imported element', - 'import-published-references/valid-reference-to-element-transitive.jv', - ], - ]; - test.each(validCases)('%s', async (_, relativeTestFilePath) => { - const model = await parseModel(relativeTestFilePath); - - expect(model.pipelines.length).toEqual(1); // file contains one pipeline - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const blocks = model.pipelines[0]!.blocks; - expect(blocks.length).toEqual(1); // pipeline contains one block - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const reference = blocks[0]!.type; // of an imported block type - - expect(reference.ref).toBeDefined(); + describe('when element is directly published in element definition', () => { + const validCases: [string, string][] = [ + // [test name, test file path] + [ + 'should resolve reference to imported element', + 'import-published-references/published-via-element-definition/valid-reference-to-imported-element.jv', + ], + [ + 'should resolve reference to transitively imported element', + 'import-published-references/published-via-element-definition/valid-reference-to-element-transitive.jv', + ], + ]; + test.each(validCases)('%s', async (_, relativeTestFilePath) => { + const model = await parseModel(relativeTestFilePath); + + expect(model.pipelines.length).toEqual(1); // file contains one pipeline + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const blocks = model.pipelines[0]!.blocks; + expect(blocks.length).toEqual(1); // pipeline contains one block + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const reference = blocks[0]!.type; // of an imported block type + + expect(reference.ref).toBeDefined(); + }); + + const invalidCases: [string, string][] = [ + // [test name, test file path] + [ + 'should not resolve reference to non-existing element', + 'import-published-references/published-via-element-definition/invalid-reference-to-not-existing-element.jv', + ], + [ + 'should not resolve reference to non-imported element', + 'import-published-references/published-via-element-definition/invalid-reference-to-not-imported-element.jv', + ], + [ + 'should not resolve reference to not re-published transitive element', + 'import-published-references/published-via-element-definition/invalid-reference-to-element-transitive-no-republish.jv', + ], + ]; + test.each(invalidCases)('%s', async (_, relativeTestFilePath) => { + const model = await parseModel(relativeTestFilePath); + + expect(model.pipelines.length).toEqual(1); // file contains one pipeline + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const blocks = model.pipelines[0]!.blocks; + expect(blocks.length).toEqual(1); // pipeline contains one block + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const reference = blocks[0]!.type; // of an imported block type + + expect(reference.ref).toBeUndefined(); + }); }); - const invalidCases: [string, string][] = [ - // [test name, test file path] - [ - 'should not resolve reference to non-existing element', - 'import-published-references/invalid-reference-to-not-existing-element.jv', - ], - [ - 'should not resolve reference to non-imported element', - 'import-published-references/invalid-reference-to-not-imported-element.jv', - ], - [ - 'should not resolve reference to not re-published transitive element', - 'import-published-references/invalid-reference-to-element-transitive-no-republish.jv', - ], - ]; - test.each(invalidCases)('%s', async (_, relativeTestFilePath) => { - const model = await parseModel(relativeTestFilePath); - - expect(model.pipelines.length).toEqual(1); // file contains one pipeline - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const blocks = model.pipelines[0]!.blocks; - expect(blocks.length).toEqual(1); // pipeline contains one block - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const reference = blocks[0]!.type; // of an imported block type - - expect(reference.ref).toBeUndefined(); + describe('when element is published delayed via export definition', () => { + const validCases: [string, string][] = [ + // [test name, test file path] + [ + 'should resolve reference to imported element', + 'import-published-references/published-via-publish-definition/valid-reference-to-imported-element.jv', + ], + [ + 'should resolve reference to transitively imported element', + 'import-published-references/published-via-publish-definition/valid-reference-to-element-transitive.jv', + ], + ]; + test.each(validCases)('%s', async (_, relativeTestFilePath) => { + const model = await parseModel(relativeTestFilePath); + + expect(model.pipelines.length).toEqual(1); // file contains one pipeline + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const blocks = model.pipelines[0]!.blocks; + expect(blocks.length).toEqual(1); // pipeline contains one block + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const reference = blocks[0]!.type; // of an imported block type + + expect(reference.ref).toBeDefined(); + }); + + const invalidCases: [string, string][] = [ + // [test name, test file path] + [ + 'should not resolve reference to non-existing element', + 'import-published-references/published-via-publish-definition/invalid-reference-to-not-existing-element.jv', + ], + [ + 'should not resolve reference to non-imported element', + 'import-published-references/published-via-publish-definition/invalid-reference-to-not-imported-element.jv', + ], + [ + 'should not resolve reference to not re-published transitive element', + 'import-published-references/published-via-publish-definition/invalid-reference-to-element-transitive-no-republish.jv', + ], + ]; + test.each(invalidCases)('%s', async (_, relativeTestFilePath) => { + const model = await parseModel(relativeTestFilePath); + + expect(model.pipelines.length).toEqual(1); // file contains one pipeline + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const blocks = model.pipelines[0]!.blocks; + expect(blocks.length).toEqual(1); // pipeline contains one block + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const reference = blocks[0]!.type; // of an imported block type + + expect(reference.ref).toBeUndefined(); + }); }); }); diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 91a852f58..228bf2798 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -116,7 +116,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { return undefined; // Cannot follow reference to original definition } - if (isExportableElement(referenced) && referenced.isPublished) { + if (isExportableElement(referenced) && this.isElementExported(referenced)) { return referenced; // Reached original definition } @@ -127,10 +127,32 @@ export class JayveeScopeProvider extends DefaultScopeProvider { return this.followExportDefinitionChain(referenced); // TODO: avoid dependency cycles } + /** + * Checks whether an exportable @param element is exported (either in definition or via an delayed export definition). + */ + protected isElementExported(element: ExportableElement): boolean { + if (element.isPublished) { + return true; + } + + const model = AstUtils.getContainerOfType(element, isJayveeModel); + assert( + model !== undefined, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + `Could not get container of exportable element ${element.name ?? ''}`, + ); + + const isExported = model.exports.some( + (exportDefinition: ExportDefinition) => + exportDefinition.element.ref === element, + ); + return isExported; + } + /** * Add all builtins' URIs to @param importedUris */ - private gatherBuiltins(importedUris: Set) { + protected gatherBuiltins(importedUris: Set) { const builtins = getStdLib(); const uris = Object.keys(builtins); @@ -144,7 +166,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { /** * Add all imported URIs of the given @jayveeModel to @param importedUris */ - private gatherImports( + protected gatherImports( jayveeModel: JayveeModel, importedUris: Set, ): void { diff --git a/libs/language-server/src/test/assets/import-published-references/invalid-reference-to-element-transitive-no-republish.jv b/libs/language-server/src/test/assets/import-published-references/published-via-element-definition/invalid-reference-to-element-transitive-no-republish.jv similarity index 100% rename from libs/language-server/src/test/assets/import-published-references/invalid-reference-to-element-transitive-no-republish.jv rename to libs/language-server/src/test/assets/import-published-references/published-via-element-definition/invalid-reference-to-element-transitive-no-republish.jv diff --git a/libs/language-server/src/test/assets/import-published-references/invalid-reference-to-not-existing-element.jv b/libs/language-server/src/test/assets/import-published-references/published-via-element-definition/invalid-reference-to-not-existing-element.jv similarity index 100% rename from libs/language-server/src/test/assets/import-published-references/invalid-reference-to-not-existing-element.jv rename to libs/language-server/src/test/assets/import-published-references/published-via-element-definition/invalid-reference-to-not-existing-element.jv diff --git a/libs/language-server/src/test/assets/import-published-references/invalid-reference-to-not-imported-element.jv b/libs/language-server/src/test/assets/import-published-references/published-via-element-definition/invalid-reference-to-not-imported-element.jv similarity index 100% rename from libs/language-server/src/test/assets/import-published-references/invalid-reference-to-not-imported-element.jv rename to libs/language-server/src/test/assets/import-published-references/published-via-element-definition/invalid-reference-to-not-imported-element.jv diff --git a/libs/language-server/src/test/assets/import-published-references/not-publishing-transitive.jv b/libs/language-server/src/test/assets/import-published-references/published-via-element-definition/not-publishing-transitive.jv similarity index 100% rename from libs/language-server/src/test/assets/import-published-references/not-publishing-transitive.jv rename to libs/language-server/src/test/assets/import-published-references/published-via-element-definition/not-publishing-transitive.jv diff --git a/libs/language-server/src/test/assets/import-published-references/publishing-transitive.jv b/libs/language-server/src/test/assets/import-published-references/published-via-element-definition/publishing-transitive.jv similarity index 100% rename from libs/language-server/src/test/assets/import-published-references/publishing-transitive.jv rename to libs/language-server/src/test/assets/import-published-references/published-via-element-definition/publishing-transitive.jv diff --git a/libs/language-server/src/test/assets/import-published-references/publishing.jv b/libs/language-server/src/test/assets/import-published-references/published-via-element-definition/publishing.jv similarity index 100% rename from libs/language-server/src/test/assets/import-published-references/publishing.jv rename to libs/language-server/src/test/assets/import-published-references/published-via-element-definition/publishing.jv diff --git a/libs/language-server/src/test/assets/import-published-references/valid-reference-to-element-transitive.jv b/libs/language-server/src/test/assets/import-published-references/published-via-element-definition/valid-reference-to-element-transitive.jv similarity index 100% rename from libs/language-server/src/test/assets/import-published-references/valid-reference-to-element-transitive.jv rename to libs/language-server/src/test/assets/import-published-references/published-via-element-definition/valid-reference-to-element-transitive.jv diff --git a/libs/language-server/src/test/assets/import-published-references/valid-reference-to-imported-element.jv b/libs/language-server/src/test/assets/import-published-references/published-via-element-definition/valid-reference-to-imported-element.jv similarity index 100% rename from libs/language-server/src/test/assets/import-published-references/valid-reference-to-imported-element.jv rename to libs/language-server/src/test/assets/import-published-references/published-via-element-definition/valid-reference-to-imported-element.jv diff --git a/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-element-transitive-no-republish.jv b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-element-transitive-no-republish.jv new file mode 100644 index 000000000..f7a65e214 --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-element-transitive-no-republish.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +use * from './not-publishing-transitive.jv'; + +pipeline TestPipeline { + block UsingBlock oftype PublishedBlockType {} +} diff --git a/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-not-existing-element.jv b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-not-existing-element.jv new file mode 100644 index 000000000..5dce66109 --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-not-existing-element.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +use * from './publishing.jv'; + +pipeline TestPipeline { + block UsingBlock oftype NotPublishedBlockType {} +} diff --git a/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-not-imported-element.jv b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-not-imported-element.jv new file mode 100644 index 000000000..eccfb294e --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/invalid-reference-to-not-imported-element.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + block UsingBlock oftype PublishedBlockType {} // ref to not imported element +} diff --git a/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/not-publishing-transitive.jv b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/not-publishing-transitive.jv new file mode 100644 index 000000000..f8308c3be --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/not-publishing-transitive.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +use * from './publishing.jv'; + +// automatically, no elements are re-published diff --git a/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/publishing-transitive.jv b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/publishing-transitive.jv new file mode 100644 index 000000000..b951b5202 --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/publishing-transitive.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +use * from './publishing.jv'; + +publish PublishedBlockType; // re-publish diff --git a/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/publishing.jv b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/publishing.jv new file mode 100644 index 000000000..8bd5a7f4b --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/publishing.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype PublishedBlockType { + input inPort oftype None; + output outPort oftype None; +} + +publish PublishedBlockType; diff --git a/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/valid-reference-to-element-transitive.jv b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/valid-reference-to-element-transitive.jv new file mode 100644 index 000000000..ac0bb36e8 --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/valid-reference-to-element-transitive.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +use * from './publishing-transitive.jv'; + +pipeline TestPipeline { + block UsingBlock oftype PublishedBlockType {} +} diff --git a/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/valid-reference-to-imported-element.jv b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/valid-reference-to-imported-element.jv new file mode 100644 index 000000000..428366c4d --- /dev/null +++ b/libs/language-server/src/test/assets/import-published-references/published-via-publish-definition/valid-reference-to-imported-element.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +use * from './publishing.jv'; + +pipeline TestPipeline { + block UsingBlock oftype PublishedBlockType {} +} From 9b6861f12b9b1d8a252bfe703bfa911114bd9073 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 18:04:58 +0200 Subject: [PATCH 04/19] Remove unnecessary recursion in followExportDefinitionChain --- libs/language-server/src/lib/lsp/jayvee-scope-provider.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 228bf2798..126fad184 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -116,15 +116,11 @@ export class JayveeScopeProvider extends DefaultScopeProvider { return undefined; // Cannot follow reference to original definition } - if (isExportableElement(referenced) && this.isElementExported(referenced)) { + if (this.isElementExported(referenced)) { return referenced; // Reached original definition } - assert( - isExportDefinition(referenced), // TODO: check if that actually can happen - 'Export referenced some non-exportable element', - ); - return this.followExportDefinitionChain(referenced); // TODO: avoid dependency cycles + // TODO: avoid dependency cycles } /** From 48df33abee7fd3f3f3276e13fec98ef8398bf7e2 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 18:37:17 +0200 Subject: [PATCH 05/19] Add validation for import cycles --- .../src/lib/lsp/jayvee-scope-provider.ts | 2 - .../checks/import-definition.spec.ts | 45 ++++++++++++++++ .../validation/checks/import-definition.ts | 51 +++++++++++++++++++ .../invalid-dependency-cycle-1.jv | 10 ++++ .../invalid-dependency-cycle-2.jv | 11 ++++ .../invalid-dependency-cycle-self-import.jv | 1 + .../invalid-dependency-cycle-transitive-1.jv | 1 + .../invalid-dependency-cycle-transitive-2.jv | 11 ++++ .../invalid-dependency-cycle-transitive-3.jv | 11 ++++ 9 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-1.jv create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-2.jv create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-self-import.jv create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 126fad184..98deca04b 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -119,8 +119,6 @@ export class JayveeScopeProvider extends DefaultScopeProvider { if (this.isElementExported(referenced)) { return referenced; // Reached original definition } - - // TODO: avoid dependency cycles } /** diff --git a/libs/language-server/src/lib/validation/checks/import-definition.spec.ts b/libs/language-server/src/lib/validation/checks/import-definition.spec.ts index 7f9e63f72..caa7ffd7e 100644 --- a/libs/language-server/src/lib/validation/checks/import-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/import-definition.spec.ts @@ -131,5 +131,50 @@ describe('Validation of ImportDefinition', () => { expect.any(Object), ); }); + + it('should diagnose error on cyclic import', async () => { + const relativeTestFilePath = + 'import-definition/invalid-dependency-cycle-1.jv'; + + await parseAndValidateImportDefinition(relativeTestFilePath); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 1, + 'error', + 'Import from "./invalid-dependency-cycle-2.jv" leads to import cycle.', + expect.any(Object), + ); + }); + + it('should diagnose error on cyclic import when importing itself', async () => { + const relativeTestFilePath = + 'import-definition/invalid-dependency-cycle-self-import.jv'; + + await parseAndValidateImportDefinition(relativeTestFilePath); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 1, + 'error', + 'Import from "./invalid-dependency-cycle-self-import.jv" leads to import cycle.', + expect.any(Object), + ); + }); + + it('should diagnose error on cyclic import even when cycle resides in imported file', async () => { + const relativeTestFilePath = + 'import-definition/invalid-dependency-cycle-transitive-1.jv'; + + await parseAndValidateImportDefinition(relativeTestFilePath); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 1, + 'error', + 'Import from "./invalid-dependency-cycle-2.jv" leads to import cycle.', + expect.any(Object), + ); + }); }); }); diff --git a/libs/language-server/src/lib/validation/checks/import-definition.ts b/libs/language-server/src/lib/validation/checks/import-definition.ts index 106169ca9..58c4fd066 100644 --- a/libs/language-server/src/lib/validation/checks/import-definition.ts +++ b/libs/language-server/src/lib/validation/checks/import-definition.ts @@ -2,6 +2,9 @@ // // SPDX-License-Identifier: AGPL-3.0-only +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + import { type ImportDefinition } from '../../ast/generated/ast'; import { type JayveeValidationProps } from '../validation-registry'; @@ -10,6 +13,11 @@ export function validateImportDefinition( props: JayveeValidationProps, ): void { checkPathExists(importDefinition, props); + if (props.validationContext.hasErrorOccurred()) { + return; + } + + checkCyclicImportChain(importDefinition, props); } function checkPathExists( @@ -28,3 +36,46 @@ function checkPathExists( ); } } + +function checkCyclicImportChain( + importDefinition: ImportDefinition, + props: JayveeValidationProps, +): void { + const isImportCycle = isCyclicImportChain(importDefinition, new Set(), props); + if (isImportCycle) { + props.validationContext.accept( + 'error', + `Import from "${importDefinition.path}" leads to import cycle.`, + { + node: importDefinition, + }, + ); + } +} + +function isCyclicImportChain( + importDefinition: ImportDefinition, + visitedDocumentUris: Set, + props: JayveeValidationProps, +): boolean { + const importedModel = props.importResolver.resolveImport(importDefinition); + if (importedModel === undefined) { + return false; + } + + const currentVisitedUri = importedModel.$document?.uri.toString(); + assert( + currentVisitedUri !== undefined, + 'Could not resolve URI of imported document', + ); + + if (visitedDocumentUris.has(currentVisitedUri)) { + // cycle detected + return true; + } + visitedDocumentUris.add(currentVisitedUri); + + return importedModel.imports.some((furtherImport) => + isCyclicImportChain(furtherImport, visitedDocumentUris, props), + ); +} diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-1.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-1.jv new file mode 100644 index 000000000..647e7c6c2 --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-1.jv @@ -0,0 +1,10 @@ +use * from './invalid-dependency-cycle-2.jv'; + +publish composite blocktype DependencyCycleBlockType1 { + input inPort oftype None; + output outPort oftype Sheet; + + block TestBlock oftype DependencyCycleBlockType2 {} + + inPort -> TestBlock -> outPort; +} diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-2.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-2.jv new file mode 100644 index 000000000..188c8872d --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-2.jv @@ -0,0 +1,11 @@ + +use * from './invalid-dependency-cycle-1.jv'; + +publish composite blocktype DependencyCycleBlockType2 { + input inPort oftype None; + output outPort oftype Sheet; + + block TestBlock oftype DependencyCycleBlockType1 {} + + inPort -> TestBlock -> outPort; +} diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-self-import.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-self-import.jv new file mode 100644 index 000000000..f911de0b7 --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-self-import.jv @@ -0,0 +1 @@ +use * from './invalid-dependency-cycle-self-import.jv'; // imported file has import cycle diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv new file mode 100644 index 000000000..26ec3a755 --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv @@ -0,0 +1 @@ +use * from './invalid-dependency-cycle-2.jv'; // imported file has import cycle diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv new file mode 100644 index 000000000..188c8872d --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv @@ -0,0 +1,11 @@ + +use * from './invalid-dependency-cycle-1.jv'; + +publish composite blocktype DependencyCycleBlockType2 { + input inPort oftype None; + output outPort oftype Sheet; + + block TestBlock oftype DependencyCycleBlockType1 {} + + inPort -> TestBlock -> outPort; +} diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv new file mode 100644 index 000000000..188c8872d --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv @@ -0,0 +1,11 @@ + +use * from './invalid-dependency-cycle-1.jv'; + +publish composite blocktype DependencyCycleBlockType2 { + input inPort oftype None; + output outPort oftype Sheet; + + block TestBlock oftype DependencyCycleBlockType1 {} + + inPort -> TestBlock -> outPort; +} From dd29b3ba75aad0d26c9c38232422337083a63911 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 19:02:19 +0200 Subject: [PATCH 06/19] Show dependency cycle to user in validation --- .../checks/import-definition.spec.ts | 21 ++++++- .../validation/checks/import-definition.ts | 59 +++++++++++++++---- .../invalid-dependency-cycle-deeper-1.jv | 1 + .../invalid-dependency-cycle-deeper-2.jv | 10 ++++ .../invalid-dependency-cycle-deeper-3.jv | 10 ++++ .../invalid-dependency-cycle-transitive-1.jv | 11 +++- .../invalid-dependency-cycle-transitive-2.jv | 12 +--- .../invalid-dependency-cycle-transitive-3.jv | 3 +- 8 files changed, 101 insertions(+), 26 deletions(-) create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-1.jv create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-2.jv create mode 100644 libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-3.jv diff --git a/libs/language-server/src/lib/validation/checks/import-definition.spec.ts b/libs/language-server/src/lib/validation/checks/import-definition.spec.ts index caa7ffd7e..cb2e0dd75 100644 --- a/libs/language-server/src/lib/validation/checks/import-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/import-definition.spec.ts @@ -142,7 +142,7 @@ describe('Validation of ImportDefinition', () => { expect(validationAcceptorMock).toHaveBeenNthCalledWith( 1, 'error', - 'Import from "./invalid-dependency-cycle-2.jv" leads to import cycle.', + 'Import from "./invalid-dependency-cycle-2.jv" leads to import cycle: "./invalid-dependency-cycle-2.jv" -> "./invalid-dependency-cycle-1.jv" -> "./invalid-dependency-cycle-2.jv"', expect.any(Object), ); }); @@ -157,12 +157,27 @@ describe('Validation of ImportDefinition', () => { expect(validationAcceptorMock).toHaveBeenNthCalledWith( 1, 'error', - 'Import from "./invalid-dependency-cycle-self-import.jv" leads to import cycle.', + 'Import from "./invalid-dependency-cycle-self-import.jv" leads to import cycle: "./invalid-dependency-cycle-self-import.jv" -> "./invalid-dependency-cycle-self-import.jv"', expect.any(Object), ); }); it('should diagnose error on cyclic import even when cycle resides in imported file', async () => { + const relativeTestFilePath = + 'import-definition/invalid-dependency-cycle-deeper-1.jv'; + + await parseAndValidateImportDefinition(relativeTestFilePath); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 1, + 'error', + 'Import from "./invalid-dependency-cycle-deeper-2.jv" leads to import cycle: "./invalid-dependency-cycle-deeper-2.jv" -> "./invalid-dependency-cycle-deeper-3.jv" -> "./invalid-dependency-cycle-deeper-2.jv"', + expect.any(Object), + ); + }); + + it('should diagnose error on cyclic import spans multiple files', async () => { const relativeTestFilePath = 'import-definition/invalid-dependency-cycle-transitive-1.jv'; @@ -172,7 +187,7 @@ describe('Validation of ImportDefinition', () => { expect(validationAcceptorMock).toHaveBeenNthCalledWith( 1, 'error', - 'Import from "./invalid-dependency-cycle-2.jv" leads to import cycle.', + 'Import from "./invalid-dependency-cycle-transitive-2.jv" leads to import cycle: "./invalid-dependency-cycle-transitive-2.jv" -> "./invalid-dependency-cycle-transitive-3.jv" -> "./invalid-dependency-cycle-transitive-1.jv" -> "./invalid-dependency-cycle-transitive-2.jv"', expect.any(Object), ); }); diff --git a/libs/language-server/src/lib/validation/checks/import-definition.ts b/libs/language-server/src/lib/validation/checks/import-definition.ts index 58c4fd066..cac0ba6f7 100644 --- a/libs/language-server/src/lib/validation/checks/import-definition.ts +++ b/libs/language-server/src/lib/validation/checks/import-definition.ts @@ -41,11 +41,22 @@ function checkCyclicImportChain( importDefinition: ImportDefinition, props: JayveeValidationProps, ): void { - const isImportCycle = isCyclicImportChain(importDefinition, new Set(), props); - if (isImportCycle) { + const cycleAnalysisTraces = analyzeImportChain( + importDefinition, + new Set(), + props, + ); + + const cycles = cycleAnalysisTraces.filter((x) => x.isCyclic); + + if (cycles.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const shownImportChain = cycles[0]!.path + .map((x) => `"${x.path}"`) + .join(' -> '); props.validationContext.accept( 'error', - `Import from "${importDefinition.path}" leads to import cycle.`, + `Import from "${importDefinition.path}" leads to import cycle: ${shownImportChain}`, { node: importDefinition, }, @@ -53,14 +64,24 @@ function checkCyclicImportChain( } } -function isCyclicImportChain( +interface ImportChainTrace { + isCyclic: boolean; + path: ImportDefinition[]; +} + +function analyzeImportChain( importDefinition: ImportDefinition, visitedDocumentUris: Set, props: JayveeValidationProps, -): boolean { +): ImportChainTrace[] { const importedModel = props.importResolver.resolveImport(importDefinition); if (importedModel === undefined) { - return false; + return [ + { + isCyclic: false, + path: [importDefinition], + }, + ]; } const currentVisitedUri = importedModel.$document?.uri.toString(); @@ -71,11 +92,29 @@ function isCyclicImportChain( if (visitedDocumentUris.has(currentVisitedUri)) { // cycle detected - return true; + return [ + { + isCyclic: true, + path: [importDefinition], + }, + ]; } visitedDocumentUris.add(currentVisitedUri); - return importedModel.imports.some((furtherImport) => - isCyclicImportChain(furtherImport, visitedDocumentUris, props), - ); + const cycledAnalysisResult: ImportChainTrace[] = []; + for (const furtherImport of importedModel.imports) { + const downwardAnalysisResult = analyzeImportChain( + furtherImport, + visitedDocumentUris, + props, + ); + const addedThisNodeToPath = downwardAnalysisResult.map((r) => { + return { + isCyclic: r.isCyclic, + path: [importDefinition, ...r.path], + }; + }); + cycledAnalysisResult.push(...addedThisNodeToPath); + } + return cycledAnalysisResult; } diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-1.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-1.jv new file mode 100644 index 000000000..c4fba7b93 --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-1.jv @@ -0,0 +1 @@ +use * from './invalid-dependency-cycle-deeper-2.jv'; // imported file has import cycle diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-2.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-2.jv new file mode 100644 index 000000000..6f8e46cd6 --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-2.jv @@ -0,0 +1,10 @@ +use * from './invalid-dependency-cycle-deeper-3.jv'; + +publish composite blocktype DependencyCycleBlockType2 { + input inPort oftype None; + output outPort oftype Sheet; + + block TestBlock oftype DependencyCycleBlockType1 {} + + inPort -> TestBlock -> outPort; +} diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-3.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-3.jv new file mode 100644 index 000000000..cf5066a74 --- /dev/null +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-3.jv @@ -0,0 +1,10 @@ +use * from './invalid-dependency-cycle-deeper-2.jv'; + +publish composite blocktype DependencyCycleBlockType2 { + input inPort oftype None; + output outPort oftype Sheet; + + block TestBlock oftype DependencyCycleBlockType1 {} + + inPort -> TestBlock -> outPort; +} diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv index 26ec3a755..f336a1ae8 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv @@ -1 +1,10 @@ -use * from './invalid-dependency-cycle-2.jv'; // imported file has import cycle +use * from './invalid-dependency-cycle-transitive-2.jv'; // imported file has import cycle + +publish composite blocktype DependencyCycleBlockType2 { + input inPort oftype None; + output outPort oftype Sheet; + + block TestBlock oftype DependencyCycleBlockType1 {} + + inPort -> TestBlock -> outPort; +} diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv index 188c8872d..2099c1270 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv @@ -1,11 +1,3 @@ +use * from './invalid-dependency-cycle-transitive-3.jv'; -use * from './invalid-dependency-cycle-1.jv'; - -publish composite blocktype DependencyCycleBlockType2 { - input inPort oftype None; - output outPort oftype Sheet; - - block TestBlock oftype DependencyCycleBlockType1 {} - - inPort -> TestBlock -> outPort; -} +publish DependencyCycleBlockType2; diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv index 188c8872d..81cd1f7c7 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv @@ -1,5 +1,4 @@ - -use * from './invalid-dependency-cycle-1.jv'; +use * from './invalid-dependency-cycle-transitive-1.jv'; publish composite blocktype DependencyCycleBlockType2 { input inPort oftype None; From 5aceb981af6ee4044f0f7334ade9da33059dd2ed Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 19:03:55 +0200 Subject: [PATCH 07/19] Add license information to newly added test files --- .../assets/import-definition/invalid-dependency-cycle-1.jv | 4 ++++ .../assets/import-definition/invalid-dependency-cycle-2.jv | 3 +++ .../import-definition/invalid-dependency-cycle-deeper-1.jv | 4 ++++ .../import-definition/invalid-dependency-cycle-deeper-2.jv | 4 ++++ .../import-definition/invalid-dependency-cycle-deeper-3.jv | 4 ++++ .../import-definition/invalid-dependency-cycle-self-import.jv | 4 ++++ .../invalid-dependency-cycle-transitive-1.jv | 4 ++++ .../invalid-dependency-cycle-transitive-2.jv | 4 ++++ .../invalid-dependency-cycle-transitive-3.jv | 4 ++++ 9 files changed, 35 insertions(+) diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-1.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-1.jv index 647e7c6c2..576444e6e 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-1.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-1.jv @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + use * from './invalid-dependency-cycle-2.jv'; publish composite blocktype DependencyCycleBlockType1 { diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-2.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-2.jv index 188c8872d..723993d5f 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-2.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-2.jv @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only use * from './invalid-dependency-cycle-1.jv'; diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-1.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-1.jv index c4fba7b93..348acee86 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-1.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-1.jv @@ -1 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + use * from './invalid-dependency-cycle-deeper-2.jv'; // imported file has import cycle diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-2.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-2.jv index 6f8e46cd6..542069f7e 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-2.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-2.jv @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + use * from './invalid-dependency-cycle-deeper-3.jv'; publish composite blocktype DependencyCycleBlockType2 { diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-3.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-3.jv index cf5066a74..0d9ce6566 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-3.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-deeper-3.jv @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + use * from './invalid-dependency-cycle-deeper-2.jv'; publish composite blocktype DependencyCycleBlockType2 { diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-self-import.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-self-import.jv index f911de0b7..21dc8214d 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-self-import.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-self-import.jv @@ -1 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + use * from './invalid-dependency-cycle-self-import.jv'; // imported file has import cycle diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv index f336a1ae8..ee8eaef09 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-1.jv @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + use * from './invalid-dependency-cycle-transitive-2.jv'; // imported file has import cycle publish composite blocktype DependencyCycleBlockType2 { diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv index 2099c1270..b9b276d1e 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-2.jv @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + use * from './invalid-dependency-cycle-transitive-3.jv'; publish DependencyCycleBlockType2; diff --git a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv index 81cd1f7c7..379f3266b 100644 --- a/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv +++ b/libs/language-server/src/test/assets/import-definition/invalid-dependency-cycle-transitive-3.jv @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + use * from './invalid-dependency-cycle-transitive-1.jv'; publish composite blocktype DependencyCycleBlockType2 { From 15eeb73a2dfb0974c972f149464fe8591878fb2e Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 19:06:20 +0200 Subject: [PATCH 08/19] Remove outdated TODO comment --- libs/language-server/src/lib/lsp/jayvee-scope-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 98deca04b..624d44e58 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -54,7 +54,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { // TODO: add caching to avoid performance issues - const importedUris = new Set(); // TODO: refactor to Set + const importedUris = new Set(); this.gatherImports(jayveeModel, importedUris); this.gatherBuiltins(importedUris); From 11e2f243a4c6af199fb5a987e80b9ce1acb3d973 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 19:10:12 +0200 Subject: [PATCH 09/19] Update documentation of followExportDefinitionChain method --- libs/language-server/src/lib/lsp/jayvee-scope-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 624d44e58..21cdf19e5 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -106,7 +106,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { } /** - * Follow an export statement to its original definition recursively. + * Follow an export statement to its original definition. */ protected followExportDefinitionChain( exportDefinition: ExportDefinition, From b94c29cec936b0168cdfc494d8c477270d68512c Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 23 May 2024 19:19:19 +0200 Subject: [PATCH 10/19] Fix followExportDefinitionChain --- libs/language-server/src/lib/lsp/jayvee-scope-provider.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 21cdf19e5..167cc1b8a 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -116,9 +116,11 @@ export class JayveeScopeProvider extends DefaultScopeProvider { return undefined; // Cannot follow reference to original definition } - if (this.isElementExported(referenced)) { - return referenced; // Reached original definition + if (!this.isElementExported(referenced)) { + return undefined; } + + return referenced; // Reached original definition } /** From 4fef7c43b34bf7df0e931ea827e606b1ae02b66d Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Fri, 24 May 2024 15:19:45 +0200 Subject: [PATCH 11/19] Refactor export mechanism to have a common grammar element instead of distributing the publish keyword --- .../allowlist-constraint-executor.spec.ts | 16 +++-- .../denylist-constraint-executor.spec.ts | 16 +++-- .../expression-constraint-executor.spec.ts | 18 ++++-- .../length-constraint-executor.spec.ts | 16 +++-- .../range-constraint-executor.spec.ts | 16 +++-- .../regex-constraint-executor.spec.ts | 16 +++-- .../lib/transforms/transform-executor.spec.ts | 12 ++-- .../src/grammar/block-type.langium | 4 +- .../src/grammar/constraint.langium | 6 +- .../src/grammar/io-type.langium | 2 +- libs/language-server/src/grammar/main.langium | 27 ++++----- .../src/grammar/transform.langium | 2 +- .../src/grammar/value-type.langium | 4 +- .../src/lib/ast/expressions/test-utils.ts | 17 ++++-- .../language-server/src/lib/ast/model-util.ts | 17 +++--- .../src/lib/lsp/jayvee-completion-provider.ts | 7 ++- .../src/lib/lsp/jayvee-scope-provider.ts | 13 ++-- .../checks/block-type-definition.spec.ts | 31 +++++----- .../property-assignment.spec.ts | 25 ++++---- .../block-type-specific/property-body.spec.ts | 25 ++++---- .../lib/validation/checks/column-id.spec.ts | 30 +++++----- .../composite-block-type-definition.spec.ts | 32 +++++----- .../property-assignment.spec.ts | 26 ++++---- .../property-body.spec.ts | 41 +++++++------ .../expression-constraint-definition.spec.ts | 34 +++++------ .../src/lib/validation/checks/jayvee-model.ts | 50 +++++++++++++--- .../validation/checks/pipe-definition.spec.ts | 27 ++++----- .../checks/pipeline-definition.spec.ts | 28 ++++----- .../checks/property-assignment.spec.ts | 59 +++++++++++-------- .../validation/checks/property-body.spec.ts | 36 ++++++----- .../validation/checks/range-literal.spec.ts | 28 ++++----- .../validation/checks/regex-literal.spec.ts | 28 ++++----- .../validation/checks/transform-body.spec.ts | 31 +++++----- .../transform-output-assignment.spec.ts | 34 +++++------ .../typed-constraint-definition.spec.ts | 32 +++++----- .../checks/value-type-definition.spec.ts | 30 +++++----- .../checks/value-type-reference.spec.ts | 49 +++++++++------ .../lib/validation/validation-utils.spec.ts | 4 +- 38 files changed, 489 insertions(+), 400 deletions(-) diff --git a/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts index d623ff64c..d8a5d6d56 100644 --- a/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts @@ -6,8 +6,8 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, - type TypedConstraintDefinition, createJayveeServices, + isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, @@ -18,6 +18,7 @@ import { import { type AstNode, type AstNodeLocator, + AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -51,10 +52,15 @@ describe('Validation of AllowlistConstraintExecutor', () => { document.parseResult.value, 'pipelines@0/blocks@2', ) as BlockDefinition; - const constraint = locator.getAstNode( - document.parseResult.value, - 'constraints@0', - ) as TypedConstraintDefinition; + + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; + expect( + allConstraints.length > 0, + 'No constraint definition found in test file', + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const constraint = allConstraints[0]!; return new AllowlistConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts index 8a0fefb03..a23f696d5 100644 --- a/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts @@ -6,8 +6,8 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, - type TypedConstraintDefinition, createJayveeServices, + isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, @@ -18,6 +18,7 @@ import { import { type AstNode, type AstNodeLocator, + AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -51,10 +52,15 @@ describe('Validation of DenylistConstraintExecutor', () => { document.parseResult.value, 'pipelines@0/blocks@2', ) as BlockDefinition; - const constraint = locator.getAstNode( - document.parseResult.value, - 'constraints@0', - ) as TypedConstraintDefinition; + + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; + expect( + allConstraints.length > 0, + 'No constraint definition found in test file', + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const constraint = allConstraints[0]!; return new DenylistConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts index eb64d0790..b71efda91 100644 --- a/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts @@ -4,10 +4,10 @@ import { type BlockDefinition, - type ExpressionConstraintDefinition, type InternalValueRepresentation, type JayveeServices, createJayveeServices, + isExpressionConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, @@ -18,6 +18,7 @@ import { import { type AstNode, type AstNodeLocator, + AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -51,10 +52,17 @@ describe('Validation of AllowlistConstraintExecutor', () => { document.parseResult.value, 'pipelines@0/blocks@2', ) as BlockDefinition; - const constraint = locator.getAstNode( - document.parseResult.value, - 'constraints@0', - ) as ExpressionConstraintDefinition; + + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allConstraints = [ + ...allElements.filter(isExpressionConstraintDefinition), + ]; + expect( + allConstraints.length > 0, + 'No constraint definition found in test file', + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const constraint = allConstraints[0]!; return new ExpressionConstraintExecutor(constraint).isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts index 63c7a6769..b519c872c 100644 --- a/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts @@ -6,8 +6,8 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, - type TypedConstraintDefinition, createJayveeServices, + isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, @@ -18,6 +18,7 @@ import { import { type AstNode, type AstNodeLocator, + AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -51,10 +52,15 @@ describe('Validation of LengthConstraintExecutor', () => { document.parseResult.value, 'pipelines@0/blocks@2', ) as BlockDefinition; - const constraint = locator.getAstNode( - document.parseResult.value, - 'constraints@0', - ) as TypedConstraintDefinition; + + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; + expect( + allConstraints.length > 0, + 'No constraint definition found in test file', + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const constraint = allConstraints[0]!; return new LengthConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts index 57361710e..4c51c63ac 100644 --- a/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts @@ -6,9 +6,9 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, - type TypedConstraintDefinition, createJayveeServices, initializeWorkspace, + isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, @@ -19,6 +19,7 @@ import { import { type AstNode, type AstNodeLocator, + AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -52,10 +53,15 @@ describe('Validation of RangeConstraintExecutor', () => { document.parseResult.value, 'pipelines@0/blocks@2', ) as BlockDefinition; - const constraint = locator.getAstNode( - document.parseResult.value, - 'constraints@0', - ) as TypedConstraintDefinition; + + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; + expect( + allConstraints.length > 0, + 'No constraint definition found in test file', + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const constraint = allConstraints[0]!; return new RangeConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts index e1b9c764f..df078f4ad 100644 --- a/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts @@ -6,8 +6,8 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, - type TypedConstraintDefinition, createJayveeServices, + isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, @@ -18,6 +18,7 @@ import { import { type AstNode, type AstNodeLocator, + AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -51,10 +52,15 @@ describe('Validation of RegexConstraintExecutor', () => { document.parseResult.value, 'pipelines@0/blocks@2', ) as BlockDefinition; - const constraint = locator.getAstNode( - document.parseResult.value, - 'constraints@0', - ) as TypedConstraintDefinition; + + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; + expect( + allConstraints.length > 0, + 'No constraint definition found in test file', + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const constraint = allConstraints[0]!; return new RegexConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/transforms/transform-executor.spec.ts b/libs/execution/src/lib/transforms/transform-executor.spec.ts index dd1bf462e..4abd01ad5 100644 --- a/libs/execution/src/lib/transforms/transform-executor.spec.ts +++ b/libs/execution/src/lib/transforms/transform-executor.spec.ts @@ -9,8 +9,8 @@ import path from 'node:path'; import { type InternalValueRepresentation, type JayveeServices, - type TransformDefinition, createJayveeServices, + isTransformDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, @@ -22,6 +22,7 @@ import { import { type AstNode, type AstNodeLocator, + AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -77,10 +78,11 @@ describe('Validation of TransformExecutor', () => { const document = await parse(input, { validation: true }); expectNoParserAndLexerErrors(document); - const transform = locator.getAstNode( - document.parseResult.value, - 'transforms@0', - ) as TransformDefinition; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allTransforms = [...allElements.filter(isTransformDefinition)]; + expect(allTransforms.length > 0); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const transform = allTransforms[0]!; const executionContext = getTestExecutionContext( locator, diff --git a/libs/language-server/src/grammar/block-type.langium b/libs/language-server/src/grammar/block-type.langium index 15d1a311c..11281327b 100644 --- a/libs/language-server/src/grammar/block-type.langium +++ b/libs/language-server/src/grammar/block-type.langium @@ -14,12 +14,12 @@ ReferenceableBlockTypeDefinition: BuiltinBlockTypeDefinition | CompositeBlockTypeDefinition; BuiltinBlockTypeDefinition: - (isPublished?='publish')? 'builtin' 'blocktype' name=ID '{' + 'builtin' 'blocktype' name=ID '{' (inputs+=BlockTypeInput | outputs+=BlockTypeOutput | properties+=BlockTypeProperty)* '}'; CompositeBlockTypeDefinition: - (isPublished?='publish')? 'composite' 'blocktype' name=ID '{' + 'composite' 'blocktype' name=ID '{' ( inputs+=BlockTypeInput | outputs+=BlockTypeOutput diff --git a/libs/language-server/src/grammar/constraint.langium b/libs/language-server/src/grammar/constraint.langium index 497c35381..375c2f560 100644 --- a/libs/language-server/src/grammar/constraint.langium +++ b/libs/language-server/src/grammar/constraint.langium @@ -11,13 +11,13 @@ ConstraintDefinition: TypedConstraintDefinition | ExpressionConstraintDefinition; TypedConstraintDefinition: - (isPublished?='publish')? 'constraint' name=ID 'oftype' type=[BuiltinConstrainttypeDefinition] body=PropertyBody; + 'constraint' name=ID 'oftype' type=[BuiltinConstrainttypeDefinition] body=PropertyBody; ExpressionConstraintDefinition: - (isPublished?='publish')? 'constraint' name=ID 'on' valueType=ValueTypeReference ':' expression=Expression ';'; + 'constraint' name=ID 'on' valueType=ValueTypeReference ':' expression=Expression ';'; BuiltinConstrainttypeDefinition: - (isPublished?='publish')? 'builtin' 'constrainttype' name=ID 'on' valueType=ValueTypeReference '{' + 'builtin' 'constrainttype' name=ID 'on' valueType=ValueTypeReference '{' (properties+=ConstrainttypeProperty)* '}'; diff --git a/libs/language-server/src/grammar/io-type.langium b/libs/language-server/src/grammar/io-type.langium index 41542532e..5b962709f 100644 --- a/libs/language-server/src/grammar/io-type.langium +++ b/libs/language-server/src/grammar/io-type.langium @@ -6,4 +6,4 @@ import './expression' import './terminal' IotypeDefinition: - (isPublished?='publish')? 'builtin' 'iotype' name=ID ';'; + 'builtin' 'iotype' name=ID ';'; diff --git a/libs/language-server/src/grammar/main.langium b/libs/language-server/src/grammar/main.langium index 6617443b8..a7d824aa2 100644 --- a/libs/language-server/src/grammar/main.langium +++ b/libs/language-server/src/grammar/main.langium @@ -17,27 +17,20 @@ entry JayveeModel: ( imports+=ImportDefinition | exports+=ExportDefinition + | exportableElements+=ExportableElementDefinition | pipelines+=PipelineDefinition - | valueTypes+=(CustomValuetypeDefinition | BuiltinValuetypeDefinition) - | constraints+=ConstraintDefinition - | transforms+=TransformDefinition - | blockTypes+=ReferenceableBlockTypeDefinition - | constrainttypes+=BuiltinConstrainttypeDefinition - | iotypes+=IotypeDefinition )*; -// When adding an element here, make sure the element has the following prependix in its rule -// (isPublished?='publish')? -// TypeScript lets us then infer that there is a field `isPublished` after using the type guard `isExportableElement`. -ExportableElement: - // Must ref rules where isPublished is defined - // So don't use aggregates like ReferenceableBlockTypeDefinition - // Otherwise it doesn't work to resolve the refenerces +ExportableElementDefinition: + (isPublished?='publish')? ExportableElement; + +ExportableElement: (CustomValuetypeDefinition | BuiltinValuetypeDefinition) - | (TypedConstraintDefinition | ExpressionConstraintDefinition | BuiltinConstrainttypeDefinition) - | TransformDefinition - | (BuiltinBlockTypeDefinition | CompositeBlockTypeDefinition) - | IotypeDefinition; + | ConstraintDefinition + | TransformDefinition + | ReferenceableBlockTypeDefinition + | BuiltinConstrainttypeDefinition + | IotypeDefinition; ExportDefinition: 'publish' element=[ExportableElement] ';'; diff --git a/libs/language-server/src/grammar/transform.langium b/libs/language-server/src/grammar/transform.langium index b8b2e5f11..d58f5a322 100644 --- a/libs/language-server/src/grammar/transform.langium +++ b/libs/language-server/src/grammar/transform.langium @@ -7,7 +7,7 @@ import 'value-type' import 'expression' TransformDefinition: - (isPublished?='publish')? 'transform' name=ID body=TransformBody; + 'transform' name=ID body=TransformBody; TransformBody: '{' (ports+=TransformPortDefinition)* (outputAssignments+=TransformOutputAssignment)* '}'; diff --git a/libs/language-server/src/grammar/value-type.langium b/libs/language-server/src/grammar/value-type.langium index cb9faad2a..f861b1c79 100644 --- a/libs/language-server/src/grammar/value-type.langium +++ b/libs/language-server/src/grammar/value-type.langium @@ -6,12 +6,12 @@ import './expression' import './terminal' BuiltinValuetypeDefinition infers ValuetypeDefinition: - (isPublished?='publish')? isBuiltin?='builtin' 'valuetype' name=ID + isBuiltin?='builtin' 'valuetype' name=ID (genericDefinition=ValuetypeGenericsDefinition)? ';'; CustomValuetypeDefinition infers ValuetypeDefinition: - (isPublished?='publish')? 'valuetype' name=ID + 'valuetype' name=ID (genericDefinition=ValuetypeGenericsDefinition)? 'oftype' type=ValueTypeReference '{' 'constraints' ':' constraints=CollectionLiteral ';' diff --git a/libs/language-server/src/lib/ast/expressions/test-utils.ts b/libs/language-server/src/lib/ast/expressions/test-utils.ts index bc6eff62e..582a3c602 100644 --- a/libs/language-server/src/lib/ast/expressions/test-utils.ts +++ b/libs/language-server/src/lib/ast/expressions/test-utils.ts @@ -2,11 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only +import { AstUtils } from 'langium'; import { NodeFileSystem } from 'langium/node'; +import { expect } from 'vitest'; import { parseHelper } from '../../../test/langium-utils'; import { createJayveeServices } from '../../jayvee-module'; -import { type TransformDefinition } from '../generated/ast'; +import { isTransformDefinition } from '../generated/ast'; import { evaluateExpression } from './evaluate-expression'; import { EvaluationContext } from './evaluation-context'; @@ -34,7 +36,6 @@ export async function executeExpressionTestHelper( ): Promise { const services = createJayveeServices(NodeFileSystem).Jayvee; const parse = parseHelper(services); - const locator = services.workspace.AstNodeLocator; const document = await parse(` transform TestTransform { @@ -45,10 +46,14 @@ export async function executeExpressionTestHelper( } `); - const transform = locator.getAstNode( - document.parseResult.value, - 'transforms@0', - ) as TransformDefinition; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allTransforms = [...allElements.filter(isTransformDefinition)]; + expect( + allTransforms.length !== 0, + `Expected to find exactly 1 transform but found ${allTransforms.length}`, + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const transform = allTransforms[0]!; const evaluationContext = new EvaluationContext( services.RuntimeParameterProvider, diff --git a/libs/language-server/src/lib/ast/model-util.ts b/libs/language-server/src/lib/ast/model-util.ts index dae9011bc..f1a5b04db 100644 --- a/libs/language-server/src/lib/ast/model-util.ts +++ b/libs/language-server/src/lib/ast/model-util.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, type LangiumDocuments } from 'langium'; +import { type AstNode, AstUtils, type LangiumDocuments } from 'langium'; import { type BuiltinBlockTypeDefinition, type BuiltinConstrainttypeDefinition, isBuiltinBlockTypeDefinition, + isBuiltinConstrainttypeDefinition, isJayveeModel, } from './generated/ast'; import { @@ -59,11 +60,10 @@ export function getAllBuiltinBlockTypes( if (!isJayveeModel(parsedDocument)) { throw new Error('Expected parsed document to be a JayveeModel'); } - parsedDocument.blockTypes.forEach((blockTypeDefinition) => { - if (!isBuiltinBlockTypeDefinition(blockTypeDefinition)) { - return; - } - + const allBlockTypes = AstUtils.streamAllContents(parsedDocument).filter( + isBuiltinBlockTypeDefinition, + ); + allBlockTypes.forEach((blockTypeDefinition) => { const wasAlreadyVisited = visitedBuiltinBlockTypeDefinitions.has(blockTypeDefinition); if (wasAlreadyVisited) { @@ -100,7 +100,10 @@ export function getAllBuiltinConstraintTypes( if (!isJayveeModel(parsedDocument)) { throw new Error('Expected parsed document to be a JayveeModel'); } - parsedDocument.constrainttypes.forEach((constraintTypeDefinition) => { + const allConstraintTypes = AstUtils.streamAllContents( + parsedDocument, + ).filter(isBuiltinConstrainttypeDefinition); + allConstraintTypes.forEach((constraintTypeDefinition) => { const wasAlreadyVisited = visitedBuiltinConstraintTypeDefinitions.has( constraintTypeDefinition, ); diff --git a/libs/language-server/src/lib/lsp/jayvee-completion-provider.ts b/libs/language-server/src/lib/lsp/jayvee-completion-provider.ts index c7fdbb768..0aebde26f 100644 --- a/libs/language-server/src/lib/lsp/jayvee-completion-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-completion-provider.ts @@ -7,6 +7,7 @@ import { strict as assert } from 'assert'; import { type AstNode, + AstUtils, type LangiumDocument, type LangiumDocuments, type MaybePromise, @@ -35,6 +36,7 @@ import { isJayveeModel, isPropertyAssignment, isPropertyBody, + isValuetypeDefinition, } from '../ast/generated/ast'; import { getAllBuiltinBlockTypes, @@ -159,7 +161,10 @@ export class JayveeCompletionProvider extends DefaultCompletionProvider { if (!isJayveeModel(parsedDocument)) { throw new Error('Expected parsed document to be a JayveeModel'); } - parsedDocument.valueTypes.forEach((valueTypeDefinition) => { + const allValueTypes = AstUtils.streamAllContents(parsedDocument).filter( + isValuetypeDefinition, + ); + allValueTypes.forEach((valueTypeDefinition) => { const valueType = this.wrapperFactories.ValueType.wrap(valueTypeDefinition); if (valueType !== undefined && valueType.isReferenceableByUser()) { diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 167cc1b8a..8311871ea 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -24,6 +24,7 @@ import { type JayveeModel, isExportDefinition, isExportableElement, + isExportableElementDefinition, isJayveeModel, } from '../ast'; import { getStdLib } from '../builtin-library'; @@ -91,7 +92,11 @@ export class JayveeScopeProvider extends DefaultScopeProvider { const exportedElements: ExportableElement[] = []; for (const node of AstUtils.streamAllContents(model)) { - if (isExportableElement(node) && node.isPublished) { + if (isExportableElementDefinition(node) && node.isPublished) { + assert( + isExportableElement(node), + 'Exported node is not an ExportableElement', + ); exportedElements.push(node); } @@ -112,6 +117,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { exportDefinition: ExportDefinition, ): ExportableElement | undefined { const referenced = exportDefinition.element.ref; + if (referenced === undefined) { return undefined; // Cannot follow reference to original definition } @@ -127,7 +133,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { * Checks whether an exportable @param element is exported (either in definition or via an delayed export definition). */ protected isElementExported(element: ExportableElement): boolean { - if (element.isPublished) { + if (isExportableElementDefinition(element) && element.isPublished) { return true; } @@ -139,8 +145,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { ); const isExported = model.exports.some( - (exportDefinition: ExportDefinition) => - exportDefinition.element.ref === element, + (exportDefinition) => exportDefinition.element.ref === element, ); return isExported; } diff --git a/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts index 5ecf49868..173ad1729 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { - type BuiltinBlockTypeDefinition, type JayveeServices, createJayveeServices, + isBuiltinBlockTypeDefinition, } from '../..'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of BuiltinBlockTypeDefinition', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,25 @@ describe('Validation of BuiltinBlockTypeDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const blockType = locator.getAstNode( - document.parseResult.value, - 'blockTypes@0', - ) as BuiltinBlockTypeDefinition; - - validateBlockTypeDefinition( - blockType, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allBlockTypes = [...allElements.filter(isBuiltinBlockTypeDefinition)]; + expect( + allBlockTypes.length > 0, + 'No builtin block type definition found in test file', ); + + for (const blockType of allBlockTypes) { + validateBlockTypeDefinition( + blockType, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts index dec8260ad..20153cc22 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts @@ -2,20 +2,17 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type PropertyBody, type PropertySpecification, type TypedObjectWrapper, createJayveeServices, + isBlockDefinition, + isPropertyBody, } from '../../..'; import { type ParseHelperOptions, @@ -36,7 +33,6 @@ describe('Validation of block type specific properties', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -48,11 +44,16 @@ describe('Validation of block type specific properties', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const propertyBody = locator.getAstNode( - document.parseResult.value, - 'pipelines@0/blocks@0/body', - ) as PropertyBody; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allPropertyBodies = [ + ...allElements + .filter(isPropertyBody) + .filter((x) => isBlockDefinition(x.$container)), + ]; + expect(allPropertyBodies.length > 0, 'No property body found in test file'); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const propertyBody = allPropertyBodies[0]!; const props = createJayveeValidationProps(validationAcceptorMock, services); const wrapper = props.wrapperFactories.TypedObject.wrap( propertyBody.$container.type, @@ -76,7 +77,7 @@ describe('Validation of block type specific properties', () => { beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts index b1c3783ec..be460e88e 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts @@ -2,18 +2,15 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type PropertyBody, createJayveeServices, + isBlockDefinition, + isPropertyBody, } from '../../..'; import { type ParseHelperOptions, @@ -34,7 +31,6 @@ describe('Validation of block type specific property bodies', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,10 +42,16 @@ describe('Validation of block type specific property bodies', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const propertyBody = locator.getAstNode( - document.parseResult.value, - 'pipelines@0/blocks@0/body', - ) as PropertyBody; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allPropertyBodies = [ + ...allElements + .filter(isPropertyBody) + .filter((x) => isBlockDefinition(x.$container)), + ]; + expect(allPropertyBodies.length > 0, 'No property body found in test file'); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const propertyBody = allPropertyBodies[0]!; const props = createJayveeValidationProps(validationAcceptorMock, services); @@ -64,7 +66,6 @@ describe('Validation of block type specific property bodies', () => { beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/column-id.spec.ts b/libs/language-server/src/lib/validation/checks/column-id.spec.ts index 583e1e606..471ea0616 100644 --- a/libs/language-server/src/lib/validation/checks/column-id.spec.ts +++ b/libs/language-server/src/lib/validation/checks/column-id.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { - type ColumnId, type JayveeServices, createJayveeServices, + isColumnId, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of ColumnId', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,24 @@ describe('Validation of ColumnId', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const columnId = locator.getAstNode( - document.parseResult.value, - 'pipelines@0/blocks@0/body/properties@0/value/columnId', - ) as ColumnId; - - validateColumnId( - columnId, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allColumnIds = [...allElements.filter(isColumnId)]; + expect( + allColumnIds.length > 0, + 'No column id definition found in test file', ); + + for (const columnId of allColumnIds) { + validateColumnId( + columnId, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts index 5a4da6455..79787391e 100644 --- a/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { - type CompositeBlockTypeDefinition, type JayveeServices, createJayveeServices, + isCompositeBlockTypeDefinition, } from '../..'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of CompositeBlockTypeDefinition', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,26 @@ describe('Validation of CompositeBlockTypeDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const blockType = locator.getAstNode( - document.parseResult.value, - 'blockTypes@0', - ) as CompositeBlockTypeDefinition; - - validateCompositeBlockTypeDefinition( - blockType, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allCompositeBlockTypes = [ + ...allElements.filter(isCompositeBlockTypeDefinition), + ]; + expect( + allCompositeBlockTypes.length > 0, + 'No composite block type definition found in test file', ); + + for (const blockType of allCompositeBlockTypes) { + validateCompositeBlockTypeDefinition( + blockType, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts index 89ec0fcc7..5b319f468 100644 --- a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts @@ -2,20 +2,17 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type PropertyBody, type PropertySpecification, type TypedObjectWrapper, createJayveeServices, + isPropertyBody, + isTypedConstraintDefinition, } from '../../..'; import { type ParseHelperOptions, @@ -36,7 +33,6 @@ describe('Validation of constraint type specific properties', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -48,10 +44,16 @@ describe('Validation of constraint type specific properties', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const propertyBody = locator.getAstNode( - document.parseResult.value, - 'constraints@0/body', - ) as PropertyBody; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allPropertyBodies = [ + ...allElements + .filter(isPropertyBody) + .filter((x) => isTypedConstraintDefinition(x.$container)), + ]; + expect(allPropertyBodies.length > 0, 'No property body found in test file'); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const propertyBody = allPropertyBodies[0]!; const props = createJayveeValidationProps(validationAcceptorMock, services); @@ -78,8 +80,6 @@ describe('Validation of constraint type specific properties', () => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; - // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts index 8ef5ab0a3..87a7569a6 100644 --- a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type PropertyBody, createJayveeServices, + isTypedConstraintDefinition, } from '../../..'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of constraint type specific property bodies', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,27 +41,35 @@ describe('Validation of constraint type specific property bodies', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const propertyBody = locator.getAstNode( - document.parseResult.value, - 'constraints@0/body', - ) as PropertyBody; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allTypedConstraints = [ + ...allElements.filter(isTypedConstraintDefinition), + ]; + expect( + allTypedConstraints.length > 0, + 'No typed constraint definition found in test file', + ); - const props = createJayveeValidationProps(validationAcceptorMock, services); + for (const constraint of allTypedConstraints) { + const propertyBody = constraint.body; + const props = createJayveeValidationProps( + validationAcceptorMock, + services, + ); - const wrapper = props.wrapperFactories.TypedObject.wrap( - propertyBody.$container.type, - ); - expect(wrapper).toBeDefined(); + const wrapper = props.wrapperFactories.TypedObject.wrap( + propertyBody.$container.type, + ); + expect(wrapper).toBeDefined(); - checkConstraintTypeSpecificPropertyBody(propertyBody, props); + checkConstraintTypeSpecificPropertyBody(propertyBody, props); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; - // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts index a05aba331..e79879a7e 100644 --- a/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { - type ExpressionConstraintDefinition, type JayveeServices, createJayveeServices, + isExpressionConstraintDefinition, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of ConstraintDefinition (expression syntax)', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,22 +41,27 @@ describe('Validation of ConstraintDefinition (expression syntax)', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const expressionConstraint = - locator.getAstNode( - document.parseResult.value, - 'constraints@0', - ) as ExpressionConstraintDefinition; - - validateExpressionConstraintDefinition( - expressionConstraint, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allExpressionConstraintDefinitions = [ + ...allElements.filter(isExpressionConstraintDefinition), + ]; + expect( + allExpressionConstraintDefinitions.length > 0, + 'No expression constraint definition found in test file', ); + + for (const expressionConstraint of allExpressionConstraintDefinitions) { + validateExpressionConstraintDefinition( + expressionConstraint, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/jayvee-model.ts b/libs/language-server/src/lib/validation/checks/jayvee-model.ts index df136b073..dd945f06f 100644 --- a/libs/language-server/src/lib/validation/checks/jayvee-model.ts +++ b/libs/language-server/src/lib/validation/checks/jayvee-model.ts @@ -2,7 +2,18 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type JayveeModel } from '../../ast/generated/ast'; +import { AstUtils } from 'langium'; + +import { + type JayveeModel, + isBuiltinConstrainttypeDefinition, + isConstraintDefinition, + isIotypeDefinition, + isPipelineDefinition, + isReferenceableBlockTypeDefinition, + isTransformDefinition, + isValuetypeDefinition, +} from '../../ast/generated/ast'; import { type JayveeValidationProps } from '../validation-registry'; import { checkUniqueNames } from '../validation-util'; @@ -10,11 +21,34 @@ export function validateJayveeModel( model: JayveeModel, props: JayveeValidationProps, ): void { - checkUniqueNames(model.pipelines, props.validationContext); - checkUniqueNames(model.transforms, props.validationContext); - checkUniqueNames(model.valueTypes, props.validationContext); - checkUniqueNames(model.constraints, props.validationContext); - checkUniqueNames(model.blockTypes, props.validationContext); - checkUniqueNames(model.constrainttypes, props.validationContext); - checkUniqueNames(model.iotypes, props.validationContext); + const allElements = AstUtils.streamAllContents(model); + + checkUniqueNames( + [...allElements.filter(isPipelineDefinition)], + props.validationContext, + ); + checkUniqueNames( + [...allElements.filter(isTransformDefinition)], + props.validationContext, + ); + checkUniqueNames( + [...allElements.filter(isValuetypeDefinition)], + props.validationContext, + ); + checkUniqueNames( + [...allElements.filter(isConstraintDefinition)], + props.validationContext, + ); + checkUniqueNames( + [...allElements.filter(isReferenceableBlockTypeDefinition)], + props.validationContext, + ); + checkUniqueNames( + [...allElements.filter(isBuiltinConstrainttypeDefinition)], + props.validationContext, + ); + checkUniqueNames( + [...allElements.filter(isIotypeDefinition)], + props.validationContext, + ); } diff --git a/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts b/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts index d884298b6..ca6e62dbd 100644 --- a/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type PipeDefinition, createJayveeServices, + isPipeDefinition, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of PipeDefinition', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,21 @@ describe('Validation of PipeDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const pipe = locator.getAstNode( - document.parseResult.value, - 'pipelines@0/pipes@0', - ) as PipeDefinition; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allPipes = [...allElements.filter(isPipeDefinition)]; + expect(allPipes.length > 0, 'No pipes found in test file'); - validatePipeDefinition( - pipe, - createJayveeValidationProps(validationAcceptorMock, services), - ); + for (const pipe of allPipes) { + validatePipeDefinition( + pipe, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts b/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts index 71cd94718..c89fed6ab 100644 --- a/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type PipelineDefinition, createJayveeServices, + isPipelineDefinition, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of PipelineDefinition', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,22 @@ describe('Validation of PipelineDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const pipeline = locator.getAstNode( - document.parseResult.value, - 'pipelines@0', - ) as PipelineDefinition; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allPipelines = [...allElements.filter(isPipelineDefinition)]; + expect(allPipelines.length > 0, 'No pipes found in test file'); - validatePipelineDefinition( - pipeline, - createJayveeValidationProps(validationAcceptorMock, services), - ); + for (const pipeline of allPipelines) { + validatePipelineDefinition( + pipeline, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts index 08b3e6649..233b2ef1f 100644 --- a/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts @@ -2,20 +2,16 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type PropertyAssignment, - type PropertyBody, type TypedObjectWrapper, createJayveeServices, + isBlockDefinition, + isPropertyBody, } from '../../../lib'; import { type ParseHelperOptions, @@ -36,7 +32,6 @@ describe('Validation of PropertyAssignment', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -48,33 +43,45 @@ describe('Validation of PropertyAssignment', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const propertyBody = locator.getAstNode( - document.parseResult.value, - 'pipelines@0/blocks@0/body', - ) as PropertyBody; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allPropertyBodies = [ + ...allElements + .filter(isPropertyBody) + .filter((x) => isBlockDefinition(x.$container)), + ]; + expect( + allPropertyBodies.length > 0, + 'No block property body found in test file', + ); - const type = propertyBody.$container.type; + for (const propertyBody of allPropertyBodies) { + const type = propertyBody.$container.type; - const props = createJayveeValidationProps(validationAcceptorMock, services); - const wrapper = props.wrapperFactories.TypedObject.wrap(type); - expect(wrapper).toBeDefined(); + const props = createJayveeValidationProps( + validationAcceptorMock, + services, + ); + const wrapper = props.wrapperFactories.TypedObject.wrap(type); + expect(wrapper).toBeDefined(); - const propertyAssignment = locator.getAstNode( - propertyBody, - 'properties@0', - ) as PropertyAssignment; + const propertyAssignments = propertyBody.properties; + expect( + propertyAssignments.length > 0, + 'No property assignment found in test file', + ); - validatePropertyAssignment( - propertyAssignment, - wrapper as TypedObjectWrapper, - props, - ); + validatePropertyAssignment( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + propertyAssignments[0]!, + wrapper as TypedObjectWrapper, + props, + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/property-body.spec.ts index 215d416bd..b742dab4a 100644 --- a/libs/language-server/src/lib/validation/checks/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/property-body.spec.ts @@ -2,18 +2,15 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type PropertyBody, createJayveeServices, + isBlockDefinition, + isPropertyBody, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +31,6 @@ describe('Validation PropertyBody', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +42,29 @@ describe('Validation PropertyBody', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const propertyBody = locator.getAstNode( - document.parseResult.value, - 'pipelines@0/blocks@0/body', - ) as PropertyBody; - - validatePropertyBody( - propertyBody, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allPropertyBodies = [ + ...allElements + .filter(isPropertyBody) + .filter((x) => isBlockDefinition(x.$container)), + ]; + expect( + allPropertyBodies.length > 0, + 'No block property body found in test file', ); + + for (const propertyBody of allPropertyBodies) { + validatePropertyBody( + propertyBody, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/range-literal.spec.ts b/libs/language-server/src/lib/validation/checks/range-literal.spec.ts index 175a5f216..af8a2d404 100644 --- a/libs/language-server/src/lib/validation/checks/range-literal.spec.ts +++ b/libs/language-server/src/lib/validation/checks/range-literal.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type RangeLiteral, createJayveeServices, + isRangeLiteral, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of RangeLiteral', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,22 @@ describe('Validation of RangeLiteral', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const rangeLiteral = locator.getAstNode( - document.parseResult.value, - 'pipelines@0/blocks@0/body/properties@0/value', - ) as RangeLiteral; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allRangeLiterals = [...allElements.filter(isRangeLiteral)]; + expect(allRangeLiterals.length > 0, 'No range literal found in test file'); - validateRangeLiteral( - rangeLiteral, - createJayveeValidationProps(validationAcceptorMock, services), - ); + for (const rangeLiteral of allRangeLiterals) { + validateRangeLiteral( + rangeLiteral, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts b/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts index 81561f705..1f53bf288 100644 --- a/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts +++ b/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type RegexLiteral, createJayveeServices, + isRegexLiteral, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of RegexLiteral', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,22 @@ describe('Validation of RegexLiteral', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const regexLiteral = locator.getAstNode( - document.parseResult.value, - 'pipelines@0/blocks@0/body/properties@0/value', - ) as RegexLiteral; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allRegexLiterals = [...allElements.filter(isRegexLiteral)]; + expect(allRegexLiterals.length > 0, 'No regex literal found in test file'); - validateRegexLiteral( - regexLiteral, - createJayveeValidationProps(validationAcceptorMock, services), - ); + for (const regexLiteral of allRegexLiterals) { + validateRegexLiteral( + regexLiteral, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/transform-body.spec.ts b/libs/language-server/src/lib/validation/checks/transform-body.spec.ts index e1ce55e9f..9ef023cee 100644 --- a/libs/language-server/src/lib/validation/checks/transform-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/transform-body.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type TransformBody, createJayveeServices, + isTransformBody, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of TransformBody', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,25 @@ describe('Validation of TransformBody', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const transformBody = locator.getAstNode( - document.parseResult.value, - 'transforms@0/body', - ) as TransformBody; - - validateTransformBody( - transformBody, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allTransformBodies = [...allElements.filter(isTransformBody)]; + expect( + allTransformBodies.length > 0, + 'No transform body found in test file', ); + + for (const transformBody of allTransformBodies) { + validateTransformBody( + transformBody, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts index 03e0e89c8..4518fc15f 100644 --- a/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type TransformOutputAssignment, createJayveeServices, + isTransformOutputAssignment, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of TransformOutputAssignment', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,22 +41,27 @@ describe('Validation of TransformOutputAssignment', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const transformOutputAssignment = - locator.getAstNode( - document.parseResult.value, - 'transforms@0/body/outputAssignments@0', - ) as TransformOutputAssignment; - - validateTransformOutputAssignment( - transformOutputAssignment, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allOutputAssignments = [ + ...allElements.filter(isTransformOutputAssignment), + ]; + expect( + allOutputAssignments.length > 0, + 'No transform output assignment found in test file', ); + + for (const transformOutputAssignment of allOutputAssignments) { + validateTransformOutputAssignment( + transformOutputAssignment, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts index fbf314963..0ce55187b 100644 --- a/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts @@ -2,19 +2,15 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type TypedConstraintDefinition, createJayveeServices, initializeWorkspace, + isTypedConstraintDefinition, } from '../../../lib'; import { type ParseHelperOptions, @@ -35,7 +31,6 @@ describe('Validation of ConstraintDefinition (typed syntax)', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -47,15 +42,21 @@ describe('Validation of ConstraintDefinition (typed syntax)', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const typedConstraint = locator.getAstNode( - document.parseResult.value, - 'constraints@0', - ) as TypedConstraintDefinition; - - validateTypedConstraintDefinition( - typedConstraint, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allTypedConstraints = [ + ...allElements.filter(isTypedConstraintDefinition), + ]; + expect( + allTypedConstraints.length > 0, + 'No typed constraint definition found in test file', ); + + for (const typedConstraint of allTypedConstraints) { + validateTypedConstraintDefinition( + typedConstraint, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(async () => { @@ -63,7 +64,6 @@ describe('Validation of ConstraintDefinition (typed syntax)', () => { services = createJayveeServices(NodeFileSystem).Jayvee; await initializeWorkspace(services); - locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts index ba6d52964..5b693f3fd 100644 --- a/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { - type AstNode, - type AstNodeLocator, - type LangiumDocument, -} from 'langium'; +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, - type ValuetypeDefinition, createJayveeServices, + isValuetypeDefinition, } from '../../../lib'; import { type ParseHelperOptions, @@ -34,7 +30,6 @@ describe('Validation of ValuetypeDefinition', () => { const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); - let locator: AstNodeLocator; let services: JayveeServices; const readJvTestAsset = readJvTestAssetHelper( @@ -46,21 +41,24 @@ describe('Validation of ValuetypeDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const valueTypeDefinition = locator.getAstNode( - document.parseResult.value, - 'valueTypes@0', - ) as ValuetypeDefinition; - - validateValueTypeDefinition( - valueTypeDefinition, - createJayveeValidationProps(validationAcceptorMock, services), + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allValueTypes = [...allElements.filter(isValuetypeDefinition)]; + expect( + allValueTypes.length > 0, + 'No value type definition found in test file', ); + + for (const valueTypeDefinition of allValueTypes) { + validateValueTypeDefinition( + valueTypeDefinition, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } } beforeAll(() => { // Create language services services = createJayveeServices(NodeFileSystem).Jayvee; - locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); }); diff --git a/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts b/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts index 96f5fd49a..e69209b3f 100644 --- a/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts +++ b/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts @@ -8,6 +8,7 @@ import { strict as assert } from 'assert'; import { type AstNode, type AstNodeLocator, + AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -16,8 +17,9 @@ import { vi } from 'vitest'; import { type JayveeServices, type ValueTypeReference, - type ValuetypeDefinition, createJayveeServices, + isReferenceableBlockTypeDefinition, + isValuetypeDefinition, } from '../..'; import { type ParseHelperOptions, @@ -50,22 +52,22 @@ describe('Validation of ValueTypeReference', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const valueTypeReferences: ValueTypeReference[] = []; + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allValueTypes = [...allElements.filter(isValuetypeDefinition)]; + expect( + allValueTypes.length > 0, + 'No value type definition found in test file', + ); - let valueTypeDefinition: ValuetypeDefinition | undefined; - let i = 0; - do { - valueTypeDefinition = locator.getAstNode( - document.parseResult.value, - `valueTypes@${i}`, - ); - if (valueTypeDefinition !== undefined) { - const valueTypeRef = valueTypeDefinition.type; - assert(valueTypeRef !== undefined); - valueTypeReferences.push(valueTypeRef); - } - ++i; - } while (valueTypeDefinition !== undefined); + const valueTypeReferences: ValueTypeReference[] = []; + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let i = 0; i < allValueTypes.length; ++i) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const valueTypeDefinition = allValueTypes[i]!; + const valueTypeRef = valueTypeDefinition.type; + assert(valueTypeRef !== undefined); + valueTypeReferences.push(valueTypeRef); + } return valueTypeReferences; } @@ -284,10 +286,19 @@ describe('Validation of ValueTypeReference', () => { const document = await parse(text); expectNoParserAndLexerErrors(document); - const valueTypeRef = locator.getAstNode( - document.parseResult.value, - `blockTypes@0/properties@0/valueType`, + + const allElements = AstUtils.streamAllContents(document.parseResult.value); + const allBlockTypes = [ + ...allElements.filter(isReferenceableBlockTypeDefinition), + ]; + expect( + allBlockTypes.length > 0, + 'No block type definition found in test file', ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const blockType = allBlockTypes[0]!; + + const valueTypeRef = blockType.properties[0]?.valueType; assert(valueTypeRef !== undefined); validateValueTypeReference( diff --git a/libs/language-server/src/lib/validation/validation-utils.spec.ts b/libs/language-server/src/lib/validation/validation-utils.spec.ts index 093d3053a..291e42fca 100644 --- a/libs/language-server/src/lib/validation/validation-utils.spec.ts +++ b/libs/language-server/src/lib/validation/validation-utils.spec.ts @@ -87,7 +87,7 @@ describe('Validation of validation-utils', () => { new DefaultOperatorTypeComputerRegistry( valueTypeProvider, new WrapperFactoryProvider( - new DefaultOperatorEvaluatorRegistry(), + new DefaultOperatorEvaluatorRegistry(valueTypeProvider), valueTypeProvider, ), ), @@ -113,7 +113,7 @@ describe('Validation of validation-utils', () => { new DefaultOperatorTypeComputerRegistry( valueTypeProvider, new WrapperFactoryProvider( - new DefaultOperatorEvaluatorRegistry(), + new DefaultOperatorEvaluatorRegistry(valueTypeProvider), valueTypeProvider, ), ), From 5e9aba2e5d60880296c740c8c8241aa0c798988b Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Sun, 26 May 2024 18:01:21 +0200 Subject: [PATCH 12/19] Fix typo in JayveeScopeProvider --- libs/language-server/src/lib/lsp/jayvee-scope-provider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 8311871ea..f6068f3f3 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -33,12 +33,12 @@ import { type JayveeImportResolver } from '../services/import-resolver'; export class JayveeScopeProvider extends DefaultScopeProvider { protected readonly langiumDocuments: LangiumDocuments; - protected readonly importResover: JayveeImportResolver; + protected readonly importResolver: JayveeImportResolver; constructor(services: JayveeServices) { super(services); this.langiumDocuments = services.shared.workspace.LangiumDocuments; - this.importResover = services.ImportResolver; + this.importResolver = services.ImportResolver; } protected override getGlobalScope( @@ -172,7 +172,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { importedUris: Set, ): void { for (const importDefinition of jayveeModel.imports) { - const uri = this.importResover.resolveImportUri(importDefinition); + const uri = this.importResolver.resolveImportUri(importDefinition); if (uri === undefined) { continue; } From 2a1c5e77b8204fb374ab0825f13e60bb287e3a2f Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Sun, 26 May 2024 18:09:26 +0200 Subject: [PATCH 13/19] Document import and export feature --- apps/docs/docs/user/core-concepts.md | 41 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/apps/docs/docs/user/core-concepts.md b/apps/docs/docs/user/core-concepts.md index 4c8cf91a0..1ad04355a 100644 --- a/apps/docs/docs/user/core-concepts.md +++ b/apps/docs/docs/user/core-concepts.md @@ -29,6 +29,7 @@ pipeline CarsPipeline { A _block_ is a processing step within a _pipeline_. It can have a default input and a default output. We differentiate the following types of _blocks_: + - _Extractor blocks_ do not have a default input but only a default output. They model a **data source**. - _Transformator blocks_ have a default input and a default output. They model a **transformation**. - _Loader blocks_ do have a default input but nor a default output. They model a **data sink**. @@ -49,7 +50,7 @@ The availability of property keys and their respective _value types_ is determin block GasReserveHttpExtractor oftype HttpExtractor { // key: value url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; -} +} ``` In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` _block type_. @@ -61,10 +62,11 @@ _Blocks_ can be either defined as part of the language, called _built-in_ or def A _value type_ is the definition of a data type of the processed data. Some _blocks_ use _value types_ to define logic (like filtering or assessing the data type in a data sink). We differentiate the following kinds of _value types_: + - _Built-in value types_ come with the basic version of Jayvee. See [built-in value types](./value-types/built-in-value-types). - _Primitive value types_ can be defined by the user to model domain-specific data types and represent a single value. _Constraints_ can be added to a _primitive value types_. -See [primitive value types](./value-types/primitive-value-types). + See [primitive value types](./value-types/primitive-value-types). - _Compound value types_: UPCOMING. ```jayvee @@ -77,6 +79,7 @@ constraint GasFillLevelRange on decimal: ``` ## Transforms + _Transforms_ are used to transform data from one _value type_ to a different one. For more details, see [transforms](./transforms.md). ```jayvee @@ -86,4 +89,36 @@ transform CelsiusToKelvin { tempKelvin: tempCelsius + 273.15; } -``` \ No newline at end of file +``` + +## Publishing / using model elements + +If you want to use a model element in a different file other than the one you define it, you need to _publish_ and _use_ it. + +1. Publish the element to make it usable in other files. + +```jayvee +// Either publish right away when defining an element +publish constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; + +// Or define first and publish separately +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; + +publish GasFillLevelRange; +``` + +2. Use the element in another file + +```jayvee +// Define from where you want to take elements +use * from './relative/path/to/file.jv'; + +// Then just use them as if they were defined on root level +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; // GasFillLevelRange is defined in another file +} +``` + +Currently, only root-level elements can be published, so elements defined within a pipeline cannot be used in other files. From d7ef7ec84e82349f95b88eb40240a090aa85b6d1 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Sun, 26 May 2024 18:27:18 +0200 Subject: [PATCH 14/19] Add caching to document-based scoping --- .../src/lib/lsp/jayvee-scope-provider.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index f6068f3f3..691985506 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -9,6 +9,7 @@ import { type AstNodeDescription, AstUtils, DefaultScopeProvider, + DocumentCache, EMPTY_SCOPE, type LangiumDocument, type LangiumDocuments, @@ -35,10 +36,16 @@ export class JayveeScopeProvider extends DefaultScopeProvider { protected readonly langiumDocuments: LangiumDocuments; protected readonly importResolver: JayveeImportResolver; + protected readonly availableElementsPerDocumentCache: DocumentCache< + string, + ExportableElement[] + >; + constructor(services: JayveeServices) { super(services); this.langiumDocuments = services.shared.workspace.LangiumDocuments; this.importResolver = services.ImportResolver; + this.availableElementsPerDocumentCache = new DocumentCache(services.shared); } protected override getGlobalScope( @@ -69,7 +76,11 @@ export class JayveeScopeProvider extends DefaultScopeProvider { continue; } - const publishedElements = this.getExportedElements(importedDocument); + const publishedElements = this.availableElementsPerDocumentCache.get( + importedDocument.uri, + 'exports', // we only need one key here as it is on document basis + () => this.getExportedElements(importedDocument), + ); importedElements.push( ...publishedElements.map((e) => this.descriptions.createDescription(e, e.name), From a197d1a0db1161ba618dd19562dbb1c6d7fd4fc1 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Sun, 26 May 2024 18:51:35 +0200 Subject: [PATCH 15/19] Remove outdated TODO comment --- libs/language-server/src/lib/lsp/jayvee-scope-provider.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts index 691985506..81fb7f3e7 100644 --- a/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts +++ b/libs/language-server/src/lib/lsp/jayvee-scope-provider.ts @@ -39,8 +39,7 @@ export class JayveeScopeProvider extends DefaultScopeProvider { protected readonly availableElementsPerDocumentCache: DocumentCache< string, ExportableElement[] - >; - + >; // DocumentCache becomes invalidated as soon the corresponding document is updated constructor(services: JayveeServices) { super(services); this.langiumDocuments = services.shared.workspace.LangiumDocuments; @@ -60,8 +59,6 @@ export class JayveeScopeProvider extends DefaultScopeProvider { return EMPTY_SCOPE; } - // TODO: add caching to avoid performance issues - const importedUris = new Set(); this.gatherImports(jayveeModel, importedUris); this.gatherBuiltins(importedUris); From 3db1d0ea9fced4a107c23d1fe0e0562b2ed523eb Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Tue, 28 May 2024 09:46:33 +0200 Subject: [PATCH 16/19] Fix expect in executeExpressionTestHelper --- libs/language-server/src/lib/ast/expressions/test-utils.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libs/language-server/src/lib/ast/expressions/test-utils.ts b/libs/language-server/src/lib/ast/expressions/test-utils.ts index 582a3c602..1bcc9ccb9 100644 --- a/libs/language-server/src/lib/ast/expressions/test-utils.ts +++ b/libs/language-server/src/lib/ast/expressions/test-utils.ts @@ -48,10 +48,7 @@ export async function executeExpressionTestHelper( const allElements = AstUtils.streamAllContents(document.parseResult.value); const allTransforms = [...allElements.filter(isTransformDefinition)]; - expect( - allTransforms.length !== 0, - `Expected to find exactly 1 transform but found ${allTransforms.length}`, - ); + expect(allTransforms.length).toBe(1); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const transform = allTransforms[0]!; From 279300dfe94cc4499d7c3ffc0e23c430988caff5 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Tue, 28 May 2024 10:24:06 +0200 Subject: [PATCH 17/19] Pull out test helper function extractTestElements --- .../language-server/src/lib/ast/test-utils.ts | 21 +++++++++++++++ .../checks/block-type-definition.spec.ts | 12 ++++----- .../property-assignment.spec.ts | 19 +++++++------- .../block-type-specific/property-body.spec.ts | 18 ++++++------- .../lib/validation/checks/column-id.spec.ts | 11 ++++---- .../composite-block-type-definition.spec.ts | 15 +++++------ .../property-assignment.spec.ts | 18 ++++++------- .../property-body.spec.ts | 14 +++++----- .../expression-constraint-definition.spec.ts | 15 +++++------ .../validation/checks/pipe-definition.spec.ts | 10 ++++--- .../checks/pipeline-definition.spec.ts | 11 +++++--- .../checks/property-assignment.spec.ts | 17 +++++------- .../validation/checks/property-body.spec.ts | 17 +++++------- .../validation/checks/range-literal.spec.ts | 11 +++++--- .../validation/checks/regex-literal.spec.ts | 11 +++++--- .../validation/checks/transform-body.spec.ts | 12 ++++----- .../transform-output-assignment.spec.ts | 14 +++++----- .../typed-constraint-definition.spec.ts | 14 +++++----- .../checks/value-type-definition.spec.ts | 12 ++++----- .../checks/value-type-reference.spec.ts | 26 ++++++++----------- 20 files changed, 153 insertions(+), 145 deletions(-) create mode 100644 libs/language-server/src/lib/ast/test-utils.ts diff --git a/libs/language-server/src/lib/ast/test-utils.ts b/libs/language-server/src/lib/ast/test-utils.ts new file mode 100644 index 000000000..7e8aaf3c3 --- /dev/null +++ b/libs/language-server/src/lib/ast/test-utils.ts @@ -0,0 +1,21 @@ +import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { expect } from 'vitest'; + +import { isJayveeModel } from './generated/ast'; + +/** + * Extract all elements that comply with the given filter function. + * Assures that the document contains a JayveeModel and that there is at least one extracted element. + */ +export function extractTestElements( + document: LangiumDocument, + filterFn: (node: AstNode) => node is E, +): E[] { + const model = document.parseResult.value; + expect(isJayveeModel(model)).toBe(true); + + const allElements = AstUtils.streamAllContents(model); + const allSelected = [...allElements.filter(filterFn)]; + expect(allSelected.length).toBeGreaterThan(0); + return allSelected; +} diff --git a/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts index 173ad1729..794ab4cb0 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts @@ -2,11 +2,12 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { + type BuiltinBlockTypeDefinition, type JayveeServices, createJayveeServices, isBuiltinBlockTypeDefinition, @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateBlockTypeDefinition } from './block-type-definition'; @@ -41,11 +43,9 @@ describe('Validation of BuiltinBlockTypeDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allBlockTypes = [...allElements.filter(isBuiltinBlockTypeDefinition)]; - expect( - allBlockTypes.length > 0, - 'No builtin block type definition found in test file', + const allBlockTypes = extractTestElements( + document, + (x): x is BuiltinBlockTypeDefinition => isBuiltinBlockTypeDefinition(x), ); for (const blockType of allBlockTypes) { diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts index 20153cc22..89fd8bedb 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type PropertyBody, type PropertySpecification, type TypedObjectWrapper, createJayveeServices, @@ -22,6 +23,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../../test'; +import { extractTestElements } from '../../../ast/test-utils'; import { checkBlockTypeSpecificProperties } from './property-assignment'; @@ -44,16 +46,13 @@ describe('Validation of block type specific properties', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allPropertyBodies = [ - ...allElements - .filter(isPropertyBody) - .filter((x) => isBlockDefinition(x.$container)), - ]; - expect(allPropertyBodies.length > 0, 'No property body found in test file'); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const propertyBody = allPropertyBodies[0]!; + const propertyBody = extractTestElements( + document, + (x): x is PropertyBody => + isPropertyBody(x) && isBlockDefinition(x.$container), + )[0]!; + const props = createJayveeValidationProps(validationAcceptorMock, services); const wrapper = props.wrapperFactories.TypedObject.wrap( propertyBody.$container.type, diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts index be460e88e..9ce19c5bd 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type PropertyBody, createJayveeServices, isBlockDefinition, isPropertyBody, @@ -20,6 +21,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../../test'; +import { extractTestElements } from '../../../ast/test-utils'; import { checkBlockTypeSpecificPropertyBody } from './property-body'; @@ -42,16 +44,12 @@ describe('Validation of block type specific property bodies', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allPropertyBodies = [ - ...allElements - .filter(isPropertyBody) - .filter((x) => isBlockDefinition(x.$container)), - ]; - expect(allPropertyBodies.length > 0, 'No property body found in test file'); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const propertyBody = allPropertyBodies[0]!; + const propertyBody = extractTestElements( + document, + (x): x is PropertyBody => + isPropertyBody(x) && isBlockDefinition(x.$container), + )[0]!; const props = createJayveeValidationProps(validationAcceptorMock, services); diff --git a/libs/language-server/src/lib/validation/checks/column-id.spec.ts b/libs/language-server/src/lib/validation/checks/column-id.spec.ts index 471ea0616..7004c7127 100644 --- a/libs/language-server/src/lib/validation/checks/column-id.spec.ts +++ b/libs/language-server/src/lib/validation/checks/column-id.spec.ts @@ -2,11 +2,12 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { + type ColumnId, type JayveeServices, createJayveeServices, isColumnId, @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateColumnId } from './column-id'; @@ -41,11 +43,8 @@ describe('Validation of ColumnId', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allColumnIds = [...allElements.filter(isColumnId)]; - expect( - allColumnIds.length > 0, - 'No column id definition found in test file', + const allColumnIds = extractTestElements(document, (x): x is ColumnId => + isColumnId(x), ); for (const columnId of allColumnIds) { diff --git a/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts index 79787391e..18f48fbdf 100644 --- a/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts @@ -2,11 +2,12 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { + type CompositeBlockTypeDefinition, type JayveeServices, createJayveeServices, isCompositeBlockTypeDefinition, @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateCompositeBlockTypeDefinition } from './composite-block-type-definition'; @@ -41,13 +43,10 @@ describe('Validation of CompositeBlockTypeDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allCompositeBlockTypes = [ - ...allElements.filter(isCompositeBlockTypeDefinition), - ]; - expect( - allCompositeBlockTypes.length > 0, - 'No composite block type definition found in test file', + const allCompositeBlockTypes = extractTestElements( + document, + (x): x is CompositeBlockTypeDefinition => + isCompositeBlockTypeDefinition(x), ); for (const blockType of allCompositeBlockTypes) { diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts index 5b319f468..a3f708622 100644 --- a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type PropertyBody, type PropertySpecification, type TypedObjectWrapper, createJayveeServices, @@ -22,6 +23,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../../test'; +import { extractTestElements } from '../../../ast/test-utils'; import { checkConstraintTypeSpecificProperties } from './property-assignment'; @@ -44,16 +46,12 @@ describe('Validation of constraint type specific properties', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allPropertyBodies = [ - ...allElements - .filter(isPropertyBody) - .filter((x) => isTypedConstraintDefinition(x.$container)), - ]; - expect(allPropertyBodies.length > 0, 'No property body found in test file'); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const propertyBody = allPropertyBodies[0]!; + const propertyBody = extractTestElements( + document, + (x): x is PropertyBody => + isPropertyBody(x) && isTypedConstraintDefinition(x.$container), + )[0]!; const props = createJayveeValidationProps(validationAcceptorMock, services); diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts index 87a7569a6..10b9501e4 100644 --- a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type TypedConstraintDefinition, createJayveeServices, isTypedConstraintDefinition, } from '../../..'; @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../../test'; +import { extractTestElements } from '../../../ast/test-utils'; import { checkConstraintTypeSpecificPropertyBody } from './property-body'; @@ -41,13 +43,9 @@ describe('Validation of constraint type specific property bodies', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allTypedConstraints = [ - ...allElements.filter(isTypedConstraintDefinition), - ]; - expect( - allTypedConstraints.length > 0, - 'No typed constraint definition found in test file', + const allTypedConstraints = extractTestElements( + document, + (x): x is TypedConstraintDefinition => isTypedConstraintDefinition(x), ); for (const constraint of allTypedConstraints) { diff --git a/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts index e79879a7e..d8b144feb 100644 --- a/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts @@ -2,11 +2,12 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { + type ExpressionConstraintDefinition, type JayveeServices, createJayveeServices, isExpressionConstraintDefinition, @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateExpressionConstraintDefinition } from './expression-constraint-definition'; @@ -41,13 +43,10 @@ describe('Validation of ConstraintDefinition (expression syntax)', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allExpressionConstraintDefinitions = [ - ...allElements.filter(isExpressionConstraintDefinition), - ]; - expect( - allExpressionConstraintDefinitions.length > 0, - 'No expression constraint definition found in test file', + const allExpressionConstraintDefinitions = extractTestElements( + document, + (x): x is ExpressionConstraintDefinition => + isExpressionConstraintDefinition(x), ); for (const expressionConstraint of allExpressionConstraintDefinitions) { diff --git a/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts b/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts index ca6e62dbd..d4ed4384d 100644 --- a/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type PipeDefinition, createJayveeServices, isPipeDefinition, } from '../../../lib'; @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validatePipeDefinition } from './pipe-definition'; @@ -41,9 +43,9 @@ describe('Validation of PipeDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allPipes = [...allElements.filter(isPipeDefinition)]; - expect(allPipes.length > 0, 'No pipes found in test file'); + const allPipes = extractTestElements(document, (x): x is PipeDefinition => + isPipeDefinition(x), + ); for (const pipe of allPipes) { validatePipeDefinition( diff --git a/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts b/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts index c89fed6ab..4969dfa49 100644 --- a/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type PipelineDefinition, createJayveeServices, isPipelineDefinition, } from '../../../lib'; @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validatePipelineDefinition } from './pipeline-definition'; @@ -41,9 +43,10 @@ describe('Validation of PipelineDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allPipelines = [...allElements.filter(isPipelineDefinition)]; - expect(allPipelines.length > 0, 'No pipes found in test file'); + const allPipelines = extractTestElements( + document, + (x): x is PipelineDefinition => isPipelineDefinition(x), + ); for (const pipeline of allPipelines) { validatePipelineDefinition( diff --git a/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts index 233b2ef1f..0e8b3f0a4 100644 --- a/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type PropertyBody, type TypedObjectWrapper, createJayveeServices, isBlockDefinition, @@ -21,6 +22,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validatePropertyAssignment } from './property-assignment'; @@ -43,15 +45,10 @@ describe('Validation of PropertyAssignment', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allPropertyBodies = [ - ...allElements - .filter(isPropertyBody) - .filter((x) => isBlockDefinition(x.$container)), - ]; - expect( - allPropertyBodies.length > 0, - 'No block property body found in test file', + const allPropertyBodies = extractTestElements( + document, + (x): x is PropertyBody => + isPropertyBody(x) && isBlockDefinition(x.$container), ); for (const propertyBody of allPropertyBodies) { diff --git a/libs/language-server/src/lib/validation/checks/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/property-body.spec.ts index b742dab4a..2f3136444 100644 --- a/libs/language-server/src/lib/validation/checks/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/property-body.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type PropertyBody, createJayveeServices, isBlockDefinition, isPropertyBody, @@ -20,6 +21,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validatePropertyBody } from './property-body'; @@ -42,15 +44,10 @@ describe('Validation PropertyBody', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allPropertyBodies = [ - ...allElements - .filter(isPropertyBody) - .filter((x) => isBlockDefinition(x.$container)), - ]; - expect( - allPropertyBodies.length > 0, - 'No block property body found in test file', + const allPropertyBodies = extractTestElements( + document, + (x): x is PropertyBody => + isPropertyBody(x) && isBlockDefinition(x.$container), ); for (const propertyBody of allPropertyBodies) { diff --git a/libs/language-server/src/lib/validation/checks/range-literal.spec.ts b/libs/language-server/src/lib/validation/checks/range-literal.spec.ts index af8a2d404..49646ff12 100644 --- a/libs/language-server/src/lib/validation/checks/range-literal.spec.ts +++ b/libs/language-server/src/lib/validation/checks/range-literal.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type RangeLiteral, createJayveeServices, isRangeLiteral, } from '../../../lib'; @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateRangeLiteral } from './range-literal'; @@ -41,9 +43,10 @@ describe('Validation of RangeLiteral', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allRangeLiterals = [...allElements.filter(isRangeLiteral)]; - expect(allRangeLiterals.length > 0, 'No range literal found in test file'); + const allRangeLiterals = extractTestElements( + document, + (x): x is RangeLiteral => isRangeLiteral(x), + ); for (const rangeLiteral of allRangeLiterals) { validateRangeLiteral( diff --git a/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts b/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts index 1f53bf288..8acb801ec 100644 --- a/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts +++ b/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type RegexLiteral, createJayveeServices, isRegexLiteral, } from '../../../lib'; @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateRegexLiteral } from './regex-literal'; @@ -41,9 +43,10 @@ describe('Validation of RegexLiteral', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allRegexLiterals = [...allElements.filter(isRegexLiteral)]; - expect(allRegexLiterals.length > 0, 'No regex literal found in test file'); + const allRegexLiterals = extractTestElements( + document, + (x): x is RegexLiteral => isRegexLiteral(x), + ); for (const regexLiteral of allRegexLiterals) { validateRegexLiteral( diff --git a/libs/language-server/src/lib/validation/checks/transform-body.spec.ts b/libs/language-server/src/lib/validation/checks/transform-body.spec.ts index 9ef023cee..960206f74 100644 --- a/libs/language-server/src/lib/validation/checks/transform-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/transform-body.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type TransformBody, createJayveeServices, isTransformBody, } from '../../../lib'; @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateTransformBody } from './transform-body'; @@ -41,11 +43,9 @@ describe('Validation of TransformBody', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allTransformBodies = [...allElements.filter(isTransformBody)]; - expect( - allTransformBodies.length > 0, - 'No transform body found in test file', + const allTransformBodies = extractTestElements( + document, + (x): x is TransformBody => isTransformBody(x), ); for (const transformBody of allTransformBodies) { diff --git a/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts index 4518fc15f..5dabb0ab3 100644 --- a/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type TransformOutputAssignment, createJayveeServices, isTransformOutputAssignment, } from '../../../lib'; @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateTransformOutputAssignment } from './transform-output-assigment'; @@ -41,13 +43,9 @@ describe('Validation of TransformOutputAssignment', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allOutputAssignments = [ - ...allElements.filter(isTransformOutputAssignment), - ]; - expect( - allOutputAssignments.length > 0, - 'No transform output assignment found in test file', + const allOutputAssignments = extractTestElements( + document, + (x): x is TransformOutputAssignment => isTransformOutputAssignment(x), ); for (const transformOutputAssignment of allOutputAssignments) { diff --git a/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts index 0ce55187b..b8ae053eb 100644 --- a/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type TypedConstraintDefinition, createJayveeServices, initializeWorkspace, isTypedConstraintDefinition, @@ -20,6 +21,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateTypedConstraintDefinition } from './typed-constraint-definition'; @@ -42,13 +44,9 @@ describe('Validation of ConstraintDefinition (typed syntax)', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allTypedConstraints = [ - ...allElements.filter(isTypedConstraintDefinition), - ]; - expect( - allTypedConstraints.length > 0, - 'No typed constraint definition found in test file', + const allTypedConstraints = extractTestElements( + document, + (x): x is TypedConstraintDefinition => isTypedConstraintDefinition(x), ); for (const typedConstraint of allTypedConstraints) { diff --git a/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts index 5b693f3fd..391a3ac42 100644 --- a/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts @@ -2,12 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; +import { type AstNode, type LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { vi } from 'vitest'; import { type JayveeServices, + type ValuetypeDefinition, createJayveeServices, isValuetypeDefinition, } from '../../../lib'; @@ -19,6 +20,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateValueTypeDefinition } from './value-type-definition'; @@ -41,11 +43,9 @@ describe('Validation of ValuetypeDefinition', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allValueTypes = [...allElements.filter(isValuetypeDefinition)]; - expect( - allValueTypes.length > 0, - 'No value type definition found in test file', + const allValueTypes = extractTestElements( + document, + (x): x is ValuetypeDefinition => isValuetypeDefinition(x), ); for (const valueTypeDefinition of allValueTypes) { diff --git a/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts b/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts index e69209b3f..570e81ae1 100644 --- a/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts +++ b/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts @@ -8,7 +8,6 @@ import { strict as assert } from 'assert'; import { type AstNode, type AstNodeLocator, - AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -16,7 +15,9 @@ import { vi } from 'vitest'; import { type JayveeServices, + type ReferenceableBlockTypeDefinition, type ValueTypeReference, + type ValuetypeDefinition, createJayveeServices, isReferenceableBlockTypeDefinition, isValuetypeDefinition, @@ -29,6 +30,7 @@ import { readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; +import { extractTestElements } from '../../ast/test-utils'; import { validateValueTypeReference } from './value-type-reference'; @@ -52,11 +54,9 @@ describe('Validation of ValueTypeReference', () => { const document = await parse(input); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allValueTypes = [...allElements.filter(isValuetypeDefinition)]; - expect( - allValueTypes.length > 0, - 'No value type definition found in test file', + const allValueTypes = extractTestElements( + document, + (x): x is ValuetypeDefinition => isValuetypeDefinition(x), ); const valueTypeReferences: ValueTypeReference[] = []; @@ -287,16 +287,12 @@ describe('Validation of ValueTypeReference', () => { const document = await parse(text); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allBlockTypes = [ - ...allElements.filter(isReferenceableBlockTypeDefinition), - ]; - expect( - allBlockTypes.length > 0, - 'No block type definition found in test file', - ); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const blockType = allBlockTypes[0]!; + const blockType = extractTestElements( + document, + (x): x is ReferenceableBlockTypeDefinition => + isReferenceableBlockTypeDefinition(x), + )[0]!; const valueTypeRef = blockType.properties[0]?.valueType; assert(valueTypeRef !== undefined); From 99dcda277538914cf19476be38efc9b8e0bcab0b Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Tue, 28 May 2024 10:34:00 +0200 Subject: [PATCH 18/19] Move extractTestElements to @language-server/test --- .../lib/validation/checks/block-type-definition.spec.ts | 2 +- .../checks/block-type-specific/property-assignment.spec.ts | 2 +- .../checks/block-type-specific/property-body.spec.ts | 2 +- .../src/lib/validation/checks/column-id.spec.ts | 2 +- .../checks/composite-block-type-definition.spec.ts | 2 +- .../constrainttype-specific/property-assignment.spec.ts | 2 +- .../checks/constrainttype-specific/property-body.spec.ts | 2 +- .../checks/expression-constraint-definition.spec.ts | 2 +- .../src/lib/validation/checks/pipe-definition.spec.ts | 2 +- .../src/lib/validation/checks/pipeline-definition.spec.ts | 2 +- .../src/lib/validation/checks/property-assignment.spec.ts | 2 +- .../src/lib/validation/checks/property-body.spec.ts | 2 +- .../src/lib/validation/checks/range-literal.spec.ts | 2 +- .../src/lib/validation/checks/regex-literal.spec.ts | 2 +- .../src/lib/validation/checks/transform-body.spec.ts | 2 +- .../validation/checks/transform-output-assignment.spec.ts | 2 +- .../validation/checks/typed-constraint-definition.spec.ts | 2 +- .../lib/validation/checks/value-type-definition.spec.ts | 2 +- .../src/lib/validation/checks/value-type-reference.spec.ts | 2 +- .../src/{lib/ast/test-utils.ts => test/ast-utils.ts} | 7 +++++-- libs/language-server/src/test/index.ts | 1 + 21 files changed, 25 insertions(+), 21 deletions(-) rename libs/language-server/src/{lib/ast/test-utils.ts => test/ast-utils.ts} (78%) diff --git a/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts index 794ab4cb0..e8b36df70 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateBlockTypeDefinition } from './block-type-definition'; diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts index 89fd8bedb..5fd7643ce 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts @@ -19,11 +19,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../../test'; -import { extractTestElements } from '../../../ast/test-utils'; import { checkBlockTypeSpecificProperties } from './property-assignment'; diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts index 9ce19c5bd..921e841a9 100644 --- a/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts @@ -17,11 +17,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../../test'; -import { extractTestElements } from '../../../ast/test-utils'; import { checkBlockTypeSpecificPropertyBody } from './property-body'; diff --git a/libs/language-server/src/lib/validation/checks/column-id.spec.ts b/libs/language-server/src/lib/validation/checks/column-id.spec.ts index 7004c7127..70fd6f652 100644 --- a/libs/language-server/src/lib/validation/checks/column-id.spec.ts +++ b/libs/language-server/src/lib/validation/checks/column-id.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateColumnId } from './column-id'; diff --git a/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts index 18f48fbdf..10715980b 100644 --- a/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateCompositeBlockTypeDefinition } from './composite-block-type-definition'; diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts index a3f708622..3bb014d06 100644 --- a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts @@ -19,11 +19,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../../test'; -import { extractTestElements } from '../../../ast/test-utils'; import { checkConstraintTypeSpecificProperties } from './property-assignment'; diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts index 10b9501e4..0c59a693d 100644 --- a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../../test'; -import { extractTestElements } from '../../../ast/test-utils'; import { checkConstraintTypeSpecificPropertyBody } from './property-body'; diff --git a/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts index d8b144feb..29a3ce454 100644 --- a/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateExpressionConstraintDefinition } from './expression-constraint-definition'; diff --git a/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts b/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts index d4ed4384d..f93a3bcc9 100644 --- a/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validatePipeDefinition } from './pipe-definition'; diff --git a/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts b/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts index 4969dfa49..4538d309e 100644 --- a/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validatePipelineDefinition } from './pipeline-definition'; diff --git a/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts index 0e8b3f0a4..320f4625f 100644 --- a/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts @@ -18,11 +18,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validatePropertyAssignment } from './property-assignment'; diff --git a/libs/language-server/src/lib/validation/checks/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/property-body.spec.ts index 2f3136444..446c64420 100644 --- a/libs/language-server/src/lib/validation/checks/property-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/property-body.spec.ts @@ -17,11 +17,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validatePropertyBody } from './property-body'; diff --git a/libs/language-server/src/lib/validation/checks/range-literal.spec.ts b/libs/language-server/src/lib/validation/checks/range-literal.spec.ts index 49646ff12..68713b9af 100644 --- a/libs/language-server/src/lib/validation/checks/range-literal.spec.ts +++ b/libs/language-server/src/lib/validation/checks/range-literal.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateRangeLiteral } from './range-literal'; diff --git a/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts b/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts index 8acb801ec..84b10eac5 100644 --- a/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts +++ b/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateRegexLiteral } from './regex-literal'; diff --git a/libs/language-server/src/lib/validation/checks/transform-body.spec.ts b/libs/language-server/src/lib/validation/checks/transform-body.spec.ts index 960206f74..f7332ef96 100644 --- a/libs/language-server/src/lib/validation/checks/transform-body.spec.ts +++ b/libs/language-server/src/lib/validation/checks/transform-body.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateTransformBody } from './transform-body'; diff --git a/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts index 5dabb0ab3..064d6e91c 100644 --- a/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts +++ b/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateTransformOutputAssignment } from './transform-output-assigment'; diff --git a/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts index b8ae053eb..2391cc23e 100644 --- a/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts @@ -17,11 +17,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateTypedConstraintDefinition } from './typed-constraint-definition'; diff --git a/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts index 391a3ac42..067e42df1 100644 --- a/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts +++ b/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts @@ -16,11 +16,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateValueTypeDefinition } from './value-type-definition'; diff --git a/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts b/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts index 570e81ae1..16e8370ff 100644 --- a/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts +++ b/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts @@ -26,11 +26,11 @@ import { type ParseHelperOptions, createJayveeValidationProps, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, validationAcceptorMockImpl, } from '../../../test'; -import { extractTestElements } from '../../ast/test-utils'; import { validateValueTypeReference } from './value-type-reference'; diff --git a/libs/language-server/src/lib/ast/test-utils.ts b/libs/language-server/src/test/ast-utils.ts similarity index 78% rename from libs/language-server/src/lib/ast/test-utils.ts rename to libs/language-server/src/test/ast-utils.ts index 7e8aaf3c3..621f697c3 100644 --- a/libs/language-server/src/lib/ast/test-utils.ts +++ b/libs/language-server/src/test/ast-utils.ts @@ -1,7 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + import { type AstNode, AstUtils, type LangiumDocument } from 'langium'; -import { expect } from 'vitest'; -import { isJayveeModel } from './generated/ast'; +import { isJayveeModel } from '../lib/ast/generated/ast'; /** * Extract all elements that comply with the given filter function. diff --git a/libs/language-server/src/test/index.ts b/libs/language-server/src/test/index.ts index fd181e695..adb4e2b86 100644 --- a/libs/language-server/src/test/index.ts +++ b/libs/language-server/src/test/index.ts @@ -4,3 +4,4 @@ export * from './langium-utils'; export * from './utils'; +export * from './ast-utils'; From 5a5ea866e4e322bcb1b04f15a17f99ba9f0309cf Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Tue, 28 May 2024 10:39:10 +0200 Subject: [PATCH 19/19] Use extractTestElements in execution lib --- .../allowlist-constraint-executor.spec.ts | 14 ++++++-------- .../denylist-constraint-executor.spec.ts | 14 ++++++-------- .../expression-constraint-executor.spec.ts | 17 +++++++---------- .../length-constraint-executor.spec.ts | 14 ++++++-------- .../executors/range-constraint-executor.spec.ts | 14 ++++++-------- .../executors/regex-constraint-executor.spec.ts | 14 ++++++-------- .../lib/transforms/transform-executor.spec.ts | 11 ++++++----- 7 files changed, 43 insertions(+), 55 deletions(-) diff --git a/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts index d8a5d6d56..90fd67a4b 100644 --- a/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts @@ -6,9 +6,11 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, + type TypedConstraintDefinition, createJayveeServices, isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; +import { extractTestElements } from '@jvalue/jayvee-language-server/test'; import { type ParseHelperOptions, expectNoParserAndLexerErrors, @@ -18,7 +20,6 @@ import { import { type AstNode, type AstNodeLocator, - AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -53,14 +54,11 @@ describe('Validation of AllowlistConstraintExecutor', () => { 'pipelines@0/blocks@2', ) as BlockDefinition; - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; - expect( - allConstraints.length > 0, - 'No constraint definition found in test file', - ); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const constraint = allConstraints[0]!; + const constraint = extractTestElements( + document, + (x): x is TypedConstraintDefinition => isTypedConstraintDefinition(x), + )[0]!; return new AllowlistConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts index a23f696d5..686bb213f 100644 --- a/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts @@ -6,19 +6,20 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, + type TypedConstraintDefinition, createJayveeServices, isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; import { type AstNode, type AstNodeLocator, - AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -53,14 +54,11 @@ describe('Validation of DenylistConstraintExecutor', () => { 'pipelines@0/blocks@2', ) as BlockDefinition; - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; - expect( - allConstraints.length > 0, - 'No constraint definition found in test file', - ); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const constraint = allConstraints[0]!; + const constraint = extractTestElements( + document, + (x): x is TypedConstraintDefinition => isTypedConstraintDefinition(x), + )[0]!; return new DenylistConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts index b71efda91..c23c20f12 100644 --- a/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts @@ -4,6 +4,7 @@ import { type BlockDefinition, + type ExpressionConstraintDefinition, type InternalValueRepresentation, type JayveeServices, createJayveeServices, @@ -12,13 +13,13 @@ import { import { type ParseHelperOptions, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; import { type AstNode, type AstNodeLocator, - AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -53,16 +54,12 @@ describe('Validation of AllowlistConstraintExecutor', () => { 'pipelines@0/blocks@2', ) as BlockDefinition; - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allConstraints = [ - ...allElements.filter(isExpressionConstraintDefinition), - ]; - expect( - allConstraints.length > 0, - 'No constraint definition found in test file', - ); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const constraint = allConstraints[0]!; + const constraint = extractTestElements( + document, + (x): x is ExpressionConstraintDefinition => + isExpressionConstraintDefinition(x), + )[0]!; return new ExpressionConstraintExecutor(constraint).isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts index b519c872c..2e70b3868 100644 --- a/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts @@ -6,19 +6,20 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, + type TypedConstraintDefinition, createJayveeServices, isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; import { type AstNode, type AstNodeLocator, - AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -53,14 +54,11 @@ describe('Validation of LengthConstraintExecutor', () => { 'pipelines@0/blocks@2', ) as BlockDefinition; - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; - expect( - allConstraints.length > 0, - 'No constraint definition found in test file', - ); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const constraint = allConstraints[0]!; + const constraint = extractTestElements( + document, + (x): x is TypedConstraintDefinition => isTypedConstraintDefinition(x), + )[0]!; return new LengthConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts index 4c51c63ac..e05450143 100644 --- a/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts @@ -6,6 +6,7 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, + type TypedConstraintDefinition, createJayveeServices, initializeWorkspace, isTypedConstraintDefinition, @@ -13,13 +14,13 @@ import { import { type ParseHelperOptions, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; import { type AstNode, type AstNodeLocator, - AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -54,14 +55,11 @@ describe('Validation of RangeConstraintExecutor', () => { 'pipelines@0/blocks@2', ) as BlockDefinition; - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; - expect( - allConstraints.length > 0, - 'No constraint definition found in test file', - ); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const constraint = allConstraints[0]!; + const constraint = extractTestElements( + document, + (x): x is TypedConstraintDefinition => isTypedConstraintDefinition(x), + )[0]!; return new RangeConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts index df078f4ad..49ee99c84 100644 --- a/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts +++ b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts @@ -6,19 +6,20 @@ import { type BlockDefinition, type InternalValueRepresentation, type JayveeServices, + type TypedConstraintDefinition, createJayveeServices, isTypedConstraintDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, expectNoParserAndLexerErrors, + extractTestElements, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; import { type AstNode, type AstNodeLocator, - AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -53,14 +54,11 @@ describe('Validation of RegexConstraintExecutor', () => { 'pipelines@0/blocks@2', ) as BlockDefinition; - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allConstraints = [...allElements.filter(isTypedConstraintDefinition)]; - expect( - allConstraints.length > 0, - 'No constraint definition found in test file', - ); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const constraint = allConstraints[0]!; + const constraint = extractTestElements( + document, + (x): x is TypedConstraintDefinition => isTypedConstraintDefinition(x), + )[0]!; return new RegexConstraintExecutor().isValid( value, diff --git a/libs/execution/src/lib/transforms/transform-executor.spec.ts b/libs/execution/src/lib/transforms/transform-executor.spec.ts index 4abd01ad5..a0bcf1c5e 100644 --- a/libs/execution/src/lib/transforms/transform-executor.spec.ts +++ b/libs/execution/src/lib/transforms/transform-executor.spec.ts @@ -9,12 +9,14 @@ import path from 'node:path'; import { type InternalValueRepresentation, type JayveeServices, + type TransformDefinition, createJayveeServices, isTransformDefinition, } from '@jvalue/jayvee-language-server'; import { type ParseHelperOptions, expectNoParserAndLexerErrors, + extractTestElements, loadTestExtensions, parseHelper, readJvTestAssetHelper, @@ -22,7 +24,6 @@ import { import { type AstNode, type AstNodeLocator, - AstUtils, type LangiumDocument, } from 'langium'; import { NodeFileSystem } from 'langium/node'; @@ -78,11 +79,11 @@ describe('Validation of TransformExecutor', () => { const document = await parse(input, { validation: true }); expectNoParserAndLexerErrors(document); - const allElements = AstUtils.streamAllContents(document.parseResult.value); - const allTransforms = [...allElements.filter(isTransformDefinition)]; - expect(allTransforms.length > 0); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const transform = allTransforms[0]!; + const transform = extractTestElements( + document, + (x): x is TransformDefinition => isTransformDefinition(x), + )[0]!; const executionContext = getTestExecutionContext( locator,