Post processing vs Post initialization

Sometimes it is useful to be able to modify an EObject right after its creation, in runtime.
With “modify” we can mean several things: change the metamodel of the model (adding for example a feature or an operation) or change the values of the model (setting the value of a given feature) or both.
For clarification I shall call the second case “post initialization” to differentiate it with the post processing step.

Read more of this post

My proposals, my order

Xtext provides us with a very useful content assist out of the box:





However, what if we want to change the order of the proposed elements?
In Xtext the ordering mechanism of these elements is broken in three parts: the assignment of a number that represents the priority of one element (implementation of IContentProposalPriorities), the availment of those numbers/priorities to achieve a sort logic (implementation of ICompletionProposalComparator) and the storing of the priority number in a ConfigurableCompletionProposal, one for each proposal; this class also deals with the default comparison logic, which compares the two priorities first, then the two display string if no outcome is achieved from the two numbers.

Also, can we change the order of the proposals, according to the state of the model? In other words, what we have to do to assign the priority based upon the model?
In fact, it seems that the sort order is not aware of “where” (related to the model) we are:


Read more of this post

Hovertext

Warning: this post contains deprecated informations. Hovering is fully implemented in the new Xtext 2.0. I strongly suggest you to upgrade if you haven’t already done it. I write this only as a memento of my efforts, I’ll scrap everything and switch to the new Xtext as soon as possible. Please, read Christoph’s post if you want to know more.

Imagine that we want to display a comment of an element of our grammar in a rectangle, with the element selected through a mouse hover (in other words, like the JDT), how can we implement that? Digging through the Xtext and Eclipse api, we should find everything that we need.
It looks like in Eclipse each TextEditor holds a SourceViewConfiguration which is the main entry point for all the UI customization of the editor. The SourceViewConfiguration is in charge, amongst other things, to provide an implementation of ITextHover which in turn provide the Object that holds the informations we want to display through a mouse hover. The SourceViewConfiguration provides an IInformationControlCreator as well, that is responsible of the creation of an IInformationControl, that displays the ITextHover‘s informations.
There are many resources that explain how the Eclipse hovering mechanism works, for instance Prashant Deva’s article or the Eclipse Wiki.
Read more of this post

Brand new page!

I have created a page with links about Xtext. You can find it in the menu, under “Pages”, of course.
Here is a link.

Happy subscriptions!

We’d like to know a little bit about you for our files

When we trig the content assist (with CTRL+Space) at a cross-reference position, we will see all the possible candidates. Imagine now that we want to add a little piece of information to each element’s label, for instance the name of the file that contains that element, how can we do that?
The ProposalProvider (MyDslProposalProvider) comes to our rescue, again. If we look in one of its super class, the AbstractJavaBasedContentProposalProvider class, we will see that it has the factory that creates the proposals, called DefaultProposalCreator. In this class the method getStyledDisplayString is called to get the styled string to display. So we have to override this method accordingly. Remember that we don’t want to replace the label, we want to append something to it. Here is the code:
MyDslProposalProvider.java

@Override
protected StyledString getStyledDisplayString(EObject element,
                                                 String qualifiedName,
                                                 String shortName)
      {
      //Get the display string of the element. We want to append something to it.
      String display = getDisplayString(element, qualifiedName, shortName);
      //Get the uri of the element. We will use it to compute the file name.
      org.eclipse.emf.common.util.URI uri;
      if(element.eIsProxy())//If it is an eIsProxy, get the proxy uri. In this case .eResource() returns null.
         uri = ((InternalEObject)element).eProxyURI();
      else
         uri = element.eResource().getURI();
      //Get the URI as a String.
      String toAppend = uri.toString();
      //Now trim & cut the string, so that only the desired information remain.
      int start = (toAppend.indexOf("resource/"));
      start+=9;//"resource/" has 9 characters.
      int end = toAppend.length();
      if(element.eIsProxy())
         end = toAppend.indexOf('#');
      toAppend = toAppend.substring(start, end);
      //Now build the styled string with the computed string.
      StyledString styled = new StyledString(display);
      //We use a pre-built style to make it quick.
      styled.append(new StyledString(" <-> "+toAppend, StyledString.QUALIFIER_STYLER));
      return styled;
      }

Here is the result:

No typing is good typing!

In the grammar we wrote some posts ago, the rule Room is formed by two separate keywords: 'OF' and 'TYPE'. For some reasons (you can think of them as some sort of weird requisite) we had to break the keywords in two distinct rules. That separation caused two immediate effects:

  1. Beetween 'OF' and 'TYPE' we allow an arbitrary number of hidden tokens (white space, end of line, ecc.).
  2. We have to call the content assist twice: one time for each keyword.

What if we want to call the content assist only once, without changing the grammar? In that way we also easly maintain the first effect. We have to customize how the content assist works, inserting 'TYPE' along with 'OF', when we trig the assistance at the position of 'OF'. Xtext gives us a class for this purpose, a ProposalProvider (MyDslProposalProvider), that extends AbstractMyDslProposalProvider. Of all the methods, there is one dedicated to the completion of keywords called completeKeyword where we will write our solution. The solution is break in two parts: first we have to check that we are calling the assist at the 'OF' position, then we have to build the new proposal that will appear in the content assist. Xtext API and Eclipse API will give us all the tools needed for the task. Here is the code:
MyDslProposalProvider.java

