Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/ide/vscode/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "zenstack-v3",
"publisher": "zenstack",
"version": "3.0.6",
"version": "3.0.8",
"displayName": "ZenStack V3 Language Tools",
"description": "VSCode extension for ZenStack (v3) ZModel language",
"private": true,
Expand Down
2 changes: 2 additions & 0 deletions packages/language/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from 'langium/lsp';
import { ZModelGeneratedModule, ZModelGeneratedSharedModule, ZModelLanguageMetaData } from './generated/module';
import { ZModelValidator, registerValidationChecks } from './validator';
import { ZModelDocumentBuilder } from './zmodel-document-builder';
import { ZModelLinker } from './zmodel-linker';
import { ZModelScopeComputation, ZModelScopeProvider } from './zmodel-scope';
import { ZModelWorkspaceManager } from './zmodel-workspace-manager';
Expand Down Expand Up @@ -49,6 +50,7 @@ export type ZModelSharedServices = LangiumSharedServices;

export const ZModelSharedModule: Module<ZModelSharedServices, DeepPartial<ZModelSharedServices>> = {
workspace: {
DocumentBuilder: (services) => new ZModelDocumentBuilder(services),
WorkspaceManager: (services) => new ZModelWorkspaceManager(services),
},
};
Expand Down
26 changes: 16 additions & 10 deletions packages/language/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { invariant } from '@zenstackhq/common-helpers';
import { AstUtils, URI, type AstNode, type LangiumDocument, type LangiumDocuments, type Reference } from 'langium';
import fs from 'node:fs';
import path from 'path';
Expand Down Expand Up @@ -172,7 +171,10 @@ export function getRecursiveBases(
}
seen.add(decl);
decl.mixins.forEach((mixin) => {
const baseDecl = mixin.ref;
// avoid using mixin.ref since this function can be called before linking
const baseDecl = decl.$container.declarations.find(
(d): d is TypeDef => isTypeDef(d) && d.name === mixin.$refText,
);
if (baseDecl) {
if (!includeDelegate && isDelegateModel(baseDecl)) {
return;
Expand Down Expand Up @@ -517,13 +519,15 @@ export function getAllFields(

const fields: DataField[] = [];
for (const mixin of decl.mixins) {
invariant(mixin.ref, `Mixin ${mixin.$refText} is not resolved`);
fields.push(...getAllFields(mixin.ref, includeIgnored, seen));
if (mixin.ref) {
fields.push(...getAllFields(mixin.ref, includeIgnored, seen));
}
}

if (isDataModel(decl) && decl.baseModel) {
invariant(decl.baseModel.ref, `Base model ${decl.baseModel.$refText} is not resolved`);
fields.push(...getAllFields(decl.baseModel.ref, includeIgnored, seen));
if (decl.baseModel.ref) {
fields.push(...getAllFields(decl.baseModel.ref, includeIgnored, seen));
}
}

fields.push(...decl.fields.filter((f) => includeIgnored || !hasAttribute(f, '@ignore')));
Expand All @@ -541,13 +545,15 @@ export function getAllAttributes(

const attributes: DataModelAttribute[] = [];
for (const mixin of decl.mixins) {
invariant(mixin.ref, `Mixin ${mixin.$refText} is not resolved`);
attributes.push(...getAllAttributes(mixin.ref, seen));
if (mixin.ref) {
attributes.push(...getAllAttributes(mixin.ref, seen));
}
}

if (isDataModel(decl) && decl.baseModel) {
invariant(decl.baseModel.ref, `Base model ${decl.baseModel.$refText} is not resolved`);
attributes.push(...getAllAttributes(decl.baseModel.ref, seen));
if (decl.baseModel.ref) {
attributes.push(...getAllAttributes(decl.baseModel.ref, seen));
}
}

attributes.push(...decl.attributes);
Expand Down
37 changes: 10 additions & 27 deletions packages/language/src/validator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-expressions */

import type { AstNode, LangiumDocument, ValidationAcceptor, ValidationChecks } from 'langium';
import type { ValidationAcceptor, ValidationChecks } from 'langium';
import type {
Attribute,
DataModel,
Expand Down Expand Up @@ -50,54 +48,39 @@ export function registerValidationChecks(services: ZModelServices) {
export class ZModelValidator {
constructor(protected readonly services: ZModelServices) {}

private shouldCheck(node: AstNode) {
let doc: LangiumDocument | undefined;
let currNode: AstNode | undefined = node;
while (currNode) {
if (currNode.$document) {
doc = currNode.$document;
break;
}
currNode = currNode.$container;
}

return doc?.parseResult.lexerErrors.length === 0 && doc?.parseResult.parserErrors.length === 0;
}

checkModel(node: Model, accept: ValidationAcceptor): void {
this.shouldCheck(node) &&
new SchemaValidator(this.services.shared.workspace.LangiumDocuments).validate(node, accept);
new SchemaValidator(this.services.shared.workspace.LangiumDocuments).validate(node, accept);
}

checkDataSource(node: DataSource, accept: ValidationAcceptor): void {
this.shouldCheck(node) && new DataSourceValidator().validate(node, accept);
new DataSourceValidator().validate(node, accept);
}

checkDataModel(node: DataModel, accept: ValidationAcceptor): void {
this.shouldCheck(node) && new DataModelValidator().validate(node, accept);
new DataModelValidator().validate(node, accept);
}

checkTypeDef(node: TypeDef, accept: ValidationAcceptor): void {
this.shouldCheck(node) && new TypeDefValidator().validate(node, accept);
new TypeDefValidator().validate(node, accept);
}

checkEnum(node: Enum, accept: ValidationAcceptor): void {
this.shouldCheck(node) && new EnumValidator().validate(node, accept);
new EnumValidator().validate(node, accept);
}

checkAttribute(node: Attribute, accept: ValidationAcceptor): void {
this.shouldCheck(node) && new AttributeValidator().validate(node, accept);
new AttributeValidator().validate(node, accept);
}

checkExpression(node: Expression, accept: ValidationAcceptor): void {
this.shouldCheck(node) && new ExpressionValidator().validate(node, accept);
new ExpressionValidator().validate(node, accept);
}

checkFunctionInvocation(node: InvocationExpr, accept: ValidationAcceptor): void {
this.shouldCheck(node) && new FunctionInvocationValidator().validate(node, accept);
new FunctionInvocationValidator().validate(node, accept);
}

checkFunctionDecl(node: FunctionDecl, accept: ValidationAcceptor): void {
this.shouldCheck(node) && new FunctionDeclValidator().validate(node, accept);
new FunctionDeclValidator().validate(node, accept);
}
}
22 changes: 22 additions & 0 deletions packages/language/src/zmodel-document-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DefaultDocumentBuilder, type BuildOptions, type LangiumDocument } from 'langium';

export class ZModelDocumentBuilder extends DefaultDocumentBuilder {
override buildDocuments(documents: LangiumDocument[], options: BuildOptions, cancelToken: any): Promise<void> {
return super.buildDocuments(
documents,
{
...options,
validation:
// force overriding validation options
options.validation === false || options.validation === undefined
? options.validation
: {
stopAfterLexingErrors: true,
stopAfterParsingErrors: true,
stopAfterLinkingErrors: true,
},
},
cancelToken,
);
}
}
Loading