Another Example: Syntax Highlighting
The preceding discussion gives you a basic idea of how to customize components in our language's infrastructure. The next example is syntax highlighting: Currently, only keywords are highlighted in the editor. We want to display DataTypes and references to them in italics to better distinguish them from Entities.
Syntax highlighting is defined in two components. The IHighlightingConfiguration defines text styles and maps them to IDs. These IDs are used in the ISemanticHighlightingCalculator to apply the text styles to regions in the text.
As we want to extend the default behavior, our DomainmodelHighlightingConfiguration should extend the DefaultHighlightingConfiguration. The following code should be clear, especially if you have a look at the superclass: We define a new text style and register it with the ID datatype.
public class DomainmodelHighlightingConfiguration extends
DefaultHighlightingConfiguration
{
public static final String DATA_TYPE_ID = "datatype";
public void configure(IHighlightingConfigurationAcceptor
acceptor)
{
super.configure(acceptor);
acceptor.acceptDefaultHighlighting(
DATA_TYPE_ID, "DataType", dataTypeTextStyle());
}
public TextStyle dataTypeTextStyle()
{
TextStyle textStyle = defaultTextStyle().copy();
textStyle.setStyle(SWT.ITALIC);
return textStyle;
}
}
Next, we have to tell Xtext to use our implementation. We only have to add a binding in the DomainmodelUiModule:
public Class<? extends IHighlightingConfiguration>
bindIHighlightingConfiguration() {
return DomainmodelHighlightingConfiguration.class;
}
If you restart your runtime workbench, you should already have a configuration item in the properties page for your language (Figure 3).
To apply the new DataType text style to the corresponding regions in the text, implement the ISemanticHighlightingCalculator. This time there is no default implementation. We need to implement a single method provideHighlightingFor(XtextResource, IHighlightedPositionAcceptor). The first parameter is the resource holding the model. We have to iterate its contents the model elements and spot all DataTypes and Features pointing to a DataType.
public class DomainmodelSemanticHighlightingCalculator
implements ISemanticHighlightingCalculator
{
public void provideHighlightingFor(XtextResource resource,
IHighlightedPositionAcceptor acceptor)
{
for(Iterator<EObject> contents = resource.getAllContents();
contents.hasNext(); )
{
EObject element = contents.next();
if (element instanceof Feature &&
((Feature) element).getType() instanceof
DataType)
highlightAsDataType(element,
DomainmodelPackage.Literals.FEATURE__TYPE,
acceptor);
else if(element instanceof DataType)
highlightAsDataType(element,
DomainmodelPackage.Literals.TYPE__NAME,
acceptor);
}
}
protected void highlightAsDataType(EObject element,
EStructuralFeature feature,
IHighlightedPositionAcceptor acceptor)
{
for(INode node:
NodeModelUtils.findNodesForFeature(element, feature))
acceptor.addPosition(node.getOffset(),
node.getLength(),
DomainmodelHighlightingConfiguration.DATA_TYPE_ID);
}
}
The NodeModelUtils provide access to the node model, which stores trace information of the parser. We use it to determine the text region (offset and length) from which a model element was parsed.
DomainmodelPackage.Literals.FEATURE__TYPE and DomainmodelPackage.Literals.TYPE__NAME is the EMF way to say "the property type of the class Feature" ("...name of the class Type"). The text style is applied to a region IHighlightedPositionAcceptor.addPosition().
What is missing now? You guessed it: We have to add the binding for the If you restart the runtime Eclipse the editor shows the desired highlighting (Figure 4).
In this tutorial, we focused on UI aspects of Xtext. Infrastructural parts, such as scoping, linking, and formatting can be changed with the same mechanism. As always, you will find many more examples in the Xtext documentation.
In this article, we've discussed the basic mechanisms to customize an Xtext-based DSL. In Xtext, everything is wired up using dependency injection. You can easily customize individual components by changing the bindings if the defaults do not fit your needs. Whenever you need another component, you just let the framework inject it. For the aspects you customize, Xtext likely offers generator fragments that create skeleton classes and their bindings. By using the Generation Gap Pattern, you can evolve your language and regenerate the infrastructure without losing your customizations. Enjoy!
Jan Koehnlein and Sebastian Zarnekow are principals on the Xtext project.
ISemanticHighlightingCalculator to our DomainmodelSemanticHighlightingCalculator/ in the UI module:
public class DomainmodelUiModule
extends org.example.domainmodel.ui.AbstractDomainmodelUiModule
{
...
public Class<? extends ISemanticHighlightingCalculator>
bindISemanticHighlightingCalculator()
{
return DomainmodelSemanticHighlightingCalculator.class;
}
}
Where to Go from Here...


