Xtext cross references and scoping – an overview (Part 2)

Overview

The Out-of-the-box/Usability Trade-off

Scopes are used by a number of components (most notably linking and content assist). This often tempts developers to implement the scope provider such that only objects that are indeed allowed to be linked are put in the scope. After all, then validation and code completion need not be adapted at all (hooray! everything works out of the box), as only valid links can be established and only valid links are proposed. However, scopes are not about validity, they are about visibility.

An analogous problem is grammar design itself. There may be a restriction that entity names have to start with an upper case letter. You can enforce that within the grammar, e.g. by defining a corresponding terminal rule. However, this will make the grammar error prone and the error messages for the user will not be very helpful. There might be a restriction that the value of a certain feature may only be between 5 and 49. Go ahead and put that into the grammar (it’s possible after all), you will have plenty of fun (in particular with other integer value features). Obviously, there are often several ways to achieve something and choices must be made.

The decision what to put into the scope should be made very carefully. There is no simple rule to be followed. Here are a few guide lines, I find sensible:

Single user language

If you develop a language/IDE for yourself only, take the path simplest for you. You know the language, you know the intended meaning, you (should) know why a cross reference could not be resolved. There is nobody who can argue that validity and visibility are not the same.

Multi user language

If you develop a language/IDE for use by somebody else, think thoroughly about usability. Usually, the user should be provided with a message specifically why a certain cross reference is invalid (you cannot extend entity X because it is final, the type of feature Y must implement a method Z and the type you try to use does not do so).
Of course this is a lot of work. You may still have to adapt scoping (reduce the number of visible objects, but not too much), adapt validation (check semantic constraints that must be satisfied), adapt code completion (suggest only valid objects). However, you can provide more meaningful error messages and specific quick fixes (great! still more work). And don’t underestimate how stupid your editor may look.

variable x;
variable y=x+3; /*could not resolve reference to x 
developer's insider knowledge: only initialised 
variables may be used on the right hand side of an assignment)
user reaction: st*&%/$/"ยง)..., look at the line above*/

“Follow up references”

A use case, where a drastic narrowing of the scope is almost always OK, is a reference that “follows” another one. Take feature calls as an example.

myType.property

If you already established a link to myType and now only properties of that type are valid, it makes no sense, having all other possible properties on the scope. On seeing a “could not resolve reference” error, the user can navigate to myType and see that there is indeed no such property.

2 Responses to “Xtext cross references and scoping – an overview (Part 2)”

  1. srilatha Says:

    Hi,

    I need to do cross referencing in my xtext grammar, and the grammar is very similar to the grammar in this link:
    http://stackoverflow.com/questions/11693120/xtext-cross-referencing-and-scoping

    ie., the grammar is:

    Model: cs+=Company* block=Block?;
    Block: g=[Company] ‘.’ e=[Employee] ;
    Company: ‘Company’ name=ID
    ‘{‘ es+= Employee* ‘}’;
    Employee: ‘Employee’ name=ID ‘;’ ;

    With the above grammar, I can have multiple companies and each company can have multiple employees.

    Company Sony{
    Employee sony_emp1;
    Employee sony_emp2;
    }

    Company philips {
    Employee philips_emp1;
    Employee philips_emp2;
    }

    when user enters sony.xxxx I need cross referencing or auto completion for the employees listed under sony only, and not both sony and philips..Can anyone suggest me how I can achieve this?

    I had already changed QualifiedNamesFragment to SimpleNamesFragment as the above link suggests.
    Did not understand how to do what is suggested in second point ie., “Alternatively, you could customize the scoping for the concrete reference ‘g’ in your scope provider.”

    Thanks,
    Sri.

  2. Alexander Nittka Says:

    Hi,
    typically you should use the qualified name fragment. With the following grammar change the linking works out of the box. There you would reference the Employee directly, not via a prior reference to the company:

    Block: e=[Employee|Fqn];
    Fqn:ID('.'ID)*;

    If you want to use your grammar, you have to adapt the scoping, making visible only those employees of the already referenced company. To do so, edit the <Mydsl>ScopeProvider in the grammar project, adding the following method:

    IScope scope_Block_e(Block b, EReference ref){
    return Scopes.scopeFor(b.getG().getEs());
    }

    It means: if you are looking for the visible elements in the Block class for the feature e (Employee), take the context element b (the Block), navigate to the company (stored in the feature g) and use the employees defined there. “Scopes” is a utility class allowing you to create scopes (here using a list of elements that should be visible) for standard cases as this.

    Alex

    P.S.: The would be the better place to ask this type of question.

Leave a Reply