完成世界书、骰子、apiconfig页面处理

This commit is contained in:
2026-04-30 01:35:10 +08:00
parent a3e3711b2b
commit ba9b925c32
4602 changed files with 785225 additions and 23 deletions

View File

@@ -0,0 +1,58 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { DefinitionParams } from 'vscode-languageserver';
import type { LangiumServices } from '../../lsp/lsp-services.js';
import type { AstNode, LeafCstNode, Properties } from '../../syntax-tree.js';
import type { MaybePromise } from '../../utils/promise-utils.js';
import type { LangiumDocuments } from '../../workspace/documents.js';
import type { Grammar, GrammarImport } from '../../languages/generated/ast.js';
import { LocationLink, Range } from 'vscode-languageserver';
import { DefaultDefinitionProvider } from '../../lsp/index.js';
import { streamContents } from '../../utils/ast-utils.js';
import { findAssignment } from '../../utils/grammar-utils.js';
import { isGrammarImport } from '../../languages/generated/ast.js';
import { resolveImport } from '../internal-grammar-util.js';
export class LangiumGrammarDefinitionProvider extends DefaultDefinitionProvider {
protected documents: LangiumDocuments;
constructor(services: LangiumServices) {
super(services);
this.documents = services.shared.workspace.LangiumDocuments;
}
protected override collectLocationLinks(sourceCstNode: LeafCstNode, _params: DefinitionParams): MaybePromise<LocationLink[] | undefined> {
const pathFeature: Properties<GrammarImport> = 'path';
if (isGrammarImport(sourceCstNode.astNode) && findAssignment(sourceCstNode)?.feature === pathFeature) {
const importedGrammar = resolveImport(this.documents, sourceCstNode.astNode);
if (importedGrammar?.$document) {
const targetObject = this.findTargetObject(importedGrammar) ?? importedGrammar;
const selectionRange = this.nameProvider.getNameNode(targetObject)?.range ?? Range.create(0, 0, 0, 0);
const previewRange = targetObject.$cstNode?.range ?? Range.create(0, 0, 0, 0);
return [
LocationLink.create(
importedGrammar.$document.uri.toString(),
previewRange,
selectionRange,
sourceCstNode.range
)
];
}
return undefined;
}
return super.collectLocationLinks(sourceCstNode, _params);
}
protected findTargetObject(importedGrammar: Grammar): AstNode | undefined {
// Jump to grammar name or the first element
if (importedGrammar.isDeclared) {
return importedGrammar;
}
return streamContents(importedGrammar).head();
}
}

View File

@@ -0,0 +1,73 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { AstNode } from '../../syntax-tree.js';
import type { SemanticTokenAcceptor } from '../../lsp/semantic-token-provider.js';
import { SemanticTokenTypes } from 'vscode-languageserver';
import { AbstractSemanticTokenProvider } from '../../lsp/semantic-token-provider.js';
import { isAction, isAssignment, isInfixRule, isParameter, isParameterReference, isReturnType, isRuleCall, isSimpleType, isTypeAttribute } from '../../languages/generated/ast.js';
export class LangiumGrammarSemanticTokenProvider extends AbstractSemanticTokenProvider {
protected highlightElement(node: AstNode, acceptor: SemanticTokenAcceptor): void {
if (isAssignment(node)) {
acceptor({
node,
property: 'feature',
type: SemanticTokenTypes.property
});
} else if (isAction(node)) {
if (node.feature) {
acceptor({
node,
property: 'feature',
type: SemanticTokenTypes.property
});
}
} else if (isReturnType(node)) {
acceptor({
node,
property: 'name',
type: SemanticTokenTypes.type
});
} else if (isSimpleType(node)) {
if (node.primitiveType || node.typeRef) {
acceptor({
node,
property: node.primitiveType ? 'primitiveType' : 'typeRef',
type: SemanticTokenTypes.type
});
}
} else if (isParameter(node)) {
acceptor({
node,
property: 'name',
type: SemanticTokenTypes.parameter
});
} else if (isParameterReference(node)) {
acceptor({
node,
property: 'parameter',
type: SemanticTokenTypes.parameter
});
} else if (isRuleCall(node)) {
if (!isInfixRule(node.rule.ref) && node.rule.ref?.fragment) {
acceptor({
node,
property: 'rule',
type: SemanticTokenTypes.type
});
}
} else if (isTypeAttribute(node)) {
acceptor({
node,
property: 'name',
type: SemanticTokenTypes.property
});
}
}
}

View File

