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, ), ),