enriching your DSL editor using code Templates
Code templates are a nice way to enrich code completion for your DSL editor, allowing the user to add and modify the proposals accoring to his/her own needs. As discribed in the Xtext documentation, templates for your DSL can be edited in its corresponding preference page. Starting with the sample grammar of a new Xtext project (0.7.0), I want to illustrate some simple principles for adding value to your editor via templates. Just to be safe, here is the grammar
Model : (imports+=Import)* (elements+=Type)*;
Import : 'import' importURI=STRING;
Type: SimpleType | Entity;
SimpleType: 'type' name=ID;
Entity : 'entity' name=ID ('extends' extends=[Entity])? '{' properties+=Property*'}';
Property: 'property' name=ID ':' type=[Type] (many?='[]')?;
Generete the Xtext artifacts, start a runtime workspace (or make your DSL and Editor available to Ecplipse in your favorite way), create a model file with some simple types and entities. Here is a sample model:
type string
type int
entity Entity1 {property longname:string}
entity Entity2 extends Entity1 {property age:int}
If you invoke code completion at the end of the file you get proposals for the keywords entity and type.
simple templates
Go to the dsl’s preference page (Eclipse preferences -> Xtext Languages -> YourDSL -> Templates) and create a new template. Suggested parameters: name = simpletemplate, description = a very simple template, context = Type, pattern = entity Entity3 {property nickname:string}. If you now invoke code completion at the end of the file, you should also see an entry for the template.
templates with simple variables
Variables are a nice way to parametrise a template. Create a template with the following parameters: name = simplevariables, description = a template with simple variables, context = Type, pattern = entity ${EntityName} {property ${PropertyName}:${Type}}. When invoking code completion at the end of your model file, you should now also see the variable template. Select it. Using the tabulator key you can now iterate through the positions defined by the variables and change the corresponding text. Use tab to get to the last variable (Type), invoke code completion (ctrl space) again and be amazed.
predefined variable types
A number of predefined variable types are supported by Xtext, among them one for proposing enums defined in the grammar and one for proposing cross-referenced elements. For those two, Xtext comes with default variable resolvers that cause several proposals for the corresponding variable to be displayed as a drop down list. Have a look at the Xtext documentation for a description how to use them.
I am now going to illustrate how to implement
your own template variable resolver.
It is a matter of writing the resolver, making it known via extending the XtextTemplateContextType and using that context type instead of the default one by adapting the existing UIModule. First the code for making the resolver known:
public class MyXtextTemplateContextType extends XtextTemplateContextType{
@Override
protected void addDefaultTemplateVariables() {
super.addDefaultTemplateVariables();
addResolver(new MyTemplateVariableResolver());
}
}
and
public class DslUiModule extends myproject.AbstractDslUiModule {
public Class bindIContextContentType() {
return MyXtextTemplateContextType.class;
}
}
MyTemplateVariableResolver has to override the method public List resolveValues(TemplateVariable variable, XtextTemplateContext xtextTemplateContext) which actually calculate the list of proposals.
public class MyTemplateVariableResolver extends
AbstractTemplateVariableResolver {
// for "registering" the variable
public MyTemplateVariableResolver() {
// the first parameter is the "type" of the variable
// under which it will be known when defining the template
super("ExampleVar",
"Sample variable type to illustrate customised variable resolution");
}
@Override
public List resolveValues(TemplateVariable variable,
XtextTemplateContext xtextTemplateContext) {
List result = new ArrayList();
result.add("EntityName");
result.add("anotherEntityName");
return result;
}
}
Create a new template with context = Type and pattern = entity ${ExampleVar}. Invoking code completion should now also give you a template where “EntityName” and “anotherEntityName” are proposed. The second example is to illustrate how variable parameters and context information can be used in the resolver; only the resolveValues method is modified.
// when using the same variable type within the same template, different
// names must be used, the parameters are a way to provide the variable
// resolver with additional information
@Override
public List resolveValues(TemplateVariable variable,
XtextTemplateContext xtextTemplateContext) {
// fetch name, type and parameters of the template variable
String variablename = variable.getName();
String variabletype = variable.getType();
List parameters = variable.getVariableType().getParams();
// fetch the context model element; i.e. the context of the
// template, not that of the position, where the variable
// is used
EObject semanticcontext =
xtextTemplateContext.getContentAssistContext().getCurrentModel();
// show variable name, type and parameters
List result = new ArrayList();
result.add("variablename: " + variablename);
result.add("variabletype: " + variabletype);
int i = 1;
for (Object o : parameters) {
result.add("param " + (i++) + ": " + (String) o);
}
// prove that we have access to the Model by printing out the type of
// the context element and print out the number of "elements" in the model
result.add("type of the context element: "
+ semanticcontext.eClass().getName());
if (semanticcontext instanceof Model) {
result.add("the model has "
+ ((Model) semanticcontext).getElements().size()
+ " elements");
}
return result;
}
Test the functionality of this variable resolver with a template having the following pattern: ${name1:ExampleVar('p1','p2','p3')} ${name2:ExampleVar('q1','q2')}.
shipping predifind templates with your editor
You can ship a set of templates with the UI plugin by creating a templates folder (as sibling of the src- and src-gen-folder) containing a templates.xml. Probably the simplest way would be to create the templates in Eclipse and export them. It is important that you have to add a parameter “id” to each template as otherwise they will not be shown.
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<template id="MANUALLYADDED_ID1"
autoinsert="true"
context="myproject.Dsl.Type"
deleted="false"
description="a very simple template"
enabled="true"
name="simpleTemplate"
>
entitiy Entity3 {property nickname:string}
</template>
</templates>
July 7th, 2009 at 2:45 pm
And yet another comprehensive post about Xtext. Thank you, Alex.