@@ -0,0 +1,84 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { ParserRule, Interface, Type, Grammar, InfixRule } from '../../../languages/generated/ast.js';
import type { URI } from '../../../utils/uri-utils.js';
import type { LangiumCoreServices } from '../../../index.js';
import type { PlainAstTypes } from './plain-types.js';
import type { AstTypes } from './types.js';
import { collectInferredTypes } from './inferred-types.js';
import { collectDeclaredTypes } from './declared-types.js';
import { getDocument } from '../../../utils/ast-utils.js';
import { isInfixRule, isParserRule } from '../../../languages/generated/ast.js';
import { resolveImport } from '../../internal-grammar-util.js';
import { isDataTypeRule } from '../../../utils/grammar-utils.js';
export interface AstResources {
parserRules: ParserRule[]
infixRules: InfixRule[]
datatypeRules: ParserRule[]
interfaces: Interface[]
types: Type[]
}
export interface TypeResources {
inferred: PlainAstTypes
declared: PlainAstTypes
astResources: AstResources
}
export interface ValidationAstTypes {
inferred: AstTypes
declared: AstTypes
astResources: AstResources
}
export function collectTypeResources(grammars: Grammar | Grammar[], services?: LangiumCoreServices): TypeResources {
const astResources = collectAllAstResources(grammars, undefined, undefined, services);
const declared = collectDeclaredTypes(astResources.interfaces, astResources.types, services);
const inferred = collectInferredTypes(astResources.parserRules, astResources.datatypeRules, astResources.infixRules, declared, services);
return {
astResources,
inferred,
declared
};
}
///////////////////////////////////////////////////////////////////////////////
export function collectAllAstResources(grammars: Grammar | Grammar[], visited: Set<URI> = new Set(),
astResources: AstResources = { parserRules: [], infixRules: [], datatypeRules: [], interfaces: [], types: [] }, services?: LangiumCoreServices): AstResources {
if (!Array.isArray(grammars)) grammars = [grammars];
for (const grammar of grammars) {
const doc = getDocument(grammar);
if (visited.has(doc.uri)) {
continue;
}
visited.add(doc.uri);
for (const rule of grammar.rules) {
if (isParserRule(rule) && !rule.fragment) {
if (isDataTypeRule(rule)) {
astResources.datatypeRules.push(rule);
} else {
astResources.parserRules.push(rule);
}
} else if (isInfixRule(rule)) {
astResources.infixRules.push(rule);
}
}
grammar.interfaces.forEach(e => astResources.interfaces.push(e));
grammar.types.forEach(e => astResources.types.push(e));
const documents = services?.shared.workspace.LangiumDocuments;
if (documents) {
const importedGrammars = grammar.imports.map(e => resolveImport(documents, e)).filter(e => e !== undefined);
collectAllAstResources(importedGrammars, visited, astResources, services);
}
}
return astResources;
}

View File

@@ -0,0 +1,125 @@
/******************************************************************************
* Copyright 2022 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
import type { Interface, Type, TypeDefinition, ValueLiteral } from '../../../languages/generated/ast.js';
import type { LangiumCoreServices } from '../../../index.js';
import { isArrayLiteral, isBooleanLiteral } from '../../../languages/generated/ast.js';
import type { PlainAstTypes, PlainInterface, PlainProperty, PlainPropertyDefaultValue, PlainPropertyType, PlainUnion } from './plain-types.js';
import { isArrayType, isReferenceType, isUnionType, isSimpleType } from '../../../languages/generated/ast.js';
import { getTypeNameWithoutError, isPrimitiveGrammarType } from '../../internal-grammar-util.js';
import { getTypeName } from '../../../utils/grammar-utils.js';
export function collectDeclaredTypes(interfaces: Interface[], unions: Type[], services?: LangiumCoreServices): PlainAstTypes {
const commentProvider = services?.documentation.CommentProvider;
const declaredTypes: PlainAstTypes = { unions: [], interfaces: [] };
// add interfaces
for (const type of interfaces) {
const properties: PlainProperty[] = [];
for (const attribute of type.attributes) {
const property: PlainProperty = {
name: attribute.name,
optional: attribute.isOptional,
astNodes: new Set([attribute]),
type: typeDefinitionToPropertyType(attribute.type),
comment: commentProvider?.getComment(attribute)
};
if (attribute.defaultValue) {
property.defaultValue = toPropertyDefaultValue(attribute.defaultValue);
}
properties.push(property);
}
const superTypes = new Set<string>();
for (const superType of type.superTypes) {
if (superType.ref) {
superTypes.add(getTypeName(superType.ref));
}
}
const interfaceType: PlainInterface = {
name: type.name,
declared: true,
abstract: false,
properties: properties,
superTypes: superTypes,
subTypes: new Set(),
comment: commentProvider?.getComment(type),
};
declaredTypes.interfaces.push(interfaceType);
}
// add types
for (const union of unions) {
const unionType: PlainUnion = {
name: union.name,
declared: true,
type: typeDefinitionToPropertyType(union.type),
superTypes: new Set(),
subTypes: new Set(),
comment: commentProvider?.getComment(union),
};
declaredTypes.unions.push(unionType);
}
return declaredTypes;
}
function toPropertyDefaultValue(literal: ValueLiteral): PlainPropertyDefaultValue {
if (isBooleanLiteral(literal)) {
return literal.true;
} else if (isArrayLiteral(literal)) {
return literal.elements.map(toPropertyDefaultValue);
} else {
return literal.value;
}
}
export function typeDefinitionToPropertyType(type: TypeDefinition): PlainPropertyType {
if (isArrayType(type)) {
return {
elementType: typeDefinitionToPropertyType(type.elementType)
};
} else if (isReferenceType(type)) {
return {
referenceType: typeDefinitionToPropertyType(type.referenceType),
isMulti: type.isMulti,
isSingle: !type.isMulti
};
} else if (isUnionType(type)) {
return {
types: type.types.map(typeDefinitionToPropertyType)
};
} else if (isSimpleType(type)) {
let value: string | undefined;
if (type.primitiveType) {
value = type.primitiveType;
return {
primitive: value
};
} else if (type.stringType) {
value = type.stringType;
return {
string: value
};
} else if (type.typeRef) {
const ref = type.typeRef.ref;
const value = getTypeNameWithoutError(ref);
if (value) {
if (isPrimitiveGrammarType(value)) {
return {
primitive: value
};
} else {
return {
value
};
}
}
}
}
return {
primitive: 'unknown'
};
}

File diff suppressed because it is too large Load Diff