The new Xtext version in Galileo changed a few things. To do scoping, a little bit of Java development is necessary. Documentation is very brief, but with a little research it is quite easy. Consider the following grammar, that allows us to define components, instances of those components and – the interesting part here – connect ports of instances:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Component :
   "component" name = ID "{"
   (ports+=Port)*
   "}";
Port :
   "port" dir=Direction name=ID ":" ref=[Interface|ID] ";"
   ;
enum Direction :
   IN="in" | OUT="out";
Instance:
   "instance" name=ID ":" type=[Component|ID] ";"
   ;
Connection:
   "connect" in=[Instance|ID]"."p=[Port|ID] "->" in2=[Instance|ID]"."p2=[Port|ID] ";"
   ; |
The default code completion will traverse all defined Ports for the references p and p2 in the Connection rule. A better behaviour would be:
- Show only the ports in the instances (in, in2)
- For p, show only “OUT”-Ports, for p2, show only “IN”-ports.
To do this, we need to define methods in a class derived from AbstractDeclarativeScopeProvider. The method names follow the signature scope_<rule>_<element>, so the code fragment for p2 is:
|
1 2 3 4 5 6 7 8 |
IScope scope_Connection_p2(Connection ctx, EReference ref)
   {
      if(ctx.getIn2() == null )
         return IScope.NULLSCOPE;
      else
         return new SimpleScope(IScope.NULLSCOPE, getRPorts(ctx.getIn2().getType()));
   } |
With some additional code the full Provider for p and p2 looks like this:
|
1 |
public class AutomotiveDSLScopeProvider extends AbstractDeclarativeScopeProvider { |
|
1 |
   IScope scope_Connection_p2(Connection ctx, EReference ref) |
|
1 |
   { |
|
1 |
      if(ctx.getIn2() == null ) |
|
1 |
         return IScope.NULLSCOPE; |
|
1 |
      else |
|
1 |
         return new SimpleScope(IScope.NULLSCOPE, getRPorts(ctx.getIn2().getType())); |
|
1 |
   } |
|
1 |
|
1 |
   IScope scope_Connection_p(Connection ctx, EReference ref) |
|
1 |
   { |
|
1 |
      if(ctx.getIn() == null ) |
|
1 |
         return IScope.NULLSCOPE; |
|
1 |
      else |
|
1 |
         return new SimpleScope(IScope.NULLSCOPE, getPPorts(ctx.getIn().getType())); |
|
1 |
   } |
|
1 |
   private Iterable<IScopedElement> getPPorts(Component clazz) { |
|
1 |
      List<IScopedElement> result = new ArrayList<IScopedElement>(); |
|
1 |
|
1 |
      for (Port f : clazz.getPorts()) |
|
1 |
         if (f instanceof Port && f.getDir() == Direction.OUT) |
|
1 |
            result.add(ScopedElement.create(f.getName(), f,"(")); |
|
1 |
|
1 |
      return result; |
|
1 |
   } |
|
1 |
|
1 |
   private Iterable<IScopedElement> getRPorts(Component clazz) { |
|
1 |
      List<IScopedElement> result = new ArrayList<IScopedElement>(); |
|
1 |
|
1 |
      for (Port f : clazz.getPorts()) |
|
1 |
         if (f instanceof Port && f.getDir() == Direction.IN) |
|
1 |
            result.add(ScopedElement.create(f.getName(), f)); |
|
1 |
|
1 |
      return result; |
|
1 |
   } |
|
1 |
|
1 |
} |
4 thoughts on “Scoping with Xtext / TMF”