8 Works to Improve VS Code Extension using Xtext

twoflat
4 min readNov 23, 2021

Overview

This is an article that changed VS Code extension built in the previous article and changed it to start Generator when saving.
I hope these help someone.

Goal

As mentioned in the previous article , the extension built immediately after Clone behaves like a live preview. This is a different behavior from the Eclipse Xtext plug-in, and I think it is not very desirable because of the unified operability and non-intuitive aspects.
As a harmful effect, when the extension of the rule defines the path of the file output to grammar (and at Generator), will file is generated each time you change the file name.
In any case, it is a little inconvenient as it is.
Therefore, in order to regain a sense of unity in operability, I tried to realize the generation of a file at the time of saving and to utilize the existing source code as unchanged as possible.

Development environment

  • Windows10 Pro 1909
  • VSCode 1.47
  • JDK 1.8
  • Xtext 2.20

Result

I put source codes on GitHub that somehow solved the problem . If you are interested, please clone and check.
The details of the work are described below.

Policy

The point raised in the issue is also in Stop VS Code from always generating code during input # 50 and Don’t run the generator for open files # 373 , and it seems that it also occurs in extensions based on Xtext 2.20.
There were several options for the solution, but I decided to duplicate the original xtext class and make the necessary changes on the duplicating side, considering the creation of the source code up to save and validation.
The main work is explained below.

Works

Replace LanguageServerImpl

It is a class that is the starting point for interacting with the client (VSCode). Duplicate LanguageServerImpl from here and change the namespace. In addition, change the didSave and didChange methods.
The content of the didSave method is do nothing at the time of duplication. By using this, the process is changed so that the content executed by the didChange method is called the didSave method. On the other hand, the didSave method adds a duplicated process before the generator call that the didChange method was doing, that is, until validation.

Replace ServerLauncher

The ServerLauncher class is responsible for instantiating the LanguageServerImpl class above.
As the LanguageServerImpl class is replaced, the ServerLauncher path also needs to be replaced. If you use the existing ServerLauncher class as it is, an error will occur in the build, so duplicate the contents from here and change the namespace.
Next, change the LanguageServer to be imported to the path of the LanguageServerImpl class above.

Modify build.gradle

Modify org.xtext.example.mydsl.ide / build.gradle.
There is a place to specify the path of the ServerLauncher class where the main method is implemented, so replace it with the existing ServerLauncher.

Replace WorkspaceManager

A class that manages the path of the folder opened by the client, etc., behind LanguageServerImpl. Duplicate from here and change the namespace.
Details of the didSave and didChange methods of LanguageServerImpl are implemented here.

Replace BuildManager

A class located between WorkspaceManager and ProjectManager. Duplicate from here and change the namespace.
The details of the didSaveTextDocumentContent method from WorkspaceManager are implemented in this class.

Replace ProjectManager

A class located between BuildManager and IncrementalBuild. Duplicate from here and change the namespace.
Details of the doValidate method from BuildManager are implemented in this class.

Replace IncrementalBuilder

This class is located at the final stage of actual build and validation. Duplicate from here and change the namespace.
The details of the validate method from BuildManager are implemented in this class.

Remove MultiProjectServerLauncher

There is no method referenced by the MultiProjectServerLauncher class. In the first place, even though it is unused, gradle will cause a build error.

Supplement

Several options

I conducted a number of studies to resolve the issue.
Both were troublesome, but on the other hand, I was able to gain knowledge and lessons. I’ll just give you an overview here, and I’ll write the details in a separate article.

Ported part of n4js

As a result of reading the source code of GitHub thoroughly, I found that XLanguageServerImpl is a class equivalent to LanguageServerImpl.
For a while, I thought that it would be over if I brought the necessary classes while porting this.

  • When the required class is ported, another class is ported to eliminate the error, which causes an error again, and fatigue accumulates.
  • Despite @inject, there is a part where the injection class had to be specified, so I can not benefit from DI

Ported the whole n4js

I couldn’t stand porting the class, so I tried to bring the whole source code without any dependency problems, and I thought it would be easier, but I gave up. for example,

  • Project dependency definitions had to be resolved with gradle and I had to get used to writing groovy
  • There is a description introduced in java 9 and 10 in the source code, and it is painful to rewrite it to Java 8 base.
  • Deprecated classes were still used normally and needed to avoid class cast exceptions at runtime
  • I stumbled on calling the bind method that specifies the class in advance for inject

I was caught in a number of traps and my heart broke.

Lessons Learned

It turns out that just because it’s open source and the contents are clear, it’s not straightforward to port only the parts needed for another system or to bring them in their entirety. I realized that I would throw away the easy idea that it would be easy to port this.

Other

Original article(in Japanese) is here.

--

--

twoflat
0 Followers

I am a developer of software for embedded devices. I would like to share what I have practiced and learned about programming languages, AI, etc.