IDE Concepts

For the following part we will refer to a concrete example grammar in order to explain certain aspect of the UI more clearly. The used example grammar is as follows:

grammar org.eclipse.text.documentation.Sample 
    with org.eclipse.xtext.common.Terminals

generate gen 'http://www.eclipse.org/xtext/documentation/Sample'

Model :
  "model" intAttribute=INT (stringDescription=STRING)? "{" 
     (rules += AbstractRule)* 
  "}" 
;

AbstractRule:
  RuleA | RuleB
;

RuleA :
   "RuleA" "(" name = ID ")" ;

RuleB return gen::CustomType:
   "RuleB" "(" ruleA = [RuleA] ")" ;

Label Provider

There are various places in the UI in which model elements have to be presented to the user: In the outline view, in hyper links, in content proposals, find dialogs etc. Xtext allows to customize each of these appearances by individual ILabelProviders.

An ILabelProvider has two methods: getText(Object) returns the text in an object’s label, while getImage(Object) returns the icon. In addition, the Eclipse UI framework offers the IStyledLabelProvider, which returns a styled string (i.e. with custom fonts, colors etc.) in the getStyledText(Object) method.

Almost all label providers in the Xtext framework inherit from the base class AbstractLabelProvider which unifies both approaches. Subclasses can either return a styled string or a string in the doGetText(Object) method. The framework will automatically convert it to a styled text (with default styles) or to a plain text in the respective methods.

Dealing with images can be cumbersome, too, as image handles tend to be scarce system resources. The AbstractLabelProvider helps you managing the images: In your implementation of doGetImage(Object) you can as well return an Image or a string, representing a path in the icons/ folder of the containing plug-in. This path is actually configurable by Google Guice. Have a look at the PluginImageHelper to learn about the customizing possibilities.

If you have the LabelProviderFragment in the list of generator fragments in the MWE2 workflow for your language, it will automatically create stubs and bindings for an EObjectLabelProvider and an DescriptionLabelProvider which you can implement by manually.

Label Providers For EObjects

The first set of label providers refers to actually loaded and thereby available model elements. By default, Xtext binds the DefaultEObjectLabelProvider to all use cases, but you can change the binding individually for the Outline, Content Assist or other places. For that purpose, there is a so called binding annotation for each use case. For example, to use a custom MyContentAssistLabelProvider to display elements in the content assist, you have to override

@Override
public void configureContentProposalLabelProvider(Binder binder) {
  binder.bind(ILabelProvider.class)
    .annotatedWith(ContentProposalLabelProvider.class)
    .to(MyContentAssistLabelProvider.class);
}

p.in your language's UI module. 

If your grammar uses an imported EPackage, there may be an existing .edit plug-in generated by EMF that also provides label providers for model elements. To use this as a fallback, your label provider should call the constructor with the delegate parameter and use DI for the initialization, e.g.

public class MyLabelProvider {
@Inject
public MyLabelProvider(AdapterFactoryLabelProvider delegate) {
super(delegate);
}
...
}  

DefaultEObjectLabelProvider

The default implementation of the LabelProvider interface utilizes the polymorphic dispatcher idiom to implement an external visitor as the requirements of the LabelProvider are kind of a best match for this pattern. It comes down to the fact that the only thing you need to do is to implement a method that matches a specific signature. It either provides a image filename or the text to be used to represent your model element. Have a look at following example to get a more detailed idea about the DefaultEObjectLabelProvider.

public class SampleLabelProvider extends DefaultLabelProvider {

  String text(RuleA rule) {
    return "Rule: " + rule.getName();
  }
  
  String image(RuleA rule) {
    return "ruleA.gif";
  }
  
  String image(RuleB rule) {
    return "ruleB.gif";
  }
}

What is especially nice about the default implementation is the actual reason for its class name: It provides very reasonable defaults. To compute the label for a certain model element, it will at first have a look for an EAttribute name and try to use this one. If it cannot find such a feature, it will try to use the first feature, that can be used best as a label. At worst it will return the class name of the model element, which is kind of unlikely to happen.

You can a also customize error handling by overriding the methods handleTextError() or handleImageError().

Label Providers For Index Entries

Xtext maintains an index of all model elements to allow quick searching and linking without loading the referenced resource (See the chapter on index-based scopes for details). The elements from this index also appear in some UI contexts, e.g. in the Find model elements dialog or in the Find references view. For reasons of scalability, the UI should not automatically load resources, so we need another implementation of a label provider that works with the elements from the index, i.e. IResourceDescription, IEObjectDescription, and IReferenceDescription.

The default implementation of this service is the DefaultDescriptionLabelProvider. It employs the same polymorphic dispatch mechanism as the DefaultEObjectLabelProvider. The default text of an EObjectDescription is its indexed name. The image is resolved by dispatching to image(EClass) with the EClass of the described object. This is likely the only method you want to override. IResourceDescriptions will be represented with their path and the icon registered for your language’s editor.

To have a custom description label provider, make sure it is bound in your UI module:

public void configureResourceUIServiceLabelProvider(Binder binder) {
  binder.bind(ILabelProvider.class)
    .annotatedWith(ResourceServiceDescriptionLabelProvider.class)
    .to(MyCustomDefaultDescriptionLabelProvider.class);
}