How to create/develop an Eclipse Theia IDE plugin

May 4, 2020 | 12 min Read

This article provides an overview on how to develop a Eclipse Theia plugin and thereby extend the Theia IDE with new and custom features. Please note that there are two ways of extending Eclipse Theia: plugins and extensions. This article focuses on plugins, please also see our related articles for a comparison of Theia plugins and extensions and on how to develop extensions for Theia.

Probably the most important thing to know about developing a plugin for Eclipse Theia is that the plugin mechanism and the API available for plugins are almost identical to VS Code extensions. In fact, you can use VS Code extensions in Theia.

Therefore, most documentation and tutorials about extending VS Code can be applied to Theia, too. This article focuses on the available tooling; on the basic concepts; a “hello world” example to get started; an overview of the available API and finally a comparison between Theia plugins and VS Code extensions.

Tooling to develop an Eclipse Theia plugin

For an easy start with authoring Eclipse Theia plugins, there are quite a few Yeoman templates. You can install them via:

npm install -g yo @theia/generator-plugin

Now the following command will open a wizard allowing you to select from a variety of plugin templates to use as a starting point

yo @theia/plugin

Those templates include a “Hello World” example, but also more comprehensive plugins, such as language support for XML. So they are a great start for any custom plugin to be developed!

Further, to develop a plugin for Eclipse Theia, you will need to select the IDE/Code Editor to do so. There are two convenient choices, either you use VS Code or you use Eclipse Theia itself. Theia provides some tooling around developing Theia plugins and is, therefore, the better candidate for this task. For this reason, we assume you use Theia for developing a Theia plugin in the following.

To use Theia to develop Theia plugins, it is recommended that you add the corresponding extension. Again, just to avoid any misunderstandings: We add a Theia extension to the Theia instance, which we use as an IDE for developing a Theia plugin. :-)

{
  "private": true,
  "dependencies": {
    ...
    "@theia/plugin-dev": "next"
  },
  "devDependencies": {
    "@theia/cli": "next"
  }
}

This will also transitively add the packages “@theia/plugin” and “@theia/plugin-ext” to your Theia product, which are both required to deploy Theia plugins at runtime.

The plugin tooling in Theia allows you to directly launch an instance of a plugin under development. As a plugin cannot exist on its own, the command “Hosted Plugin: Start Instance” will more precisely start a new Theia instance, which is identical to your development environment, but additionally host the deployed plugin under development (as shown in the following screenshot). To not get confused by the two Theia instances running in this scenario, the status bar at the bottom indicates, which Theia you are looking at.

Other commands allow you to stop the plugin instance, to restart it and finally to debug it, which enables breakpoints and inspecting a plugin under development.

If you use Eclipse Che as a development environment, the default Theia editor in Che has all the required tools for plugin development preinstalled.

Hello World

Before we go more into detail about the plugin API, let us look at a simple “hello world” plugin to get started. Luckily, you can instantiate a hello world template plugin via:

yo @theia/plugin

Choose “Back-end plugin” => “Hello world” in the menu. Assign a plugin name and browse the folders created. The plugin project will contain exactly one source file called “{pluginname}-backend.ts”, so in a sense, this file is the actual plugin. A plugin is expected to implement a start and a stop function. The start function adds custom contributions to Theia, such as new commands or even a new view. The stop function is expected to clean up any resources or processes that the plugin might have started, it is executed when a plugin is stopped.

To do any kind of interaction and contributions to Theia, plugins interact with a module (i.e. the plugin API) provided by “@theia/plugin”. It is common practise to import this whole module and assign an alias (“import * as theia”) so that you can essentially access the available API using one namespace. This is already done in the example generated.

As shown in the following listing, the “hello world” example essentially registers a command using the Theia API. On execution of this command, the Theia API is used again to trigger a notification saying “hello world”. We will provide more details about this API in the following section.

Now if you use the command to launch a Theia instance included in this plugin, as described in the previous section, you can use the command palette (“F1”) and trigger the “hello world” command. Now let us take a more detailed look at the API that Theia provides for plugins.

Plugin API

