如何为语法规则集成Scope Provider与自定义名称?
Solution: Custom Scope Provider for Xtext Visibility Restriction
Let's walk through how to implement a scope provider that restricts Ref references to only those Greeting instances marked as visible via Visible entries in the same model.
Step 1: Refine the Grammar (Optional but Recommended)
First, let's clarify the grammar with more descriptive names for better readability:
grammar org.example.MyDsl with org.eclipse.xtext.common.Terminals generate myDsl "http://www.example.org/MyDsl" Model: greetings+=Greeting+ (visibles+=Visible)* reference=Ref; Ref: 'ref_greeting' target=[Greeting]; Visible: 'vis' visibleGreeting=[Greeting]; Greeting: 'Hallo' name=ID '!';
Step 2: Implement the Custom Scope Provider
Create a scope provider class that overrides the default behavior for the Ref.target reference. This class will filter allowed Greeting instances based on the Visible entries in the same model:
package org.example.scoping; import java.util.ArrayList; import java.util.List; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.scoping.Scopes; import org.example.mydsl.myDsl.Model; import org.example.mydsl.myDsl.Ref; import org.example.mydsl.myDsl.Visible; public class MyDslScopeProvider extends org.eclipse.xtext.scoping.impl.AbstractScopeProvider { @Override public IScope getScope(EObject context, EReference reference) { // Target only the Ref.target reference if (context instanceof Ref && reference.getName().equals("target")) { Model parentModel = (Model) context.eContainer(); List<EObject> allowedGreetings = new ArrayList<>(); if (!parentModel.getVisibles().isEmpty()) { // If there are Visible entries, only include referenced Greetings for (Visible visible : parentModel.getVisibles()) { if (visible.getVisibleGreeting() != null) { allowedGreetings.add(visible.getVisibleGreeting()); } } } else { // If no Visible entries, allow all Greetings in the model allowedGreetings.addAll(parentModel.getGreetings()); } // Return a scope containing only allowed Greetings return Scopes.scopeFor(allowedGreetings); } // Fallback to default scope for all other references return super.getScope(context, reference); } }
Step 3: Bind the Scope Provider in the Runtime Module
Update your runtime module to use the custom scope provider instead of the default:
package org.example; import org.eclipse.xtext.scoping.IScopeProvider; import org.example.scoping.MyDslScopeProvider; public class MyDslRuntimeModule extends AbstractMyDslRuntimeModule { @Override public Class<? extends IScopeProvider> bindIScopeProvider() { return MyDslScopeProvider.class; } }
Step 4: Add Optional Validation (For Clearer Error Messages)
To provide explicit error feedback when an invalid reference is used, add a validator class:
package org.example.validation; import org.eclipse.xtext.validation.AbstractDeclarativeValidator; import org.eclipse.xtext.validation.Check; import org.example.mydsl.myDsl.Model; import org.example.mydsl.myDsl.Ref; import org.example.mydsl.myDsl.Visible; public class MyDslValidator extends AbstractDeclarativeValidator { @Check public void checkRefTargetIsVisible(Ref ref) { Model parentModel = (Model) ref.eContainer(); if (!parentModel.getVisibles().isEmpty() && ref.getTarget() != null) { boolean isVisible = false; for (Visible visible : parentModel.getVisibles()) { if (visible.getVisibleGreeting() != null && visible.getVisibleGreeting().equals(ref.getTarget())) { isVisible = true; break; } } if (!isVisible) { error("Greeting '" + ref.getTarget().getName() + "' is not marked as visible", ref, ref.eClass().getEStructuralFeature("target")); } } } }
How It Works
- Scoping Behavior:
- When no
Visibleentries exist, allGreetinginstances in the model are allowed (matches your first input file's valid case). - When
Visibleentries are present, only theGreetinginstances referenced in those entries appear in auto-completion and are valid targets.
- When no
- Validation:
- Explicitly flags invalid references with a user-friendly error message, even if the user manually types an invalid name.
Testing Your Input Files
- First Input (Valid):
NoHallo hallo! Hallo hallo_other_vis! ref_greeting halloVisibleentries exist, so all greetings are allowed. - Second Input (Invalid):
Two errors:Hallo h! vis hallo_other_vis ref_greeting hallo_other_vishallo_other_visdoesn't exist in the model (invalidVisiblereference).- Even if it did exist, without a corresponding
Greeting, theReftarget would be invalid.
内容的提问来源于stack exchange,提问作者Johan
相关产品推荐
相关产品推荐

