Usually I hate it when APIs break. TMF 0.8 introduces a different scoping API than Xtext 0.7. It took me a while to figure out. So I’ll show the changes below:
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 the old code fragment for was:
public class AutomotiveDSLScopeProvider extends AbstractDeclarativeScopeProvider {
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()));
}
IScope scope_Connection_p(Connection ctx, EReference ref)
{
if(ctx.getIn() == null )
return IScope.NULLSCOPE;
else
return new SimpleScope(IScope.NULLSCOPE, getPPorts(ctx.getIn().getType()));
}
private Iterable<IScopedElement> getPPorts(Component clazz) {
List<IScopedElement> result = new ArrayList<IScopedElement>();
for (Port f : clazz.getPorts())
if (f instanceof Port && f.getDir() == Direction.OUT)
result.add(ScopedElement.create(f.getName(), f,"("));
return result;
}
private Iterable<IScopedElement> getRPorts(Component clazz) {
List<IScopedElement> result = new ArrayList<IScopedElement>();
for (Port f : clazz.getPorts())
if (f instanceof Port && f.getDir() == Direction.IN)
result.add(ScopedElement.create(f.getName(), f));
return result;
}
}
One of the important changes that I use different imports, because some are deprecated and some are generally nice to use. Newly introduced are:
import static org.eclipse.xtext.scoping.Scopes.*;
for helpful functions and
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
for working with lists. So the new code looks like this:
public class AutomotiveDSLScopeProvider extends AbstractDeclarativeScopeProvider {
protected Predicate<Port> pPortPred = new Predicate<Port>(){
public boolean apply(Port input) { return input.getDir() == Direction.OUT ; }
};
protected Predicate<Port> rPortPred = new Predicate<Port>(){
public boolean apply(Port input) { return input.getDir() == Direction.IN ; }
};
public IScope scope_Connection_p2(Connection ctx, EReference eRef)
{
EList<Port> features = ctx.getIn2().getType().getPorts();
return scopeFor(Iterables.filter(features,rPortPred));
}
public IScope scope_Connection_p(Connection ctx, EReference eRef)
{
EList<Port> features = ctx.getIn2().getType().getPorts();
return scopeFor(Iterables.filter(features,pPortPred));
}
}
Note that the scopeFor helper takes away the work of creating the newly introduced IEObjectDescription that I struggled with at first. Once you know how, migrating seems not too bad.