What makes using the Theia plugin API very convenient is the fact that it is compatible with the VS Code extension API. This means that if you search for a specific capability, you can also use all documentation resources available for VS Code. As good documentation is available at Theia, too, we only provide a detailed API overview in this article.

Just as the API is for VS Code extensions, so the Eclipse Theia plugin API also consists of two conceptional parts, the TypeScript API and a declarative API, to be used via the package.json.

TypeScript API

The Eclipse Theia TypeScript API is very easy to use, you can access basically everything via the module import “@theia/plugin” (typically bound to a variable “theia”). As auto completion is well supported in Theia, a good way of exploring the API is actually to type “theia.” and browse the suggestions available. In the following, we provide an overview of its namespaces:

  • commands: To add executable commands to the workbench and editors, which can be triggered via the command palette, menu items or key bindings
  • comment: To create a CommentController allowing more sophisticated commenting support in the code editor (see this extension for an example)
  • debug: Anything related to extending the debug capabilities, but also to interact with breakpoints and debug sessions
  • env: Provides access to the environment that Theia is running in, e.g. the clipboard or the user-specific language setting.
  • languageServer: To register a custom language server.
  • languages: Everything else language-related, e.g. to implement a custom “Call hierarchy provider” or a custom formatter.
  • plugins: To interact with other installed plugins. Plugin can expose an API to be used by others.
  • scm: To add support for a source control system
  • tasks: To contribute or interact with tasks, e.g. being notified if a task has been completed. Tasks are like jobs, e.g. a compilation could be a task
  • window: To interact and contribute to the workbench, e.g. to show any kind of dialogue, change the status bar or react to an opened terminal
  • workspace: To interact with the workspace, e.g. to react on save or browser folders.

From a high-level point of view, there are two ways of using this API. The first one is to call a specific function to trigger an action. A good example of  this is triggering the notification in the hello world example before.

The second type of API call registers a “contribution” to Theia. This basically means you add something to Theia, typically a callback, that is triggered by “some other action” in Theia. “Some other action” in this context can mean the direct invocation of a command by the user (as in the hello world example), but also more indirect use cases, e.g. if the user triggers “call hierarchy” in the code editor. The second type typically uses functions in the API starting with “register…”, e.g. the “registerCommand” call in the hello world example.

Registration functions usually return a “Disposable”. This object allows you to unregister the contribution later. This is very useful because of the dynamic nature of plugins. Just as VS Code extensions, Theia plugins can also potentially be removed or disabled during runtime. In this case, Theia needs to disable all contributions which were previously done by the plugin. To enable this, you should push all “Disposables” into the provided context. This is essentially what the call “context.subscriptions.push” does in the example above.

Please note that any plugin contribution will work without pushing the resulting Disposable into the context. However, Theia will not be able to clean up your plugins contribution on deactivation, so in order to be a “good citizen”, you should handle Disposables correctly.

Declarative API (package.json)

Besides the “code API”, meaning the API you can directly call from within TypeScript, Theia provides a declarative API for plugins via the package.json, called “contribution points”. Again this is similar to VS Code, so you can refer to the VS Code documentation about contribution points. Declarative contribution points make some use cases simpler compared to code, especially configurations and “wiring things together”. Technically even more important, these contributions can be simply parsed by Theia, without executing any code. This way is faster and more robust from the viewpoint of Theia. As an example, if you want to contribute to the hello world command, described above, to the context menu, you need to use such a declarative contribution point. In the following listing, the hello world command is contributed to the context menu of the editor. It will be only visible on files with the extension “hello”, as specified using the “when” condition.

"contributes": {

        "menus": {

            "explorer/context": [

                {

                    "when": "resourceExtname == .hello",

                    "command": "hello-world-example-generated"

                }

            ]

        }

    }

The following screencast shows the context menu in a running Theia instance (with the plugin deployed).

Further, many contribution points allow you to register configuration files, which are not code, e.g. language configuration files. The following example shows a combination of those typical use cases. It adds language support for XML files. It defines the language id and (human readable) aliases. It binds the language to the file extension “xml” as well as to files, which first line matches a regular expression. Finally, it points to a language configuration file. The following snippet would be part of the package.json of a Theia plugin:

 "contributes": {
        "languages": [
          {
            "id": "xml",
            "aliases": [
              "XML",
              "xml"
            ],
            "extensions": [
              ".xml"
            ],
            "firstLine": "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
            "configuration": "./src/language-configuration.json"
          }
        ]
     }

