avatar

Xtext and more

  • Impressum

Xtext and Strings as Cross-References

March 19, 2015 · Christian Dietrich · Tags: Cross-Reference , ProposalProvider , STRING , Xtext

Cross References are a often used concept in Xtext. They ususally work like this

Model:
    definitions+=Definition*
    usages+=Usage*
;
 
Definition:
    "define" name=ID
;
 
Usage:
   "use" definition=[Definition]
;

They can be used in the model like this

define Thing
use Thing

but what if i want to write something like

define "This is a Thing"
use "This is a Thing"

well the definition part is easily changed

Definition:
    "define" name=STRING
;

But what about the usage part? well it is quite easy as well. refName=[Type] is short for refName=[Type|ID] which means ‘Refererence a Type and parse an ID. So to use another Terminal or Data Type Rule we change it to refName=[Type|RULENAME]

Usage:
   "use" definition=[Definition|STRING]
;

Now the cross refs are working fine. but if we try the editor we find out what autoedit and content assist disturb each other. We type " and auto edit gets us to “|”. If we now type Crtl+Space for content assist we finally get “This is a Thing”" with an extra " at the end. To avoid this we have to tweak the proposal provider a bit.

package org.xtext.example.mydsl.ui.contentassist
 
import org.xtext.example.mydsl.ui.contentassist.AbstractMyDslProposalProvider
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.Assignment
import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext
import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor
import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor.Delegate
import org.eclipse.jface.text.contentassist.ICompletionProposal
import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal
 
class MyDslProposalProvider extends AbstractMyDslProposalProvider {
 
    override completeUsage_Definition(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
        super.completeUsage_Definition(model, assignment, context, new  StringProposalDelegate(acceptor, context))
    }
 
    static class StringProposalDelegate extends Delegate {
 
        ContentAssistContext ctx
 
        new(ICompletionProposalAcceptor delegate, ContentAssistContext ctx) {
            super(delegate)
            this.ctx = ctx
        }
 
        override accept(ICompletionProposal proposal) {
            if (proposal instanceof ConfigurableCompletionProposal) {
                val endPos = proposal.replacementOffset + proposal.replacementLength 
                if (ctx.document != null && ctx.document.length > endPos) {
                    // We are not at the end of the file
                    if ("\"" == ctx.document.get(endPos, 1)) {
                        proposal.replacementLength = proposal.replacementLength-1
                        proposal.replacementString = proposal.replacementString.substring(0,proposal.replacementString.length-1)
                    }
                }
            }
            super.accept(proposal)
        }
 
    }
 
}

what we basically do is detecting the situation and remove the extra "

Xtext: Referencing Elements of one DSL from another DSL

August 7, 2012 · Christian Dietrich · Tags: Cross-Reference , Import , Inter-Language , Xtext

This is a blog post on Inter-Language-Cross-References in Xtext. Let us asume we have a DSL that contains Definitions. Now we want to create another DSL that references (uses) the Definitions defined in the the first DSL.

So let us start with the first DSL: We create a new Xtext Project

New Project Wizard First DSL

And here is the (for demonstration purpose oversimplyfied) Grammar

grammar org.xtext.example.definitions.Definitions with org.eclipse.xtext.common.Terminals
 
generate definitions "http://www.xtext.org/example/definitions/Definitions"
 
Model:
    definitions+=Definition*;
 
Definition:
    'define' name=ID;

We run GenerateDefinitions.mwe2 to generate the language. This is all for the first DSL.

Now let us create a project for the second DSL.

New Project Wizard Second DSL

To be able to reference the first DSL from the second we add a bundle dependency to the Manifest of the second

Dependencies

Here the resulting manifest

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: org.xtext.example.usages
Bundle-Vendor: My Company
Bundle-Version: 1.0.0.qualifier
Bundle-SymbolicName: org.xtext.example.usages; singleton:=true
Bundle-ActivationPolicy: lazy
Require-Bundle: org.eclipse.xtext;visibility:=reexport,
 org.eclipse.xtext.xbase;resolution:=optional;visibility:=reexport,
 org.eclipse.xtext.generator;resolution:=optional,
 org.apache.commons.logging;bundle-version="1.0.4";resolution:=optional,
 org.eclipse.emf.codegen.ecore;resolution:=optional,
 org.eclipse.emf.mwe.utils;resolution:=optional,
 org.eclipse.emf.mwe2.launch;resolution:=optional,
 org.xtext.example.definitions;bundle-version="1.0.0"
Import-Package: org.apache.log4j
Bundle-RequiredExecutionEnvironment: J2SE-1.5

Now we can create the Grammar from the Usage DSL

grammar org.xtext.example.usages.Usages with org.eclipse.xtext.common.Terminals
 
generate usages "http://www.xtext.org/example/usages/Usages"
 
import "http://www.xtext.org/example/definitions/Definitions" as def
 
Model:
    usages+=Usage*;
     
Usage:
    'use' definition=[def::Definition];

With "http://www.xtext.org/example/definitions/Definitions" as def we import the metamodel of our Define DSL to our Usage DSL. Thus the Type Definition is available and can be used to define the cross reference definition=[def::Definition]

To get the thing running we have to do some adjustments to the workflow of the Usages language.

module org.xtext.example.usages.GenerateUsages
 
import org.eclipse.emf.mwe.utils.*
import org.eclipse.xtext.generator.*
import org.eclipse.xtext.ui.generator.*
 
var grammarURI = "classpath:/org/xtext/example/usages/Usages.xtext"
var file.extensions = "use"
var projectName = "org.xtext.example.usages"
var runtimeProject = "../${projectName}"
 
Workflow {
    bean = StandaloneSetup {
        scanClassPath = true
        platformUri = "${runtimeProject}/..";
        registerGeneratedEPackage = "org.xtext.example.definitions.definitions.DefinitionsPackage"
        registerGenModelFile = "platform:/resource/org.xtext.example.definitions/src-gen/org/xtext/example/definitions/Definitions.genmodel"
    }
     
        ...
}

we generate our Usage DSL GenerateUsages.mwe2

finally we start a new runtime eclipse and give it a try

Result

Update Xtext 2.9.2+:

Here is how the workflow looks like with the new Xtext 2.9.x generator

module org.xtext.example.usages.GenerateUsages
 
import org.eclipse.xtext.xtext.generator.*
import org.eclipse.xtext.xtext.generator.model.project.*
 
var rootPath = ".."
 
Workflow {
     
    component = XtextGenerator {
        configuration = {
            project = StandardProjectConfig {
                baseName = "org.xtext.example.usages"
                rootPath = rootPath
                runtimeTest = {
                    enabled = true
                }
                eclipsePlugin = {
                    enabled = true
                }
                eclipsePluginTest = {
                    enabled = true
                }
                createEclipseMetaData = true
            }
            code = {
                encoding = "UTF-8"
                fileHeader = "/*\n * generated by Xtext \${version}\n */"
            }
        }
        language = StandardLanguage {
            name = "org.xtext.example.usages.Usages"
            referencedResource = "platform:/resource/org.xtext.example.definitions/model/generated/Definitions.genmodel"
            fileExtensions = "use"
 
            serializer = {
                generateStub = false
            }
            validator = {
            }
        }
    }
}

© 2023 - Powered by Hugo with the Type Theme