Scope Provider Issue #1323
-
Hi, guys! This is the first time I ask a question on this forum. Until now I was able to find some threads about the issue I was facing, but this time it seems like nobody had this problem. My goal was to create a custom scope computation and scope provider to filter the scope of a cross-reference, with respect to a condition I am setting. What I am trying to say is that in my DSL code, when I want to reference a node through a symbol (grammar allows it) but there are 2 nodes that have the same name/ symbol, I will choose the one that respects a certain condition. I know that one of the solutions would be using qualified names, but I don't want that. The problem appeared here: For this reason I am only trying to override the globalScope. MapScope being used as the class for the scope, I realize that the AstNodeDescriptions are saved in a Map(key, value) data structure and the constructor assigns elements to this.elements using this.elements.set(name, element) . This method has the property that if the name is already found in the map, it will override the value saved for that key/name. The name represents the name of the node of the respective AstNodeDescription, which is the same as the symbol we look for in the DSL code. Because of this, the global scope will not contain both nodes with the same symbol, but only the one that was saved the second (which overrides the first one). Question: Custom Scope Computation + Provider: export class MyScopeComputation extends DefaultScopeComputation {
qualifiedNameProvider: QualifiedNameProvider;
constructor(services: Dt5Services){
super(services);
this.qualifiedNameProvider= services.references.QualifiedNameProvider;
}
/**
* Export all functions using their fully qualified name
*/
override async computeExports(document: LangiumDocument): Promise<AstNodeDescription[]> {
const exportedDescriptions: AstNodeDescription[] = [];
for (const childNode of streamAllContents(document.parseResult.value)) {
if (isStateCategory(childNode) || isStateValue(childNode) || isNameSpaceDefinition(childNode) || isComponentType(childNode)
|| isColor(childNode) || isName(childNode) || isDisplayDefinition(childNode)) {
let name = this.nameProvider.getName(childNode);
if(name){
const fullyQualifiedName = this.qualifiedNameProvider.getQualifiedName(childNode, name);
/**
* Checking the qualified names on the console
*/
exportedDescriptions.push(this.descriptions.createDescription(childNode, fullyQualifiedName, document));
}
}
}
return exportedDescriptions;
}
override async computeLocalScopes(document: LangiumDocument, cancelToken = CancellationToken.None): Promise<PrecomputedScopes> {
const model = document.parseResult.value as Model;
const scopes = new MultiMap<AstNode, AstNodeDescription>();
await this.processContainer(model, scopes, document, cancelToken);
return scopes;
}
protected async processContainer(container: Model, scopes: PrecomputedScopes, document: LangiumDocument, cancelToken: CancellationToken): Promise<AstNodeDescription[]> {
const localDescriptions: AstNodeDescription[] = [];
for (const element of container.elements) {
await interruptAndCheck(cancelToken);
if (!isStateCategory(element) && !isStateValue(element) && !isColor(element) && !isDisplayDefinition(element) && 'name' in element) {
const description = this.descriptions.createDescription(element, element.name as string, document);
localDescriptions.push(description);
} else if (isStateCategory(element) || isStateValue(element) || isColor(element) || isDisplayDefinition(element)) {
const nestedDescriptions = await this.processContainer(element, scopes, document, cancelToken);
for (const description of nestedDescriptions) {
// Add qualified names to the container
const qualified = this.createQualifiedDescription(element, description, document);
localDescriptions.push(qualified);
}
}
}
scopes.addAll(container, localDescriptions);
return localDescriptions;
}
protected createQualifiedDescription(pack: StateCategory | StateValue | Color | DisplayDefinition, description: AstNodeDescription, document: LangiumDocument): AstNodeDescription {
const name = this.qualifiedNameProvider.getQualifiedName(pack, description.name);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.descriptions.createDescription(description.node!, name, document);
}
}
export class MyScopeProvider extends DefaultScopeProvider {
constructor(services: Dt5Services){
super(services);
}
protected override getGlobalScope(referenceType: string, context: ReferenceInfo): Scope{
let globalScope = new MapScope(this.indexManager.allElements(referenceType));
if(isModelInstanceDefault(context.container)){
if(context.property=='category'){
const modelInstanceDefault = context.container;
const modelInstance = modelInstanceDefault.$container;
const componentType = modelInstance.type.ref;
let filteredGlobalScope: MapScope = new MapScope([]);
filteredGlobalScope.elements.clear();
/**
* element = <name, AstNodeDescription>
*/
for(const element of globalScope.elements){
const name = element[0];
const desc = element[1];
const nodeOfDesc = desc.node;
if(isStateCategory(nodeOfDesc) && nodeOfDesc.$container!==componentType){
//globalScope.elements.delete(name);
//I think this deletes actually the other node, that we want to keep, because the name is the same.
}else{
filteredGlobalScope.elements.set(name, desc);
}
}
return filteredGlobalScope;
}
else{ // context.property == 'stateValue'
return globalScope;
}
}
return globalScope;
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Hey @vasilache-radu, thanks for the question, I'm not sure why you're doing this: let globalScope = new MapScope(this.indexManager.allElements(referenceType)); In your global scope computation. Your issue seems to be that you're losing information in the Note that we also have a |
Beta Was this translation helpful? Give feedback.
So that generally makes sense. The index is stored in the order in which files are processed. In case a file is removed and re-added it gets moved to the end of the index, therefore possibly making other elements have higher "priority" in the linking process. Generally, linking becomes non-deterministic once you have multiple elements in the same scope with the same name.