Kostya
Kostya

Reputation: 1102

Simple import instruction in XText

I want to add some of the objects from another file in XText to the scope. To illustrate what I need, I have this toy grammar:

grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"

Model:
   (imports += Import)*
   (classes += ClassDef )*;

Import:
    'import' filename += STRING;    

ClassDef:
    'class' name=ID ('extends' extends=[ClassDef])?;

Lets say I want to be able to extend only the classes that are defined before a given class, and only those that start with the same first letter as the class being defined.

A recommended way of doing that is via scoping. Here is the scope provider that does it within a single file:

package org.xtext.example.mydsl.scoping
import ....

class MyDslScopeProvider extends AbstractMyDslScopeProvider {
    override getScope(EObject context, EReference reference){
        if(reference ==  MyDslPackage.Literals.CLASS_DEF__EXTENDS){
            if(! (context instanceof ClassDef) ) {return IScope.NULLSCOPE }
            val root = (context as ClassDef).getRootContainer
            val classes = root.getAllContentsOfType(ClassDef)
            val before = classes.subList(0, classes.indexOf(context))
            val fstLetter = (context as ClassDef).name.charAt(0)
            val haveSame1stLetter = before.filter[name.charAt(0) == fstLetter]          
            return Scopes::scopeFor(haveSame1stLetter)
        }
    }
}

I want to be able to access the class declarations from imported files like this:

// --- c ---
class Xlib
class Xextend extends Xlib

// --- main.mydsl ---
import "lib.mydsl"
class A
class Xmain extends Xlib

I've found some conflicting suggestions about using importURI thing, but it doesn't seem to work for me and seems to be and outdated way of doing things now.

The documentation mentions that for cross-resource references I probably need to:

... and many more other things. Unfortunately, they do not add up into a coherent picture of what I have to do to a achieve a desired behavior.


Edit: Following Sven's suggestion I've come up with the solution. If anyone is interested -- I've posted the code in this gist.

Upvotes: 2

Views: 1413

Answers (2)

Sven Efftinge
Sven Efftinge

Reputation: 3095

There are decent default implementations for all the services you mentioned, so you don't need to implement them unless you want to change how they work.

It is indeed discouraged to use the importUri mechanism, but instead you should use name-based linking, which is more reliable.

Xtext automatically maintains an index for you that contains globally visible qualified names. There are two sides of the index :

1. Contributing to the Index

During indexing the ResourceDescriptionManager is asked for named elements. The default implementation will contribute any elements that have a property name. If you want to change that behavior you can subclass DefaultResourceDescriptionStrategy and override createEObjectDesciptions. For instance you could export the root element of your model using the simple name of the resource, to allow for

Import :
   'import' referencedModel+=[Model|STRING]
;

This would only allow importing files, if you want to support importing files from different folders, I'd recommend to use namespaces.

namespace foo;
type Bar {}

other file

import foo.Bar
type Foo extends Bar

2. Using the Index

The scope provider automatically delegates to the GlobalScopeProvider which will provide the descriptions from the index. So if you make sure that the elements are index properly (see 1.) they will end up in the scope provider automatically.

Upvotes: 1

Christian Dietrich
Christian Dietrich

Reputation: 11868

Hi I cannot really follow you. Either you simply use import Uri although it is discouraged https://www.eclipse.org/forums/index.php?t=msg&th=1078818&goto=1736351&#msg_1736351 or you use namespace based scoping (don't know if you want to support relative imports or fancy Uri schemes ) by adapting the name provider (give the element the name .elementname) and importednamespaceawarelocalscopeprovider (turn Import xxx to a Import xxx.*)

Can you please give more hints on your requirements

Upvotes: 1

Related Questions