@Override
public void completeKeyword(Keyword keyword,
                                              ContentAssistContext contentAssistContext, 
                                              ICompletionProposalAcceptor acceptor)
   {
   //Call the super version: we still want all the old proposals.
   super.completeKeyword(keyword, contentAssistContext, acceptor);
   if(keyword.getValue().compareTo("OF") == 0)
      {
      //Get the last Node.
      AbstractNode lastNode = contentAssistContext.getLastCompleteNode();
      //Get the EObject from the Node.
      EObject precModel = NodeUtil.findASTElement(lastNode);
      //Identify the EObject, here we can use various criteria.
      if(precModel instanceof Room && //What kind is it?
         precModel.eContainingFeature().getName().compareTo("rooms")==0 && //Where is contained?
         precModel.eContainer() instanceof Structure) //Which is its parent?
         {
         //The new proposal.
         String proposal = "OF TYPE";
         //Create the proposal using the minimum version of the creation method.
         ICompletionProposal completion = createCompletionProposal(proposal, contentAssistContext);
         //Register the completion.
         acceptor.accept(completion);
         }
      }
   }

Now calling the content assist at the 'OF' position should show something like this:

Change the outline, part 3

You can judge an outline by its outfit

Time for some make up!

Changing the icons of the outline is quite easy, just write the right methods in the LabelProvider (MyDslLabelProvider) class. Xtext expect a method named image which returns a String and has the element of our model as the sole parameter. That element is the one that we want to define the icon in the outline and the String is the name of the file containing the image. The default implementation expects that the images are in a folder, of the plug-in, called icons. If you don’t like this behaviour you can still customize it through Google Guice, changing the proper member of PluginImageHelper.
Here are the project’s folders, with the icons folder:

Don’t forget to set that folder to be included in the binary build (in plugin.xml)!

With everything in place, just edit the LabelProvider:
MyDslLabelProvider.java

...
String image(TypeDeclaration type)
   {
   return "yellow_square.gif";
   }
   
String image(Room room)
   {
   return "red_square.gif";
   }
   
String image(FurnitureDeclaration furn)
   {
   return "green_square.gif";
   }
   
String image(Share share)
   {
   return "blue_square.gif";
   }
   
String image(RoomReference roomRef)
   {
   //Of course, you can return different icons according to the model's state.
   String featName = roomRef.eContainmentFeature().getName();
   if(featName.compareTo("first")==0)
      return "one_square.gif";
   return "two_square.gif";
   }
...

Now the outline looks like this:


Changing the style of a label of the outline, again, is not hard: each ContentOutlineNode holds a StyledString that we can customize. Just override the relative createNode method in the Transformer class, updating the StyledString. A StyledString need a StyledString.Styler that apply the styles. To build that we need a font and a color, each one registered in the right ResourceRegistry (FontRegistry and ColorRegistry). We have some initialization to do, so we explicit the default constructor of MyDslTransformer annotating it with @Inject, to direct it to Google Guice.
Here is the code for all these things:

MyDslTransformer.java

...
/**
  * We want a separate styler to get the bold font.
  */
protected StyledString.Styler roomStyler;
   
@Inject//For Google Guice.
public MyDslTransformer()
   {
   //Put the colors that we need later. Use Class names as keys.
   JFaceResources.getColorRegistry().put(TypeDeclaration.class.getSimpleName(), new RGB(255, 0, 0));
   JFaceResources.getColorRegistry().put(Room.class.getSimpleName(), new RGB(255, 100, 0));
   //Get the default font height. We get only the first element of the array.
   int defHeight = (int)JFaceResources.getFontRegistry().defaultFont().getFontData()[0].height;
   //Create the font data so that it becomes bold.
   FontData fontData = new FontData(Room.class.getSimpleName(), defHeight, SWT.BOLD);
   //Put the font in the registry.
   JFaceResources.getFontRegistry().put(Room.class.getSimpleName(), new FontData[]{fontData});
   roomStyler = new StyledString.Styler()
      {
      @Override
      public void applyStyles(TextStyle textStyle)
         {
         //Retrieve the registered font and color.
         textStyle.font = JFaceResources.getFontRegistry().get(Room.class.getSimpleName());
         textStyle.foreground = JFaceResources.getColorRegistry().get(Room.class.getSimpleName());
         }
      };
   }
	
public ContentOutlineNode createNode(TypeDeclaration semanticNode, ContentOutlineNode outlineParentNode)
   {
   ContentOutlineNode node = super.newOutlineNode(semanticNode, outlineParentNode);
   //Update the StyledString for all the label.
   node.getStyledString().setStyle(0, node.getLabel().length(), StyledString.createColorRegistryStyler(TypeDeclaration.class.getSimpleName(), null));
    return node;
    }

public ContentOutlineNode createNode(Room semanticNode, ContentOutlineNode outlineParentNode)
    {
   ContentOutlineNode node = super.newOutlineNode(semanticNode, outlineParentNode);
   //Set the styler with the one created to all the label.
   node.getStyledString().setStyle(0, node.getLabel().length(), roomStyler);
   return node;
   }
...

Now, your outline should looks like this: