Tutorial – Building A Language Server For Your DSL

Hey there, this is Christian.

VS Code‘s Language Server Protocol opens up a new horizon of programming IDE development. And there’s good news: Eclipse Xtext 2.11 will support you in building up a language server for your DSLs.
With this post I want to demo that by means a pre-release snapshot starting with a blank desk. I gonna tell you about

  • setting up an Eclipse-based development IDE
  • creating and configuring the required Xtext projects
  • testing the language server supporting your DSL

The Eclipse-based development IDE

So lets get started with the development IDE. You can use either the Eclipse Installer or, like me, choose a pre-build IDE. I suggest the OxygenM4 build of the “Eclipse IDE for Java Developers” package. Once downloaded, extracted, and running install the Xtext Complete SDK from

If you don’t use an Oxygen package, make sure to have the latest milestone of Buildship installed, you’ll get it from http://download.eclipse.org/buildship/updates/e46/milestones/2.x/.

Creating the Xtext projects

With Xtext installed create a new Xtext Project via File → New → Project…. On the Advanced Xtext Configuration page you have to switch to the Gradle build system. Besides you may want to deactivate the Eclipse Plugin.


After finishing the wizard you find your workspace like this:


For simpler orientation in the workspace I suggest to switch to the Project Explorer that is able to present the projects in a hierarchical fashion.


Now invoke the Xtext code generator as usual, e.g. via the context menu.


Adding a dedicated language server test project

For this example I suggest to put our language server test into an additional project named org.xtext.example.mydsl.ide.tests, so let’s create that project. Create a new Java Project and set the project location manually to <yourWorkspaceDir>/org.xtext.example.mydsl.parent/org.xtext.example.mydsl.ide.tests, like this:


Include the new project into the Gradle build configuration by adding it to the settings.gradle of the parent project, see line no 4 below:


Copy build.gradle from org.xtext.example.mydsl.tests to org.xtext.example.mydsl.ide.tests and add a dependency to org.xtext.example.mydsl.ide, see line no 3 in the following screen shot.


Having everything saved we need to poke Buildship, which connects Gradle and Eclipse, to re-evaluate the build settings. This is done via the context menu of a project – I just chose the parent project – → Gradle → Refresh Gradle Project.


The language server test

Create an Xtend class named org.xtext.example.mydsl.ide.tests.LanguageServerTest in the src folder of your new project with the super class org.eclipse.xtext.testing.AbstractLanguageServerTest.
Add the following constructor and test method.

This first test initializes your language server. The language server answers the initialize() call with infos on the supported features. The test assumes the language server to be able of resolving definitions of cross-references and formatting documents according to DSL-specific formatting rules.

The contributions of AbstractLanguageServerTest instantiate the language server and initialize the test. The test itself simulates a language client and collects responses from the server. Last but not least AbstractLanguageServerTest contributes lots of convenience methods corresponding to the services of language server.

Now run your test class as JUnit Test, e.g. via the context menu (right-click on the class name!), …


… and checkout the result:


Let’s add further tests.
The following one creates a file named hello.mydsl on the disc and instructs the language server to load it. The content is Hello Xtext! The test expects the language server to successfully load the document without any issues, which is indicated by an empty list of diagnostics provided by the server.

Last but not least let’s test the code completion ability of your language server.
The following test assumes a document with the content He, the cursor is located at the end of the line that is in column 2. The test expects the language server to offer a (single) completion proposal labeled Hello replacing the prefix He starting at line 0, column 0 and ending at line 0, column 2 with Hello.


Congrats! You build your own language server!
For further reading on customizing your Xtext-based language server wrt. the grammar of your DSL, scoping & linking, formatting, and code completion refer to http://www.eclipse.org/Xtext/documentation/. Your DSL contains expressions? Have a look on this post.

Finally, you wanna use your language within Visual Studio Code? Miro explains here how to achieve that.

By | 2017-01-24T15:54:29+00:00 December 22nd, 2016|Eclipse, Language Server, Tutorial, Xtext|9 Comments

About the Author:


  1. Vlad Dumitrescu January 13, 2017 at 10:09 - Reply

    Thanks Christian, nice tutorial!
    Is there yet any information on how to build & package the language server and use it from VSCode?

  2. Christian Schneider January 13, 2017 at 23:07 - Reply

    Hi Vlad!

    If I get you right, Miro’s post on ‘Building a VS Code Extension with Xtext’ should answer your questions.
    Find the link in the last sentence of my post above.


  3. Chetan Laddha February 1, 2017 at 09:54 - Reply

    Thanks, It helps me a lot !!!
    I want to use this language server DSL with the Eclipse Che, Can you please add more details regarding packaging?

  4. Neeraj Bhusare February 23, 2017 at 10:29 - Reply

    Tx for the tutorial .

  5. Vlad Dumitrescu March 7, 2017 at 11:07 - Reply

    I am getting a crash when running the tests, see below. Any ideas? Thanks!

    com.google.inject.internal.util.$ComputationException: java.lang.ArrayIndexOutOfBoundsException: 46031
    at com.google.inject.internal.util.$MapMaker$StrategyImpl.compute(MapMaker.java:553)
    at com.google.inject.internal.util.$MapMaker$StrategyImpl.compute(MapMaker.java:419)
    at com.google.inject.internal.util.$CustomConcurrentHashMap$ComputingImpl.get(CustomConcurrentHashMap.java:2041)
    at com.google.inject.internal.util.$StackTraceElements.forMember(StackTraceElements.java:53)
    at com.google.inject.internal.Errors.formatInjectionPoint(Errors.java:712)
    at com.google.inject.internal.Errors.formatSource(Errors.java:684)
    at com.google.inject.internal.Errors.format(Errors.java:555)
    at com.google.inject.ConfigurationException.getMessage(ConfigurationException.java:70)
    at java.lang.Throwable.getLocalizedMessage(Throwable.java:391)
    at java.lang.Throwable.toString(Throwable.java:480)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintWriter.println(PrintWriter.java:754)
    at java.lang.Throwable$WrappedPrintWriter.println(Throwable.java:764)
    at java.lang.Throwable.printStackTrace(Throwable.java:655)
    at java.lang.Throwable.printStackTrace(Throwable.java:721)
    at org.junit.runner.notification.Failure.getTrace(Failure.java:75)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestListener.testFailure(JUnit4TestListener.java:91)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestListener.testFailure(JUnit4TestListener.java:69)
    at org.junit.runner.notification.SynchronizedRunListener.testFailure(SynchronizedRunListener.java:63)
    at org.junit.runner.notification.RunNotifier$4.notifyListener(RunNotifier.java:142)
    at org.junit.runner.notification.RunNotifier$SafeNotifier.run(RunNotifier.java:72)
    at org.junit.runner.notification.RunNotifier.fireTestFailures(RunNotifier.java:138)
    at org.junit.runner.notification.RunNotifier.fireTestFailure(RunNotifier.java:132)
    at org.junit.internal.runners.model.EachTestNotifier.addFailure(EachTestNotifier.java:23)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:329)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
    Caused by: java.lang.ArrayIndexOutOfBoundsException: 46031
    at com.google.inject.internal.asm.$ClassReader.(Unknown Source)
    at com.google.inject.internal.asm.$ClassReader.(Unknown Source)
    at com.google.inject.internal.asm.$ClassReader.(Unknown Source)
    at com.google.inject.internal.util.$LineNumbers.(LineNumbers.java:62)
    at com.google.inject.internal.util.$StackTraceElements$1.apply(StackTraceElements.java:36)
    at com.google.inject.internal.util.$StackTraceElements$1.apply(StackTraceElements.java:33)
    at com.google.inject.internal.util.$MapMaker$StrategyImpl.compute(MapMaker.java:549)
    … 38 more

  6. […] haben wir einen YANG-Sprachserver via Xtext implementiert. Wie Christian Schneider bereits in einem vorangegangenen Blogpost anmerkte, kann Xtext mit wenig Aufwand einen LSP generieren. Wie fast jede Sprache hat YANG […]

  7. Shivani March 13, 2018 at 01:27 - Reply

    Thanks for the great tutorial, May I please know how can I integrate the built DSL language server with theia?

    Appreciate your efforts!

  8. Adrian Yankov March 27, 2018 at 08:21 - Reply

    I also tried the tutorial. It works flawlessly with hello world. Nevertheless, I tried it with a custom metamodel and had problems.
    I imported the metamodel via a jar file and loaded it in gradle through the build.settings. I did manage to generate the artifacts and installDist worked. Nevertheless, the default tests fail. I am also not sure how to test it properly. I also tried integrating it with VS code, but I somehow my server always rejected the connection.

    Can you please post a tutorial how to make an LSP with a custom grammar that uses a metamodel?
    Maybe also include both build systems Maven and Gradle.

    Kind regards,
    Adrian Yankov

Leave A Comment