As you can see, the plugin API of Eclipse Theia is a mix of declarative contributions (in the package.json), use case specific files (like the language configuration) and code using the TypeScript API (like the implementation of the hello world command).

Finally, the declarative API is also the bridge between configuration files and the entry TypeScript file of a plugin. More concretely, it is used to let Theia know which file to trigger to activate the plugin. There is actually a property in the package.json pointing to the plugin source file. This way, Theia is able to call the start function on start-up and thereby process the contributions made using the TypeScript API. The following example shows the registration of the hello world plugin:

"theiaPlugin": {
            "backend": "lib/helloWorldTheiaPlugin-backend.js"
}

Eclipse Theia plugin vs. VS Code extension

One thing you might wonder about is the following question: If the Theia plugin API is compatible with the VS Code extension API and you can even run VS Code extensions in Theia, can you also run Eclipse Theia plugins in VS Code?

The answer is: Not without adaptations. There are two reasons for that. First, even though the Theia API is almost the same as the VS Code API, the Theia API is actually a super set. That means the Theia API provides all functions that VS Code provides, but a little bit more. The second reason is even simpler, the Theia plugin is using the “theia” namespace, which is not available in VS Code (VS Code uses the “vscode” namespace).

However, there is a very simple solution to this issue, in case you want to develop something which is compatible with both tools. Eclipse Theia fortunately also provides the VS Code API as the “vscode” namespace. Technically this actually contains the same functions as the “theia” namespace. However, if you use the “vscode” namespace, you make sure that your code runs in both, Eclipse Theia and VS Code. If you want to follow this strategy, you should also rely on the VS Code Yeoman template instead of the one provided by Theia, which makes sure that your extension/plugin is compatible. However, besides the import, your code will look identical.

This approach would have one potential disadvantage: If the Theia extension API, which is a superset of VS Code provided a feature that you wanted to use, you wouldn’t be able do so, as it would not be available in the “vscode” namespace. Eclipse Theia actually provides more customization and enhancement capabilities compared to VS Code, however, not currently in the plugin API, but via Theia extensions. The goal of the Theia plugin API has been to achieve full compatibility with VS Code and consequently, the two APIs are almost identical at the moment. It is even being currently discussed to keep them fully identical and provide additional platform features for Theia plugins outside of the regular plugin API.

Conclusion

In this article, we provide a basic overview of how to develop a plugin for Eclipse Theia. The main advantages in comparison to Theia extensions are (1) plugins can be installed by the user during runtime, (2) plugins run in a dedicated process and cannot easily “harm” the stability of the hosting Theia instances (3) the plugin API is generally easier to use and better documented, because (4), the API is compatible with (the same as) VS Code, so all documentation resources available for VS Code extensions are valid for Theia plugins, too. With the ease of use, there comes the main disadvantage: you cannot access/customize everything in Theia using plugins, the API is restricted to specific use cases. Please see this article on a detailed comparison of Theia plugins vs. extensions.

A final word about the compatibility between Theia plugins and VS Code extensions: While the API is almost the same, there are some minor differences in implementation. If you target the VS Code API, as described in the previous section, you can even develop extensions/plugins, which are compatible with Eclipse Theia and VS Code. However, if you want to target both environments, you should set up your build and testing for both platforms to ensure correct behavior of your plugin in both tool platforms.

If you are interested in getting support to develop a plugin for Eclipse Theia, or you want to target VS Code and Theia at the same time or if you want to provide a custom product based on Eclipse Theia, have a look at our support, training and consulting offerings around Eclipse Theia, then have a look at our service offering for web-based tools or tools in general and please get in contact with us!

Finally, if you are interested in future articles on this topic, please follow us on Twitter!

Jonas, Maximilian & Philip

Jonas Helming, Maximilian Koegel and Philip Langer co-lead EclipseSource. They work as consultants and software engineers for building web-based and desktop-based tools. …