From 1cea264287e94b241d94e89b576fb50a15adb9fe Mon Sep 17 00:00:00 2001 From: raylras Date: Mon, 18 Nov 2024 08:14:45 +0800 Subject: [PATCH 1/7] feat: description index --- packages/zenscript/src/module.ts | 3 + .../src/reference/dynamic-provider.ts | 13 +++-- .../src/reference/member-provider.ts | 19 ++++--- .../src/workspace/description-index.ts | 55 +++++++++++++++++++ 4 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 packages/zenscript/src/workspace/description-index.ts diff --git a/packages/zenscript/src/module.ts b/packages/zenscript/src/module.ts index 1c6a0465..bbb5cb14 100644 --- a/packages/zenscript/src/module.ts +++ b/packages/zenscript/src/module.ts @@ -17,6 +17,7 @@ import { ZenScriptTypeComputer } from './typing/type-computer' import { registerValidationChecks, ZenScriptValidator } from './validation/validator' import { ZenScriptBracketManager } from './workspace/bracket-manager' import { ZenScriptConfigurationManager } from './workspace/configuration-manager' +import { ZenScriptDescriptionIndex } from './workspace/description-index' import { ZenScriptPackageManager } from './workspace/package-manager' import { ZenScriptWorkspaceManager } from './workspace/workspace-manager' @@ -37,6 +38,7 @@ export interface ZenScriptAddedServices { workspace: { PackageManager: ZenScriptPackageManager BracketManager: ZenScriptBracketManager + DescriptionIndex: ZenScriptDescriptionIndex } } @@ -74,6 +76,7 @@ export const ZenScriptModule: Module new ZenScriptPackageManager(services), BracketManager: services => new ZenScriptBracketManager(services), + DescriptionIndex: services => new ZenScriptDescriptionIndex(services), }, parser: { TokenBuilder: () => new CustomTokenBuilder(), diff --git a/packages/zenscript/src/reference/dynamic-provider.ts b/packages/zenscript/src/reference/dynamic-provider.ts index ef9d48eb..613c93f4 100644 --- a/packages/zenscript/src/reference/dynamic-provider.ts +++ b/packages/zenscript/src/reference/dynamic-provider.ts @@ -1,7 +1,8 @@ -import type { AstNode, AstNodeDescription, AstNodeDescriptionProvider } from 'langium' +import type { AstNode, AstNodeDescription } from 'langium' import type { ZenScriptAstType } from '../generated/ast' import type { ZenScriptServices } from '../module' import type { TypeComputer } from '../typing/type-computer' +import type { DescriptionIndex } from '../workspace/description-index' import type { MemberProvider } from './member-provider' import { AstUtils, stream } from 'langium' import { isCallExpression, isClassDeclaration, isFunctionDeclaration, isOperatorFunctionDeclaration } from '../generated/ast' @@ -15,12 +16,12 @@ type SourceMap = ZenScriptAstType type RuleMap = { [K in keyof SourceMap]?: (source: SourceMap[K]) => AstNodeDescription[] } export class ZenScriptDynamicProvider implements DynamicProvider { - private readonly descriptions: AstNodeDescriptionProvider + private readonly descriptionIndex: DescriptionIndex private readonly typeComputer: TypeComputer private readonly memberProvider: MemberProvider constructor(services: ZenScriptServices) { - this.descriptions = services.workspace.AstNodeDescriptionProvider + this.descriptionIndex = services.workspace.DescriptionIndex this.typeComputer = services.typing.TypeComputer this.memberProvider = services.references.MemberProvider } @@ -37,7 +38,7 @@ export class ZenScriptDynamicProvider implements DynamicProvider { // dynamic this const classDecl = AstUtils.getContainerOfType(source, isClassDeclaration) if (classDecl) { - dynamics.push(this.descriptions.createDescription(classDecl, 'this')) + dynamics.push(this.descriptionIndex.getThisDescription(classDecl)) } // dynamic arguments @@ -52,7 +53,7 @@ export class ZenScriptDynamicProvider implements DynamicProvider { .filter(it => isFunctionDeclaration(it)) .filter(it => it.prefix === 'static') .filter(it => it.parameters.length === 0) - .forEach(it => dynamics.push(this.descriptions.createDescription(it, it.name))) + .forEach(it => dynamics.push(this.descriptionIndex.getDescription(it))) } } } @@ -70,7 +71,7 @@ export class ZenScriptDynamicProvider implements DynamicProvider { .filter(it => it.parameters.length === 1) .find(it => it.op === '.') if (operatorDecl) { - dynamics.push(this.descriptions.createDescription(operatorDecl.parameters[0], source.target.$refText)) + dynamics.push(this.descriptionIndex.createDynamicDescription(operatorDecl.parameters[0], source.target.$refText)) } return dynamics diff --git a/packages/zenscript/src/reference/member-provider.ts b/packages/zenscript/src/reference/member-provider.ts index 1edbcf9f..5fbeb3fe 100644 --- a/packages/zenscript/src/reference/member-provider.ts +++ b/packages/zenscript/src/reference/member-provider.ts @@ -1,13 +1,14 @@ -import type { AstNode, AstNodeDescription, AstNodeDescriptionProvider } from 'langium' +import type { AstNode, AstNodeDescription } from 'langium' import type { ZenScriptAstType } from '../generated/ast' import type { ZenScriptServices } from '../module' import type { TypeComputer } from '../typing/type-computer' +import type { DescriptionIndex } from '../workspace/description-index' import type { ZenScriptSyntheticAstType } from './synthetic' import { stream } from 'langium' import { isClassDeclaration, isVariableDeclaration } from '../generated/ast' import { ClassType, isAnyType, isClassType, isFunctionType, type Type, type ZenScriptType } from '../typing/type-description' import { getClassChain, isStatic } from '../utils/ast' -import { createSyntheticAstNodeDescription, isSyntheticAstNode } from './synthetic' +import { isSyntheticAstNode } from './synthetic' export interface MemberProvider { getMembers: (source: AstNode | Type | undefined) => AstNodeDescription[] @@ -17,11 +18,11 @@ type SourceMap = ZenScriptAstType & ZenScriptType & ZenScriptSyntheticAstType type RuleMap = { [K in keyof SourceMap]?: (source: SourceMap[K]) => AstNodeDescription[] } export class ZenScriptMemberProvider implements MemberProvider { - private readonly descriptions: AstNodeDescriptionProvider + private readonly descriptionIndex: DescriptionIndex private readonly typeComputer: TypeComputer constructor(services: ZenScriptServices) { - this.descriptions = services.workspace.AstNodeDescriptionProvider + this.descriptionIndex = services.workspace.DescriptionIndex this.typeComputer = services.typing.TypeComputer } @@ -35,10 +36,10 @@ export class ZenScriptMemberProvider implements MemberProvider { const declarations = stream(source.children.values()) .filter(it => it.isDataNode()) .flatMap(it => it.data) - .map(it => this.descriptions.createDescription(it, undefined)) + .map(it => this.descriptionIndex.getDescription(it)) const packages = stream(source.children.values()) .filter(it => it.isInternalNode()) - .map(it => createSyntheticAstNodeDescription('SyntheticHierarchyNode', it.name, it)) + .map(it => this.descriptionIndex.getPackageDescription(it)) return stream(declarations, packages).toArray() }, @@ -49,7 +50,7 @@ export class ZenScriptMemberProvider implements MemberProvider { source.statements.filter(it => isVariableDeclaration(it)) .filter(it => it.prefix === 'static') .forEach(it => members.push(it)) - return members.map(it => this.descriptions.createDescription(it, undefined)) + return members.map(it => this.descriptionIndex.getDescription(it)) }, ImportDeclaration: (source) => { @@ -60,7 +61,7 @@ export class ZenScriptMemberProvider implements MemberProvider { return getClassChain(source) .flatMap(it => it.members) .filter(it => isStatic(it)) - .map(it => this.descriptions.createDescription(it, undefined)) + .map(it => this.descriptionIndex.getDescription(it)) }, VariableDeclaration: (source) => { @@ -162,7 +163,7 @@ export class ZenScriptMemberProvider implements MemberProvider { return getClassChain(source.declaration) .flatMap(it => it.members) .filter(it => !isStatic(it)) - .map(it => this.descriptions.createDescription(it, undefined)) + .map(it => this.descriptionIndex.getDescription(it)) }, } } diff --git a/packages/zenscript/src/workspace/description-index.ts b/packages/zenscript/src/workspace/description-index.ts new file mode 100644 index 00000000..ea43c499 --- /dev/null +++ b/packages/zenscript/src/workspace/description-index.ts @@ -0,0 +1,55 @@ +import type { HierarchyNode } from '@intellizen/shared' +import type { AstNode, AstNodeDescription, AstNodeDescriptionProvider } from 'langium' +import type { ClassDeclaration } from '../generated/ast' +import type { ZenScriptServices } from '../module' +import { createSyntheticAstNodeDescription } from '../reference/synthetic' + +export interface DescriptionIndex { + getDescription: (astNode: AstNode) => AstNodeDescription + getPackageDescription: (pkgNode: HierarchyNode) => AstNodeDescription + getThisDescription: (classDecl: ClassDeclaration) => AstNodeDescription + createDynamicDescription: (astNode: AstNode, name: string) => AstNodeDescription +} + +export class ZenScriptDescriptionIndex implements DescriptionIndex { + private readonly descriptions: AstNodeDescriptionProvider + + private readonly astDescriptions: WeakMap + private readonly pkgDescriptions: WeakMap, AstNodeDescription> + private readonly thisDescriptions: WeakMap + + constructor(services: ZenScriptServices) { + this.descriptions = services.workspace.AstNodeDescriptionProvider + this.astDescriptions = new WeakMap() + this.pkgDescriptions = new WeakMap() + this.thisDescriptions = new WeakMap() + } + + getDescription(astNode: AstNode): AstNodeDescription { + if (!this.astDescriptions.has(astNode)) { + this.astDescriptions.set(astNode, this.descriptions.createDescription(astNode, undefined)) + } + return this.astDescriptions.get(astNode)! + } + + getPackageDescription(pkgNode: HierarchyNode): AstNodeDescription { + if (pkgNode.isDataNode()) { + throw new Error(`Expected a package node, but received a data node: ${pkgNode}`) + } + if (!this.pkgDescriptions.has(pkgNode)) { + this.pkgDescriptions.set(pkgNode, createSyntheticAstNodeDescription('SyntheticHierarchyNode', pkgNode.name, pkgNode)) + } + return this.pkgDescriptions.get(pkgNode)! + } + + getThisDescription(classDecl: ClassDeclaration): AstNodeDescription { + if (!this.thisDescriptions.has(classDecl)) { + this.thisDescriptions.set(classDecl, this.descriptions.createDescription(classDecl, 'this')) + } + return this.thisDescriptions.get(classDecl)! + } + + createDynamicDescription(astNode: AstNode, name: string): AstNodeDescription { + return this.descriptions.createDescription(astNode, name) + } +} From 52960a8e9d55979b99d4c430a98431d793cf511c Mon Sep 17 00:00:00 2001 From: raylras Date: Mon, 18 Nov 2024 08:59:12 +0800 Subject: [PATCH 2/7] set ast descriptions --- .../src/reference/scope-computation.ts | 18 +++++++++++++++++- .../src/workspace/description-index.ts | 6 +++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/zenscript/src/reference/scope-computation.ts b/packages/zenscript/src/reference/scope-computation.ts index 880a155c..0ed2e94e 100644 --- a/packages/zenscript/src/reference/scope-computation.ts +++ b/packages/zenscript/src/reference/scope-computation.ts @@ -1,12 +1,16 @@ -import type { AstNode, AstNodeDescription, LangiumDocument } from 'langium' +import type { AstNode, AstNodeDescription, LangiumDocument, PrecomputedScopes } from 'langium' import type { Script } from '../generated/ast' import type { ZenScriptServices } from '../module' +import type { ZenScriptDescriptionIndex } from '../workspace/description-index' import { DefaultScopeComputation } from 'langium' import { isGlobal } from '../utils/ast' export class ZenScriptScopeComputation extends DefaultScopeComputation { + private readonly descriptionIndex: ZenScriptDescriptionIndex + constructor(services: ZenScriptServices) { super(services) + this.descriptionIndex = services.workspace.DescriptionIndex } protected override exportNode(node: AstNode, exports: AstNodeDescription[], document: LangiumDocument