Home > Uncategorized > Configuring local and global scope providers at the same time

Configuring local and global scope providers at the same time

This post is about how to take control of configuration of the local and global scope providers through a custom scoping fragment.

In the .mwe2 workflow file (I’m assuming you’re on Xtext 1.0.1 and Eclipse Helios) the scoping is configured through an appropriate Xtext generator fragment. Xtext is shipped with an org.eclipse.xtext.generator.scoping.AbstractScopingFragment support class and two implementations (in the same Java package): ImportNamespacesScopingFragment and ImportURIScopingFragment.

AbstractScopingFragment binds the custom scope provider implementation MyDslScopeProvider (which inherits from org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider, by default) to the runtime (Guice module) configuration and provides two abstract methods to also declare which classes you want to bind to the runtime for the global scope provider and a delegate or ‘fall-back’ local scope provider which is injected into the custom scope provider (in the field AbstractDeclarativeScopeProvider#delegate). This scoping delegate is invoked in case the custom scope provider (as long as that still inherits from AbstractDeclarativeScopeProvider, which would typically be the case) happens to return a null scope. (Note that ‘local’ means nothing more than ‘confined to the current Resource‘ with ‘global’ being the opposite.)

The following figure attempts to sum up the default situation which you get out-of-the-box after creating an Xtext project (with the familiar language name ‘MyDsl’, in this case) and running the MWE2 workflow:

The MWE2 workflow file (in the 1st editor) specifies the ImportNamespacesScopingFragment as (only) scope fragment. The source for the fragment (2nd editor) shows that in this case-sensitive case, the delegate local scope provider is to be an instance of ImportedNamespaceAwareLocalScopeProvider and the global scope provider is to be an instance of DefaultGlobalScopeProvider. Indeed, the 3rd editor shows that IScopeProvider is bound to the custom local scope provider, the delegate field (which is appropriately tagged using a Guice Named annotation) to ImportedNamespaceAwareLocalScopeProvider and IGlobalScopeProvider to the DefaultGlobalScopeProvider class.

As you already can guess from the fragments’ source in the example above, the two default implementations of AbstractScopingFragment mentioned above both declare both a global scope provider class as well as a local delegate scope provider class, so when configuring both fragments in the DSL generator workflow, the bindings will override each other and order is important. This is particularly annoying in case you want both the namespace-based imports as well the global URI imports functionality -after all these don’t necessarily conflict. Also, you have to configure at least one scoping fragment in order to have the custom scope provider bound, which may mean that your DSL could have unwanted scoping properties.

The solution is simply to roll your own AbstractScopingFragment implementation and configure exactly what you need. Note that this is only possible since Xtext 1.0.1 aka SR1 since before that the org.eclipse.xtext.generator.scoping package this class resides in, was not exported from the plugin, so this class and the Xpand and Xtext files it needs are not accessible to the class loader. With any implementation of AbstractScopingFragment should come an Xpand file with the exact same name as the implementing class’ name and residing in the same package. This Xpand file allows you to execute additional templates, either as separate files or as “add-ins”. We just need to execute the one for the custom Java scope provider, so we can simply copy the ImportURIScopingFragment.xpt file from within the plugin and rename it appropriately. The following screenshot sums things up:

After modifying the Xtext generator workflow and running it again, the workflow file and binding situation now look as follows:

This is indeed what we wanted. Note that the two standard scoping fragments will usually suffice, since you can override basically anything in the custom scope provider. But, for full-blown language development as well as for language modularization it sure comes in handy to have ultimate control of the scoping architecture.

Advertisements
Categories: Uncategorized
  1. joost
    June 27, 2013 at 8:23 am

    This approach worked in 2.3.1, but now fails when generating with 2.4.2.

    11770 [main] ERROR g.eclipse.xtext.generator.Generator – No Definition my::project::scoping::MyScopingFragment::addToStandaloneSetup(String,Boolean,Boolean) for org::eclipse::xtext::impl::GrammarImpl could be found!
    EvaluationException : No Definition my::project::scoping::MyScopingFragment::addToStandaloneSetup(String,Boolean,Boolean) for org::eclipse::xtext::impl::GrammarImpl could be found!
    Internal error : element was null

    at org.eclipse.xpand2.XpandFacade.evaluate2(XpandFacade.java:59)
    at org.eclipse.xtext.generator.AbstractGeneratorFragment.addToStandaloneSetup(AbstractGeneratorFragment.java:65)

    • joost
      June 27, 2013 at 8:34 am

      Actually, this exception is thrown before that:

      8711 [main] ERROR enerator.CompositeGeneratorFragment – No Definition my::project::scoping::MyScopingFragment::generate(String,Boolean,Boolean) for org::eclipse::xtext::impl::GrammarImpl could be found!
      EvaluationException : No Definition my::project::scoping::MyScopingFragment::generate(String,Boolean,Boolean) for org::eclipse::xtext::impl::GrammarImpl could be found!
      Internal error : element was null

  2. June 27, 2013 at 10:30 am

    Hi Joost,

    There have been some changes between Xtext 2.3.x and 2.4.y – possibly because Xtext is eating its own Xtend dog food by porting the Xtext generator to Xtend(2) instead of the old Xpand. In fact, I’m a bit surprised this has worked beyond Xtext 1.a.b.

    The approach described in the post is not really necessary to achieve the same thing. Since everything surrounding Xtext is nicely Guice-managed you can simply override the bindings for the scope providers in the appropriate Guice module.

    Hope that helps!

    • Joost
      June 27, 2013 at 11:05 am

      Actually, if I look at the source code for the Scoping providers they are using, it seems that it is still not possible to do these with the Guice-binding. But perhaps that’s just because I do not fully understand that framework yet.

      The MWE2 fragments can still use the XPT files (through the XpandFacade), they just need to be a little different from yours (as the generate-methods now have a different signature).

      I’ve been able to solve it by looking at the templates they have provided in http://git.eclipse.org/c/tmf/org.eclipse.xtext.git/plain/plugins/org.eclipse.xtext.generator/src/org/eclipse/xtext/generator/scoping/ especially ImportURIScopingFragment.xpt helped me fix my own template.

      If there really is a better (more future-proof) way to bind my scope providers, then I would appreciate you detailing this in a new blog post 😉

      • June 27, 2013 at 11:35 am

        It should be possible to just override the bindings with what you want. What do you see as obstruction?

        I don’t do too much with Xtext these days (Xtend is another story, altogether), so I don’t have a short-time incentive to come up with a new blog post on this. However, I could make it an addendum to this one.

      • Joost
        June 27, 2013 at 11:50 am

        I believe the way the Guice bindings have to be created looks the same as in http://git.eclipse.org/c/tmf/org.eclipse.xtext.git/plain/plugins/org.eclipse.xtext.generator/src/org/eclipse/xtext/generator/ImplicitRuntimeFragment.java, but this one inherits from DefaultGeneratorFragment, and the Scoping.ImportURIScopingFragment inherits AbstractScopingFragment. These abstract fragments (including the validationFragment, etc) have not been refactored to use the new Guice bindings yet. It seems not to be a priority (as they e.g. have updated the signatures in the XPT files) to move this functionality to the new construction.

        Please correct me if I’m wrong! (At least now this information is out on the web!)

      • June 27, 2013 at 12:17 pm

        But DefaultGeneratorFragment c.s. are not Guice modules, while the MyDslRuntimeModule is one. So if you add the correct binding code to MyDslRuntimeModule, overriding the wrong binding code in AbstractMyDslRuntimeModule, then you should be OK.

      • Joost
        June 27, 2013 at 1:16 pm

        Somehow, I cannot wrap my head around this yet. At least, I can’t get it to work atm. If you get some time, adding a full example to this blog post would be very helpful and highly appreciated.

  3. Anudeep
    June 2, 2016 at 9:57 am

    Hi ,

    I have tried to implement the same since i want all the references global and local.

    But when i give ImportUriGlobalScopeProvider.class in globalscopeprovider in CustomScopingFragment,global scoping fails..

    and when i give DefaultGlobalScopeProvider ,gloal scope works fine but local scope fails(Not exactly the same but something similar,scoping doesn’t happen properly)

    Can you please provide some inputs on this.

    Regards,
    Anudeep.

    • June 6, 2016 at 7:21 am

      Hi Anudeep,

      I rarely use this way of configuring scope providers anymore, but instead take control of all scoping through custom local scoping, typically leaving global scoping out of the equation.

      Hope that helps,

      Regards,

      Meinte

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: