theia Documentation

Eclipse Theia is a cloud & desktop IDE framework implemented in TypeScript.

21,629 stars TypeScript TypeScript 1 file ยท ~20,763 tokens #cloud-ide#editor#electron#ide#language-server-protocol#typescript
RAW Doc

1. Project Overview & Quickstart (eclipse-theia/theia)

File: README.md

Website

Visit the Eclipse Theia website for more information and the Theia documentation.

Repositories

This is the main repository for the Eclipse Theia project, containing the sources of the Theia Platform. Please open generic discussions, bug reports and feature requests about Theia on this repository. The Theia project also includes additional repositories, e.g. for the artifacts building the Theia IDE and the Theia website. Please also see the overview of all Theia project repositories.

Releases

Scope

  • Support building browser-based and desktop IDEs and tools
  • Provide a highly flexible architecture for adopters
  • Support VS Code Extension protocol
  • Develop under vendor-neutral open-source governance

More details on the project goals are available on the Theia website.

Roadmap

See our roadmap for an overview about the current project goals and the upcoming releases.

Getting Started

Here you can find guides and examples for common scenarios to adopt Theia:

Contributing

Read below to learn how to take part in improving Theia:

Feedback

Read below how to engage with Theia community:

  • Join the discussion on GitHub.
  • Ask a question, request a new feature and file a bug with GitHub issues.
  • Vote on existing GitHub issues by reacting with a ๐Ÿ‘. We regularly check issues with votes!
  • Star the repository to show your support.
  • Follow Theia on X.
  • Join the weekly developer call

Documentation

SBOM

To enhance supply chain security and offer users clear insight into project components, Eclipse Theia now generates a Software Bill of Materials (SBOM) for every release. These are published to the Eclipse Foundation SBOM registry, with access instructions and usage details available in this documentation.

License

Trademark

"Theia" is a trademark of the Eclipse Foundation. Learn More


File: dev-packages/request/README.md

Description

The @theia/request package is used to send proxy-aware http requests to other services.

Additional Information

License

Trademark

"Theia" is a trademark of the Eclipse Foundation
https://www.eclipse.org/theia


File: dev-packages/private-test-setup/README.md

Description

The @theia/test-setup contributes a setup script for mocha executed tests in Theia.
This setup script is executed before any test file is loaded or compiled.
This is for example useful for globals which must exist before dependencies are loaded.

Additional Information

License

Trademark

"Theia" is a trademark of the Eclipse Foundation
https://www.eclipse.org/theia


File: dev-packages/private-re-exports/README.md

`@theia/re-export`

Utility package to re-export dependencies.

This is useful when you use and expose some APIs from a dependency and want
your dependent to get access to the exact same symbols as you did.

`package.json`

You can configure how the theia-re-export CLI will generate re-exports
through your package.json file with a theiaReExports key:

json
{
    "theiaReExports": {
        "destination": {
            "export *": [
                "packages that export via *"
            ],
            "export =": [
                "packages that export via ="
            ],
            "copy": "other-package#destination"
        }
    }
}

`transitive`

If you want to re-export packages from another package that also re-exports
its dependencies. We use this in @theia/core to simplify the consumption
of some optional Electron-specific dependencies.

`export *`

Packages that export their symbols as export const x = ....

`export =`

Packages that export their symbols as a namespace like export = ....


File: dev-packages/private-ext-scripts/README.md

Description

theiaext is a command line tool to run shared npm scripts in Theia packages.
For instance, if you want add a new hello script that prints Hello World:

json
{
    "name": "@theia/ext-scripts",
    "theia-monorepo-scripts": {
        "ext:hello": "echo 'Hello World'"
    }
}
  • install theiaext in your package (the actual version can be different)
json
{
    "name": "@theia/myextension",
    "devDependencies": {
        "@theia/ext-scripts": "^0.1.1"
    }
}
  • you should be able to call hello script in the context of your package:
shell
npx theiaext hello
  • and from npm scripts of your package:
json
{
    "name": "@theia/myextension",
    "scripts": {
        "hello": "theiaext hello"
    }
}

Additional Information

License

Trademark

"Theia" is a trademark of the Eclipse Foundation
https://www.eclipse.org/theia


File: dev-packages/private-eslint-plugin/README.md

Description

The @theia/eslint-plugin contributes rules useful for Eclipse Theia development.
The plugin helps identify problems during development through static analysis including code quality, potential issues and code smells.

Rules

`annotation-check`

Inversify >=6.1 requires to annotate all constructor parameters of injectable classes as otherwise runtime errors are thrown.
The rule checks that all constructor parameters of injectable classes are annotated with @inject, @unmanaged or @multiInject.

`localization-check`

The rule prevents the following localization related issues:

  • incorrect usage of the nls.localizeByDefault function by using an incorrect default value.
  • unnecessary call to nls.localize which could be replaced by nls.localizeByDefault.

`no-src-import`

The rule prevents imports using /src/ rather than /lib/ as it causes build failures.
The rule helps developers more easily identify the cause of build errors caused by the incorrect import.

`runtime-import-check`

The rule prevents imports from folders meant for incompatible runtimes.
The check enforces the code organization guidelines of the framework and guards against invalid imports which may cause unforeseen issues downstream.

`shared-dependencies`

The rule prevents the following:

  • prevents the implicit use of a shared dependency from @theia/core.
  • prevents extensions from depending on a shared dependency without re-using it from @theia/core.

Additional Information

License

Trademark

"Theia" is a trademark of the Eclipse Foundation
https://www.eclipse.org/theia


File: dev-packages/ovsx-client/README.md

Description

The @theia/ovsx-client package is used to interact with open-vsx through its REST APIs.
The package allows clients to fetch extensions and their metadata, search the registry, and
includes the necessary logic to determine compatibility based on a provided supported API version.

Note that this client only supports a subset of the whole OpenVSX API, only what's relevant to
clients like Theia applications.

`OVSXRouterClient`

This class is an OVSXClient that can delegate requests to sub-clients based on some configuration (OVSXRouterConfig).

jsonc
{
    "registries": {
        // `[Alias]: URL` pairs to avoid copy pasting URLs down the config
    },
    "use": [
        // List of aliases/URLs to use when no filtering was applied.
    ],
    "rules": [
        {
            "ifRequestContains": "regex matched against various fields in requests",
            "ifExtensionIdMatches": "regex matched against the extension id (without version)",
            "use": [/*
                List of registries to forward the request to when all the
                conditions are matched.

                `null` or `[]` means to not forward the request anywhere.
            */]
        }
    ]
}

Additional Information

License

Trademark

"Theia" is a trademark of the Eclipse Foundation
https://www.eclipse.org/theia


File: dev-packages/localization-manager/README.md

Description

The @theia/localization-manager package is used easily create localizations of Theia and Theia extensions for different languages. It has two main use cases.

First, it allows to extract localization keys and default values from nls.localize calls within the codebase using the nls-extract Theia-CLI command. Take this code for example:

ts
const hi = nls.localize('greetings/hi', 'Hello');
const bye = nls.localize('greetings/bye', 'Bye');

It will be converted into this JSON file (nls.json):

json
{
  "greetings": {
    "hi": "Hello",
    "bye": "Bye"
  }
}

Afterwards, any manual or automatic translation approach can be used to translate this file into other languages. These JSON files are supposed to be picked up by LocalizationContributions.

Additionally, Theia provides a simple way to translate the generated JSON files out of the box using the DeepL API. For this, a DeepL free or pro account is needed. Using the nls-localize command of the Theia-CLI, a target file can be translated into different languages. For example, when calling the command using the previous JSON file with the fr (french) language, the following nls.fr.json file will be created in the same directory as the translation source:

json
{
  "greetings": {
    "hi": "Bonjour",
    "bye": "Au revoir"
  }
}

Only JSON entries without corresponding translations are translated using DeepL. This ensures that manual changes to the translated files aren't overwritten and only new translation entries are actually sent to DeepL.

Use theia nls-localize --help for more information on how to use the command and supply DeepL API keys.

For more information, see the internationalization documentation.

Additional Information

License

Trademark

"Theia" is a trademark of the Eclipse Foundation
https://www.eclipse.org/theia


File: dev-packages/ffmpeg/README.md

`ffmeg.node`

This is a Node Native Addon to dynamically link to Electron's ffmpeg.dll and fetch a list of included codecs.

2. Official Technical Reference & Guides (eclipse-theia/theia-website)

File: README.md

Theia's Website

The source for the website and online documentation for the Theia IDE Framework.

Work locally

Building locally requires node 14.x. Alternatively, you can use npm i --legacy-peer-deps to ignore conflicting peer dependencies.

bash
npm install && npm run start

To build for production and serve, run:

bash
npm run build
npm run serve

CI

The website is automatically built with Github workflows and deployed on Github pages, which are reachable via theia-ide.org.

A preview of every pull request is published at eclipse-theia/theia-website-previews. You'll see a comment with the link to the preview once the build is finished.
For more information, see publish.yml and preview.yml.

License

Trademark

"Theia" is a trademark of the Eclipse Foundation
https://www.eclipse.dev/theia


File: src/docs/architecture.md


title: Architecture Overview

Architecture Overview

This section describes the overall architecture of the Theia Platform.

Theia is designed to work as a native desktop application as well as in the
context of a browser and a remote server. To support both situations with a
single source, Theia runs in two separate processes. Those processes are called
frontend and backend respectively, and they communicate through JSON-RPC
messages over WebSockets or REST APIs over HTTP. In the case of Electron, the
backend, as well as the frontend, run locally, while in a remote context the
backend would run on a remote host.

Both the frontend and backend processes have their dependency injection (DI)
container (see below) to which extensions can contribute.

Frontend

The frontend process represents the client and renders the UI. In the browser,
it simply runs in the rendering loop, while in Electron it runs in an Electron
Window, which basically is a browser with additional Electron and Node.js APIs.
Therefore, any frontend code may assume browser as a platform but not Node.js.

The startup of the frontend process will first load the DI modules of all
contributing extensions before it obtains an instance of FrontendApplication
and call start() on it.

Backend

The backend process runs on Node.js. We use express as the HTTP server. It
may not use any code that requires a browser (DOM API) as the platform.

The startup of the backend application will first load the DI modules of all
contributing extensions before it obtains an instance of BackendApplication
and calls start(portNumber) on it.

By default, the backend's express server also serves the code for the frontend.

Separation By Platform

In an extension's top folder we have an additional layer of folders to separate
by platform:

  • The common folder contains code that doesn't depend on any runtime.
  • The browser folder contains code requiring a modern browser as a platform
    (DOM API).
  • The electron-browser folder contains frontend code that requires DOM API
    as well as Electron renderer-process specific APIs.
  • The node folder contains (backend) code requiring Node.js as a platform.
  • The node-electron folder contains (backend) code specific for Electron.

See also

For a high level overview of Theia's Architecture, see this document:

Multi-Language IDE Implemented in JS - Scope and Architecture


File: src/docs/authoring_extensions.md


title: Authoring an Extension

Authoring Theia Extensions

This guide will walk you through the process of creating Theia extensions and deploying them in your Theia-based application. Please make sure to be aware of how to create a Theia based application and the different available extension mechanisms of Theia (Plugins vs. Extensions) before you continue reading.

As an example, we are going to add a menu item Say hello that displays a notification "Hello world!". This article is guiding you through all the necessary steps.

Prerequisites

Prerequisites information are available from the Theia repository.

Project Layout

Please refer to the guide to create Theia applications to get familiar with the default project layout and generate a Theia project with the example 'hello world' extension using our Yeoman Generator

A Custom Theia Extension

Let's look at the generated code for our extension in the hello-world folder. Letโ€™s start with the package.json. It specifies the packageโ€™s metadata, its dependencies to the Theia core package, a few scripts and dev dependencies, and the theia-extension itself. Please note that the following listing might be outdated, please always refer to the generated examples from our Yeoman Generator

The keyword theia-extension is important: It allows a Theia app to identify and install Theia extensions from npm.

json
{
  "name": "hello-world",
  "keywords": [
    "theia-extension"
  ],
  "version": "0.0.0",
  "files": [
    "lib",
    "src"
  ],
  "dependencies": {
    "@theia/core": "latest"
  },
  "devDependencies": {
    "rimraf": "latest",
    "typescript": "~5.4.5"
  },
  "scripts": {
    "prepare": "yarn run clean && yarn run build",
    "clean": "rimraf lib",
    "build": "tsc",
    "watch": "tsc -w"
  },
  "theiaExtensions": [
    {
      "frontend": "lib/browser/hello-world-frontend-module"
    }
  ]
}

As you can see, the extension is a dedicated package that just depends on Theia. However, as the extension contributes features to our application, it needs to be wired a runtime. To achive this in a modular way, in Theia, everything is wired up via dependency injection. An extension defines one or more dependency injection modules. This is where it binds its contribution implementations to the respective contribution interface. The modules are listed in the package.json of the extension package. An extension can contribute to the frontend, e.g. providing a UI extension, as well as to the backend, e.g. contributing a language server. When the application starts, the union of all these modules is used to configure a single, global dependency injection container on each, the frontend and the backend. The runtime will then collect all contributions of a specific kind by means of a multi-inject.

The last property theiaExtensions in the package.json above is where we list the JavaScript modules that export the DI modules defining the contribution bindings of our extension. In our case, we only provide a frontend capability (a command and a menu entry). Analogously, you could also define contributions to the backend, e.g. a language contribution with a language server.

Theia defines a plethora of contribution interfaces that allow extensions to add their behaviour to various aspects of the application. Browse the documentation section 'Platform Concepts & APIs' or search for interfaces with the name *Contribution to get an idea. An extension implements the contribution interfaces belonging to the functionality it wants to deliver. In this example, we are going to implement a CommandContribution and a MenuContribution. Other ways for extensions to interact with a Theia application are via one of the various services or managers.

In the frontend module we export a default object that is a InversifyJS ContainerModule with bindings for a command contribution and a menu contribution. Please see our dependency injection guide for more details.

typescript
export default new ContainerModule(bind => {
    // add your contribution bindings here
    bind(CommandContribution).to(HelloWorldCommandContribution);
    bind(MenuContribution).to(HelloWorldMenuContribution);
});

A command is a plain data structure defining an ID and a label. The behaviour of a command is implemented by registering a handler to its ID in a command contribution. The generator has already added a command and a handler that shows a "Hello World!" message.

typescript
export const HelloWorldCommand = {
    id: 'HelloWorld.command',
    label: "Shows a message"
};

@injectable()
export class HelloWorldCommandContribution implements CommandContribution {

    constructor(
        @inject(MessageService) private readonly messageService: MessageService,
    ) { }

    registerCommands(registry: CommandRegistry): void {
        registry.registerCommand(HelloWorldCommand, {
            execute: () => this.messageService.info('Hello World!')
        });
    }
}
...

Note how we use @inject in the constructor to get the MessageService as a property, and how we use that later in the implementation of the handler. This is the elegance of dependency injection: As a client, we neither care where these dependencies come from nor what their lifecycle is.

To make it accessible by the UI, we implement a MenuContribution, adding an item to the Search/Replace section of the edit menu in the menu bar.

typescript
...
@injectable()
export class HelloWorldMenuContribution implements MenuContribution {

    registerMenus(menus: MenuModelRegistry): void {
        menus.registerMenuAction(CommonMenus.EDIT_FIND, {
                commandId: HelloWorldCommand.id,
                label: 'Say Hello'
            });
    }
}

Adding Extensions to a Theia application

To make sure your extension is included in your Theia application, list it as a dependency in your browser or electron app, e.g. like this:

json
{
  "private": true,
  "name": "browser-app",
  "version": "0.0.0",
  "dependencies": {
    "@theia/core": "latest",
    ...
    "hello-world": "0.0.0"
  },
  "devDependencies": {
    "@theia/cli": "latest"
  },
  "scripts": {
    "bundle": "yarn rebuild && theia build --mode development",
    "rebuild": "theia rebuild:browser --cacheRoot ..",
    "start": "theia start",
    "watch": "yarn rebuild && theia build --watch --mode development"
  },
  "theia": {
    "target": "browser"
  }
}

Deploying the Extension

To run the extension, you have two options:

  1. Have your extension as part of a monorepo containing a Theia-based application importing your extension (like the structure created by the Yeoman Generator)
  2. Publish the extension with yarn publish and consume it from your Theia-based application

See Executing the Browser Application and Executing the Extension in Electron for more details* for adding extensions to the dependencies of a Theia-based application and running it.


File: src/docs/authoring_plugins.md


title: Authoring Plug-ins

Authoring Theia Plug-ins

This documentation is deprecated and needs to be updated. We currently recommend using VS Code extensions or Theia extensions instead of Theia Plugins. See the extension overview for more details.


File: src/docs/authoring_vscode_extensions.md


title: Authoring VS Code Extensions

Authoring VS Code Extensions

Alongside Theia extensions, VS Code extensions can also be used to enhance Theia applications with additional functionality, such as language support, commands, or tree views.
VS Code extensions contribute functionality through the dedicated VS Code API, which the Theia framework also supports.
This means that extensions that have been developed for VS Code are also compatible with Theia, and vice versa.
The coverage report provides more details on the extent to which the VS Code API is supported by each Theia version.

While there are certain overlaps in which types of functionality can be contributed to a Theia application with a Theia extension or a VS Code extension, both have their unique advantages and disadvantages.
Please refer to the overview on extensions and plugins and an in-depth comparison for a more detailed discussion.

In the remainder of this page, we guide you through the process of creating VS Code extensions and deploying them in Theia.
The steps for deploying VS Code extensions apply not only to VS Code extensions you develop yourself, but also to third-party VS Code extensions from the Theia marketplace. If you use a third-party VS Code extension, you can skip the section "Creating VS Code Extensions" and "Developing VS Code Extensions in a Theia Project".

Creating VS Code Extensions

As VS Code extensions can be used in Theia without further modification, you can refer to the documentation on extending VS Code to learn more about creating extensions for VS Code as well as for Theia.
The easiest way to get started is to use the VS Code Extension Generator and follow the Getting Started Guide.

Since Theia 1.72, VS Code extensions delivered as ECMAScript Modules (ESM) are supported. Extensions whose entry point ends in .mjs, or whose package.json declares "type": "module", are loaded via dynamic import(). The import 'vscode' resolution is wired up by an internal Node loader hook, so both CommonJS and named ESM imports (import { commands, window } from 'vscode') work without any changes on the extension side. ESM-based built-ins such as the GitHub authentication extension load correctly out of the box.

As an example, let's generate the hello world extension.

bash
npm install yo generator-code # install the VS Code Extension Generator
npm install vsce # install VS Code Extension CLI tooling

yo code # generate an extension
# take a look at the generated package.json defining a command "Hello World"
# take a look at the src/extension.ts registering a command handler
# make changes to the README and the code, if you like
vsce package # package your VS Code extension

As a result, a vsix file is generated, which can be used directly in VS Code. For Theia, the extension can be installed at runtime by users (see Installing VS Code Extensions at Runtime), downloaded automatically at build time from a registry (see Pre-installing VS Code Extensions), or manually unpacked for pre-installation.
Please note that you can also create so-called extension packs with the VS Code extension generator that defines a collection of extensions.

Conditional View Containers via `when` Clauses

VS Code's contributes.viewsContainers entries support an optional when clause that controls visibility based on context keys. Theia honors this field: a container with a when clause that evaluates to false is not attached, does not appear in the View menu or the Open View quick pick, and is automatically created or disposed as the underlying context keys change. This is useful for extensions that contribute mutually exclusive containers (for example one for the primary sidebar and one for the secondary sidebar guarded by negated context keys), so that only the active one is shown.

jsonc
{
    "contributes": {
        "viewsContainers": {
            "activitybar": [
                {
                    "id": "my-sidebar",
                    "title": "My View",
                    "icon": "resources/icon.svg",
                    "when": "!myExtension.useSecondarySidebar"
                }
            ],
            "panel": [
                {
                    "id": "my-sidebar-secondary",
                    "title": "My View",
                    "icon": "resources/icon.svg",
                    "when": "myExtension.useSecondarySidebar"
                }
            ]
        }
    }
}

Language Model Tools

VS Code extensions can contribute tools for AI agents through the language model tools API (vscode.lm.registerTool and vscode.lm.invokeTool). Theia supports this API and integrates the registered tools with Theia AI: a tool registered by an extension can be referenced in a user or system prompt via the ~{toolId} syntax and is then invoked by the language model like any built-in tool. The tool id is also offered in the chat input auto-completion.

This lets VS Code extensions extend the AI features of a Theia-based product with custom tools, without depending on Theia-specific APIs.

Prerequisites for Running VS Code Extensions in Theia

To enable Theia's support for VS Code extensions in order to use your own extensions or third-party VS Code extensions in your Theia application, you'll need to add @theia/plugin-ext-vscode as a dependency.
Typically, this is done in your browser or electron application package.

json
{
  ...
  "name": "browser-app",
  "dependencies": {
    "@theia/core": "latest",
    ...
    "@theia/plugin-ext-vscode": "latest"
  },
  "theia": {
    "target": "browser"
  }
}

Next, you need to specify a plugins location, which is a disk location containing your VS Code extensions.
Theia will load all VS Code extensions from this location on initialization of a new user session automatically.
This location can be set either with an environment variable:

bash
export THEIA_DEFAULT_PLUGINS=local-dir://absolute-path-to-plugins-folder

Or it can be provided in the command line option --plugins when starting Theia.

bash
theia start --plugins=local-dir:../plugins

A common approach for Theia applications that include VS Code extensions is to add this command line parameter to the startup script in the browser or electron application package.

json
{
  ...
  "name": "browser-app",
  ...
  "scripts": {
    ...
    "start": "theia start --plugins=local-dir:../plugins",
  },
  "theia": {
    "target": "browser"
  }
}

Consuming VS Code Extensions

Once you've enabled VS Code extension support in your Theia application as described above and made available your or any third-party VS Code extensions that you'd like to use in your Theia application, you have two options on how to consume them:

  1. Pre-installing the VS Code extension in your Theia application
  2. Enabling your users to install your VS Code extensions at runtime

In the following, we'll provide more details on each of those options.

Pre-installing VS Code Extensions

For pre-installing a certain set of VS Code extensions in your Theia application, you need to place them as unpacked folders into the plugins location (see prerequisites).
On start-up of a user session, Theia will load all VS Code extensions from that location automatically.

Note: .vsix files placed in the plugins location will not be automatically unpacked and loaded. If you have a .vsix file, you must manually unpack it first before placing it in the plugins location. Attempting to use .vsix files directly will result in a warning message: "Only user plugins will be handled by file handlers, please unpack the plugin manually."

A more systematic approach, however, is to publish VS Code extensions, to OpenVSX, GitHub Releases, or any other accessible location, and download them automatically at build time of the Theia application with the Theia CLI.
See publishing extensions to OpenVSX for more details.
This approach avoids having to manually distribute VS Code extensions or adding them in the form of a build artifact to your git repository.

Therefore, you need to extend your package.json to

  1. specify the plugins location to the Theia CLI
  2. define the VS Code extensions to be downloaded
  3. add a download script for convenience
json
{
  ...
  "scripts": {
    "prepare": "yarn run clean && yarn build && yarn run download:plugins",
    "download:plugins": "theia download:plugins",
    ...
  },
  "theiaPluginsDir": "plugins",
  "theiaPlugins": {
      "my-hello-world-extension": "https://",
      "vscode.git": "https://open-vsx.org/api/vscode/git/1.52.1/file/vscode.git-1.52.1.vsix",
      ...
  }
}

As a result, running yarn will download any listed extensions (including .vsix files from URLs) and automatically unpack them into the folder specified in theiaPluginsDir.
For more details, see also the documentation on composing Theia applications.

Installing VS Code Extensions at Runtime

One of the unique advantages of VS Code extensions is that they can be installed and uninstalled by users at runtime.
To enable the users of your Theia application to install VS Code extensions at runtime, you have to add @theia/vsx-registry as a dependency to your package.json.

json
{
  ...
  "name": "browser-app",
  "dependencies": {
    "@theia/core": "latest",
    ...
    "@theia/vsx-registry": "latest"
  },
  "theia": {
    "target": "browser"
  }
}

This dependency will add a view Extensions to your application, which allows searching and installing extensions from an OpenVSX registry.
By default, the public registry at http://open-vsx.org is used, the registry URL be configured via the environment variable VSX_REGISTRY_URL.
Please confer to the OpenVSX extension documentation for more details.

To make your VS Code extension available, you'll need to publish your extension to the configured registry.

Developing VS Code Extensions in a Theia Project

In certain scenarios, you may not want to develop your VS Code extension in isolation from your Theia application, but instead you may prefer to develop both your extensions and your application in project to keep update cycles short and immediate.
This is particularly useful if you develop your extensions primarily to be part a specific Theia application, thus you want to develop, test and debug them directly in the context of your Theia app, potentially alongside other Theia extensions.

In those scenarios, you can also include VS Code extensions as part of your Theia application monorepo.
While there are several possible configuration options, probably the most straightforward approach is to follow the steps below.

Generate your VS Code extension as usual.
However, to make things easier and consistent, choose TypeScript as your language and yarn as a package manager for your VS Code extension to be aligned with a typical Theia application.

Put your VS Code extension package, let's say your-vs-code-extension, into your monorepo, alongside your other packages and make sure it is part of your yarn workspaces.
Let's assume, we eventually have the following a directory structure.

text
monorepo
โ”œโ”€โ”€ apps
โ”‚   โ”œโ”€โ”€ browser-app
โ”‚   โ””โ”€โ”€ electron-app
โ”œโ”€โ”€ extensions
โ”‚   โ”œโ”€โ”€ your-theia-extension
โ”‚   โ””โ”€โ”€ your-vs-code-extension
โ”œโ”€โ”€ plugins
โ””โ”€โ”€ package.json

In your-vs-code-extension/package.json, add symlink as a dev dependency to link your extension into the plugins location of your Theia application on each build.

json
{
    ...
    "scripts": {
        "compile": "tsc -p ./",
        "watch": "tsc -watch -p ./",
        "prepare": "yarn run clean && yarn run build && yarn symlink",
        "symlink": "symlink-dir . ../../plugins/your-vs-code-extension",
        ...
    },
    "devDependencies": {
        "@types/vscode": "^1.32.0",
        "symlink-dir": "latest",
        ...
    }
}

With that, your VS Code extension will be built as part of your Theia application and directly added to the plugins location for automatic installation in your Theia application.
This leads to a consistent development experience, even if you have multiple VS Code extensions and Theia extensions combined in one Theia application.

If you aim at a consistent tsconfig.json across your VS Code extension and a typical Theia extension, you may want to change the outDir from out (default in VS Code) to lib (commonly used in Theia) and adjust the package.json accordingly.

To also enable debugging from your development IDE (VS Code), you need to setup launch.json to include the VS Code extension in the outFiles (if it is still building to the folder out and not lib) and enable source maps.

json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Start Browser Backend",
            "program": "${workspaceRoot}/browser-app/src-gen/backend/main.js",
            "args": [
                "--loglevel=debug",
                "--port=3000",
                "--no-cluster",
                "--plugins=local-dir:${workspaceRoot}/plugins"
            ],
            "env": {
                "NODE_ENV": "development",
                "NODE_OPTIONS": "--enable-source-maps"
            },
            "sourceMaps": true,
            "outFiles": [
                "${workspaceRoot}/node_modules/@theia/*/lib/**/*.js",
                "${workspaceRoot}/extensions/*/lib/**/*.js",
                "${workspaceRoot}/extensions/*/out/**/*.js",
                "${workspaceRoot}/apps/*/src-gen/**/*.js"
            ],
            "smartStep": true,
            "internalConsoleOptions": "openOnSessionStart",
            "outputCapture": "std"
        },
        ...
    ]
}

Troubleshooting

Plugins not appearing

If no plugins are available in the running Theia instance, it may be that you need to tell Theia where to find the downloaded plugins.
The example above sets the --plugins switch in the start command which should be sufficient.
However, if running theia start directly, you can alternatively set an environment variable to achieve the same thing:

text
export THEIA_DEFAULT_PLUGINS=local-dir:plugins

File: src/docs/backend_application_contribution.md


title: Backend Application Contributions

Backend Application Contributions

A backend application contribution allows Eclipse Theia extensions to hook into the lifecycle of the Theia backend. Backend application contributions are instantiated immediately after starting the backend application, which makes them a good place for initializing backend services that are required during the entire lifespan of the backend.

To register backend application contributions, an extension needs to bind an implementation of the interface BackendApplicationContribution in the backend module.

typescript
export default new ContainerModule(bind => {
   bind(MemoryTracker).toSelf().inSingletonScope();
   bind(BackendApplicationContribution).toService(MemoryTracker);
});

The interface BackendApplicationContribution provides hooks, which are all optional so that implementations can freely choose which of those hooks they are interested in, without having to implement the others.
For initialization of backend services, the most common hook is the initialize() method that is invoked as soon as the Theia backend is initialized.

As an example, letโ€™s implement a backend service that shall run during the entire lifespan of the Theia backend to track its memory usage.
For the sake of simplicity it will just print the memory to the log, whenever it changes more than a certain threshold.

Therefore, a BackendApplicationContribution named MemoryTracker is bound in the dependency injection context as shown in the code above.
The implementation of MemoryTracker reacts to the hook initialize() and executes the method logMemory() periodically every two seconds.
This method obtains the currently used memory, compares it to the previous memory and prints a message to the log as soon as the difference to the previous memory is above 0.1 MB.

typescript
@injectable()
export class MemoryTracker implements BackendApplicationContribution {

   @inject(ILogger)
   protected readonly logger: ILogger;
   protected logTimer: NodeJS.Timer;
   protected memoryUsed = 0;

   initialize(): MaybePromise<void> {
       this.logTimer = setInterval(() => this.logMemory(), 2000);
   }

   protected logMemory(): void {
       const currentMemoryUsed = this.currentRoundedMemoryUsage();
       const diff = currentMemoryUsed - this.memoryUsed;
       if (Math.abs(diff) > 0.1) {
           const timestamp = new Date().toUTCString();
           this.logger.info(
               `[${timestamp}] PID ${process.pid} uses ${currentMemoryUsed} MB (${diff > 0 ? '+' : ''}${diff.toFixed(2)})`
           );
           this.memoryUsed = currentMemoryUsed;
       }
   }

   protected currentRoundedMemoryUsage() {
       return Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100;
   }

   onStop(): void {
       if (this.logTimer) {
           clearInterval(this.logTimer);
       }
   }
}

Once this backend application contribution is registered and the backend is started, it starts logging an output, similar to the following:

text
root INFO Theia app listening on http://localhost:3000.
root INFO Configuration directory URI: 'file:///home/foobar/.theia'
root INFO [Fri, 20 Aug 2021 12:20:43 GMT] PID 46590 uses 18.14 MB (+18.14)
root INFO [Fri, 20 Aug 2021 12:20:47 GMT] PID 46590 uses 18.94 MB (+0.80)
root INFO [Fri, 20 Aug 2021 12:20:51 GMT] PID 46590 uses 15.25 MB (-3.69)
root INFO [Fri, 20 Aug 2021 12:21:07 GMT] PID 46590 uses 15.36 MB (+0.11)
root INFO [Fri, 20 Aug 2021 12:21:21 GMT] PID 46590 uses 15.47 MB (+0.11)
root INFO [Fri, 20 Aug 2021 12:21:41 GMT] PID 46590 uses 15.6 MB (+0.13)
root INFO [Fri, 20 Aug 2021 12:21:59 GMT] PID 46590 uses 15.71 MB (+0.11)

Often such backend application contributions also provide methods that can be invoked by other backend services and, for instance, start external processes that need to be available as soon as the application is started.
Examples are database connections, REST services, etc.

In addition to initializing backend services as early as the backend is started, backend application contributions can also configure and extend the HTTP server used by the Theia backend.
Therefore, the BackendApplicationContribution interface provides the three methods configure(app: express.Application), onStart(app: express.Application), and onStop(app: express.Application).
Thus, the HTTP server can be configured with custom settings and even extended with additional endpoints that need to be available for the respective Theia application.

An example for configuring an additional endpoint named /myendpoint is given below.

typescript
import { injectable } from '@theia/core/shared/inversify';
import { json } from 'body-parser';
import { Application, Router } from '@theia/core/shared/express';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';

@injectable()
export class MyCustomEndpoint implements BackendApplicationContribution {
   configure(app: Application): void {
       app.get('/myendpoint', (request, response) => {
           โ€ฆ
       });
   }
}

For more information on how to configure HTTP endpoints and handle events requests, please refer to the Express API.


File: src/docs/blueprint_documentation.md


title: Extending/Adopting the Theia IDE

Extending/Adopting the Theia IDE

This guide provides an overview on how to extend and customize the Theia IDE to your own custom IDE or tool. In this scenario, the Eclipse Theia IDE is an example product used as a reference on how to build desktop IDE-like products based on the Eclipse Theia framework. If you just want to use the Theia IDE, see the user guide

Please note that adopting the Theia IDE as a basis is just one of several ways to get started with building a Theia-based application. We recommend reading the article "Build your own IDE/Tool" as a first step. Furthermore, this guide is focused on building a desktop app. We also provide an experimental Docker version of the Theia IDE as an alternative.

The Theia IDE assembles a selected subset of existing Theia features and extensions.
We provide installers for the Theia IDE to be downloaded (see links below).
In the respective git repository you can also find the source code for the Theia IDE and its installers.

This documentation will use these sources as a template. We will explain
how to customize this template so that you can build your own custom Theia-based product including installers and packaging for installing the desktop-based version of your custom product on all major operating systems. Please note that the technical name (e.g. in the source code) for the Theia IDE is "Theia Blueprint" to avoid confusion with the generic term "IDE".

Building a product and installers

The Theia IDE build uses electron-builder to package the product as a desktop application.

The product can be built and packaged with yarn.
Note that you usually can only package the product for the operating system you execute the build on.
For more information see the electron-builder documentation on multi-platform builds.

The following commands may be run from the root directory of the repository.

To install dependencies and build the application, simply execute yarn.

You can also directly run the unpackaged application, e.g. to test it during development with yarn electron start.

With yarn electron package, you package the application into an executable file for your current operating system.
The packaged application will be located in applications/electron/dist.
The folder applications/electron/dist/<OS>-unpackaged will contain the files that are bundled into the final packaged executable.
For Linux, this is an executable .AppImage, for Windows a .exe installer, and a .dmg disk image for macOS.

You can also just create the unpackaged content by running yarn electron package:preview.
This is useful to see the bundled files and saves time compared to a full package.
To publish the current version, the command yarn electron deploy can be used.
For more information on publishing also see section "Configure publish and update".

Signing

Electron-builder supports signing the packaged application on Windows and macOS.
The current signing scripts for the Theia IDE are located in applications/electron/scripts.
The file after-pack.js is the current entry point for the configured signing via Eclipse infrastructure.

However, as signing is highly dependent on your setup, see the electron builderโ€™s signing documentation on how to properly set up your own signing.

Adding/Removing Features

The Theia IDE is based on the Theia platform, which is a flexible and adaptable platform for build tools and IDEs. Therefore, you can adapt the feature set and general appearance of the Theia IDE to your custom requirements with almost no limits. The Theia platform provides two mechanism to add your custom extensions: VS Code extensions and Theia extensions. Please have a look at the overview about Theia extension capabilities for details. When assembling a product such as the Theia IDE, you can freely decide, which VS Code extensions and Theia extensions are part of it and thereby influence the feature set of your custom product. The following two sections describe how to modify which VS Code Extensions and which Theia extensions are part of your product. Please also note that you can allow users of a Theia-based tool to install VS Code extensions at runtime.

Updating Bundled VS Code Extensions

All VS Code extensions that are already included in the product at start-up ("built-ins"), are defined in applications/electron/package.json.
They are listed under the theiaPlugins property as key-value pairs.
The keys can be freely chosen as long as they represent a valid folder name and are unique within the theiaPlugins property.
We suggest using the extensionโ€™s unique identifier.
The value is the download URL of the extensions.
It will automatically be downloaded during the applicationโ€™s build process.
Any new plugin will be automatically downloaded the next time one of the following npm scripts is executed:

  • install (which is the same as just running yarn)
  • prepare
  • download:plugins

To remove an extension from the product, simply delete its entry.
If plugins were not already downloaded, no further steps are required as downloaded plugins are ignored via gitignore.
However, previously downloaded plugins are not automatically removed.
Therefore, you need to remove its folder from the applications/electron/plugins folder.
Alternatively, you can remove the whole applications/electron/plugins folder and execute yarn electron download:plugins to download all defined plugins.

Extension sources

We use the Open VSX Registry of the Eclipse Foundation to install extensions.
It is an open and community-driven VS Code extension marketplace.
More information can be found at eclipse.org.

Customizing Theia Extensions

Eclipse Theia extensions can be added through dependencies in applications/electron/package.json.
Like any other dependency, it will be installed via yarn.
Similarly, removing an extension works by removing it from dependencies.
For extensions already published on npm (or your private npm registry) this is all you need to do.

An alternative approach is developing your extension inside Theia IDE's mono repo.
The advantage of doing this is that you don't need to publish the extension and can build the product with the local version of the extension.
This is facilitated by the lerna build already configured in the Theia IDEs's repository.
It links the product and all extensions in the repository together during the build.

Adding Yeoman-Generated Extensions to Theia IDE

Many users start with the Yeoman generator as described in Build your own IDE/Tool, and later want to integrate their extensions into the Theia IDE. This section explains how to add an existing Yeoman-generated extension to your custom Theia IDE.

Prerequisites

  • You have a Yeoman-generated Theia extension (created with yo theia-extension --standalone)
  • You have cloned the Theia IDE repository

Step-by-Step Integration

  1. Move your extension into the Theia IDE repository

    Copy your extension folder into the theia-extensions directory of the Theia IDE repository. For example, if your extension is called hello-world, copy it so it becomes theia-extensions/hello-world.

  2. Verify workspace configuration

    The Theia IDE's root package.json already includes theia-extensions/* in its workspaces configuration, so your extension is automatically recognized. No manual editing of the workspaces array is needed.

  3. Find your extension's name and version

    Open your extension's package.json file (e.g., theia-extensions/hello-world/package.json) and look for the name and version fields:

    json
    {
      "name": "hello-world",
      "version": "0.0.0",
      ...
    }

    You will need these values in the next step.

  4. Add your extension as a dependency

    Open applications/electron/package.json (for the desktop application) or applications/browser/package.json (for the browser application), or both if you want your extension in both versions.

    Add your extension to the dependencies section using the name and version from the previous step:

    json
    "dependencies": {
      ...
      "theia-ide-launcher-ext": "1.65.100",
      "theia-ide-product-ext": "1.65.100",
      "theia-ide-updater-ext": "1.65.100",
      "hello-world": "0.0.0"
    }
  5. Install dependencies and build

    From the repository root, run:

    bash
    yarn && yarn build

    This will install all dependencies, link your extension with the Theia IDE, and build the application with your extension included.

  6. Run and test

    Start the Theia IDE to test your extension:

    bash
    yarn (browser|electron) start

    Your extension's features should now be available in the Theia IDE.

Important Notes

  • Version Compatibility: Check that your extension's @theia/core dependency version (in your extension's package.json) is compatible with the Theia version used by the Theia IDE. You may need to update the version if they differ significantly.
  • Browser and Electron: Remember to add your extension to both applications/browser/package.json and applications/electron/package.json if you want it available in both versions.
  • Always Build: Always run yarn && yarn build after adding new extensions before starting the application.
  • Monorepo Structure: Once your extension is in the theia-extensions folder, it becomes part of the monorepo and will be built alongside other extensions.

Branding

You can also add your own branding to the product by customizing the application icons and title, the welcome page and the About dialog.
In addition, some parts of the installer can be customized.

Customizing the App

Application Window Title

The window title is the applicationโ€™s name if no workspace is opened and <workspace name> โ€” <application name> if a workspace is opened.
The application name can be adapted in applications/electron/package.json by navigating through the nested properties.

To change the application name, open the file and locate the following nested structure:

json
"theia": {
    "frontend": {
      "config": {
        "applicationName": "Theia IDE",
      },
    },
  },

Change the value of applicationName to the desired name.
Also see the structure in the original file.

Application Icons

Application icons are located in applications/electron/resources/.
Simply replace them with your own icons.
Because each operating system handles icons differently, they should all be replaced to ensure proper use.
They map as follows:

  • macOS: icons.icns
  • Windows: icon.ico
  • Linux: icons subfolder

Customizing the Welcome Page

The Eclipse Theia welcome page can be customized by binding a custom WidgetFactory for Theiaโ€™s GettingStartedWidget.
This is done with the Theia IDE in the theia-blueprint-product extension.
The easiest way to customize the welcome page is to adapt the class TheiaBlueprintGettingStartedWidget in theia-extensions/theia-blueprint-product/src/browser/theia-blueprint-getting-started-widget.tsx.

The widget is bound in theia-extensions/theia-blueprint-product/src/browser/theia-blueprint-frontend-module.ts like this:

typescript
bind(TheiaBlueprintGettingStartedWidget).toSelf();
    bind(WidgetFactory).toDynamicValue(context => ({
        id: GettingStartedWidget.ID,
        createWidget: () => context.container.get(TheiaBlueprintGettingStartedWidget),
    })).inSingletonScope();

To use another custom widget, remove this code and bind your widget correspondingly.

Customizing the About Dialog

The Eclipse Theia about dialog can be customized by binding a custom subclass of Theiaโ€™s AboutDialog class to AboutDialog.
This is done with Theia IDE in the theia-blueprint-product extension.
The easiest way to customize the about dialog is to adapt the class TheiaBlueprintAboutDialog in theia-extensions/theia-blueprint-product/src/browser/theia-blueprint-about-dialog.tsx.

The widget is bound in theia-extensions/theia-blueprint-product/src/browser/theia-blueprint-frontend-module.ts like this:

typescript
isBound(AboutDialog) ? rebind(AboutDialog).to(TheiaBlueprintAboutDialog).inSingletonScope() : bind(AboutDialog).to(TheiaBlueprintAboutDialog).inSingletonScope();

To use another custom dialog widget, remove this code, extend Theiaโ€™s AboutDialog class, and (re)bind it as above.

Customizing the Preferences

The default preferences directory in Eclipse Theia IDE is .theia-blueprint and is located as described in the Preferences documentation. You can customize this location by modifying theia-blueprint-variables-server.ts.

Customizing the Electron Splash Screen

Since Theia 1.49.0 Theia Electron applications support displaying a splash screen before showing the main window.

The splash screen can be enabled within the application package.json's Theia options by providing an object at theia.frontend.config.electron.splashScreenOptions with the following properties:

typescript
{
    content: string  // <mandatory> path to the content to render within the splash screen, resolved from application root
    width: number // default 640
    height: number // default 480
    minDuration: number // minimum amount of time in milliseconds to show the splash screen before main window is shown. default 0
    maxDuration: number // maximum amount of time in milliseconds before splash screen is removed and main window is shown. default 30000
}

If not configured otherwise via minDuration, the splash screen will be shown until the frontend is ready, i.e. when the loading spinner is gone.
Then the splash screen will be closed and the main window is shown.

Showing Windows Early

The main window / splash screen can be configured to be shown "early", i.e. the window will be shown before it's ready to render content.
By default this is true to give the user visual feedback as early as possible.

If you prefer only showing the main window / splash screen once they are ready to render content, then you can configure theia.frontend.config.electron.showWindowEarly: false in your application's package.json.

Customizing the Installer

The installers are created using electron-builder.
The corresponding configuration file is located at applications/electron/electron-builder.yml.

Installer File Base Name

The installer filesโ€™ base names are defined by the productName property in applications/electron/electron-builder.yml.

Windows Installer

As is typical for Windows applications, there is an installation wizard for the Windows version of the Theia IDE.
The installer is configured in the nsis section of the configuration file.
Available customizations include settings such as:

  • Icons
  • Sidebar image
  • License
  • One click installation
  • Automatic application start after installation
  • Whether users can change the installation directory

More details on available options and how they can be customized can be found in the official electron builder documentation.
This documentation also includes information about more advanced features such as custom NSIS scripts.

Configure Publish and Update

The Theia IDE uses electron-builder to create and publish installers.
It also uses electron-updater, developed by the electron-builder organization, to provide automatic updates of the installed application.

There are various deployment targets which can be configured in the applications/electron/package.json and applications/electron/electron-builder.yml as documented here in the Electron Builder documentation.
Multiple publish configurations can be configured.
Thereby, the first one is automatically used by the updater to look for available updates.
The currently used generic publishing method does not automatically publish to the specified server, but is just used as the lookup location for the updater.


File: src/docs/blueprint_download.md


title: Eclipse Theia Blueprint

Eclipse Theia Blueprint

Eclipse Theia Blueprint has been rebranded to "Theia IDE" and has moved to this page.


File: src/docs/breadcrumbs.md


title: Breadcrumbs

If enabled via the preferences, Theia can show a navigation bar, often referred to as breadcrumbs, above the content of widgets that are located in the main area of the workbench.
This interactive navigation bar indicates the location of the widget's content in the context of an overall structure, such as the filesystem.
By default, Theia shows breadcrumbs for the location of files in the filesystem when a file is opened in the text editors.

However, Theia's breadcrumbs mechanism can also be used for any custom widget, too.
Moreover, Theia allows showing a widget's content location in an arbitrary custom logical structure, such as a custom project structure.
On this page, we describe how custom breadcrumbs work and how widgets declare their location within an overall structure.

What is being shown in the breadcrumbs of a widget is defined by breadcrumbs contributions.
Such contributions must implement the BreadcrumbsContribution interface and return an array of Breadcrumbs for a specific URI, which are then shown for any widget that specifies this URI as their content's location.
A widget declares the location of its content in the form of a URI by implementing the interface NavigatableWidget.

So let's look at an example widget that declares an arbitrary URI.

typescript
@injectable()
export class CustomWidget extends ReactWidget implements NavigatableWidget {
    // ...
    getResourceUri(): URI | undefined {
        return new URI('resource://project-a/component-b/config-c');
    }
}

When this CustomWidget is opened, Theia will look for breadcrumbs contributions that return breadcrumbs for this URI.
By default, it won't find any and thus won't show any breadcrumbs.
So let's add a breadcrumbs contribution that reacts to URIs with the scheme resource as used by our widget above.

typescript
export default new ContainerModule(bind => {
    bind(CustomBreadcrumbsContribution).toSelf().inSingletonScope();
    bind(BreadcrumbsContribution).toService(CustomBreadcrumbsContribution);
});

@injectable()
export class CustomBreadcrumbsContribution implements BreadcrumbsContribution {
    type: symbol = CustomBreadcrumbType;
    priority: number = 100;

    protected readonly onDidChangeBreadcrumbsEmitter = new Emitter();
    get onDidChangeBreadcrumbs(): Event {
        return this.onDidChangeBreadcrumbsEmitter.event;
    }

    computeBreadcrumbs(uri: URI): MaybePromise {
        if (uri.scheme !== 'resource') {
            return [];
        }
        // uri is 'resources://project-a/component-b/config-c' for our widget above
        // but let's just hard code breadcrumbs as an example for the sake of simplicity
        return [
            {
                id: 'project',
                label: 'Project A',
                longLabel: 'The project',
                iconClass: 'codicon codicon-folder default-folder-icon file-icon',
                type: CustomBreadcrumbType
            },
            {
                id: 'component',
                label: 'Component B',
                longLabel: 'A component within a project',
                iconClass: 'codicon codicon-file default-file-icon file-icon',
                type: CustomBreadcrumbType
            },
            {
                id: 'config',
                label: 'Configuration C',
                longLabel: 'A configuration within a component',
                iconClass: 'markdown-icon medium-blue theia-file-icons-js file-icon',
                type: CustomBreadcrumbType
            }
        ];
    }

    // ...
}

Once the breadcrumbs contribution is registered, Theia will show the following for our custom widget.

Interactive Breadcrumbs

Breadcrumbs contributions also must implement the method attachPopupContent.
Theia invokes this method as soon as a user clicks on one of the breadcrumbs and passes a parent HTML element into which the breadcrumbs contribution can now add arbitrary HTML content, including trees, buttons, etc.
This content is consequently being shown as a popup below the clicked breadcrumbs element.
If this method returns a Disposable, Theia will call it, once the popup is closed, allowing to dispose resources that may have been allocated for the rendered content.

As an example, let's just return add some simple HTML to the popup, as shown below.

typescript
@injectable()
export class CustomBreadcrumbsContribution implements BreadcrumbsContribution {
    // ...
    attachPopupContent(breadcrumb: Breadcrumb, parent: HTMLElement): Promise<Disposable | undefined> {
        parent.innerHTML = '<h3>'
                            + breadcrumb.label
                            + '</h3>We can show arbitrary content here and react to clicks.';
        return Promise.resolve(undefined);
    }
}

Priority of Breadcrumbs Contributions

As defined by the BreadcrumbsContribution interface, a contribution must define a priority.
The property is used by Theia to properly order the breadcrumbs returned by all breadcrumbs contributions.
Thus, if you have for instance three breadcrumbs contributions that return a non-empty array of breadcrumbs for a particular URI, we'll end up with a final breadcrumbs list that contains all breadcrumbs of all three contributions ordered from lowest to highest priority.
The Theia text editor uses this mechanism to append logical structures of code to a source file, for instance.
Contributions that return an empty array for a specific URI are obviously ignored.

text
Contribution A with a priority of 10 returns [a, b]
Contribution B with a priority of 20 returns [c, d]
Contribution C with a priority of 30 returns [e, f]
=> [a, b, c, d, e, f]

File URIs

Theia's filesystem extension registers a breadcrumbs contribution that returns breadcrumbs according to the directory hierarchy for any file URI, such as file://home/user/a/b/c.txt.
The breadcrumbs returned by that contribution are also interactive by showing a file tree on click for navigating to adjacent files.

You can also make use of the filesystem breadcrumbs in custom widgets by just returning file URIs in your implementation of NavigatableWidget#getResourceUri().
If needed, you can also register additional breadcrumbs contributions with a high number as priority to append to the file URI shown in the breadcrumbs, such as for navigating to specific sections in your custom widget.

Accordingly, however, if you want to avoid showing filesystem breadcrumbs altogether and only use custom breadcrumbs, your widget must return a URI that isn't a file URI, for instance resource://a/b/c.


File: src/docs/commands_keybindings.md


title: Commands/Menus/Keybindings

Commands, Menus and Keybindings

Commands are runnable actions defined by an ID and the function to be executed (plus some optional parameters like a name or an icon). Commands can be triggered via the command palette, they can be bound to keybindings or menu items, and they can be called programmatically. The action that commands trigger can be context-sensitive, so that they can only be called under certain conditions (window focus, current selection etc.).

The following sections provide details about how to contribute commands, keybindings and menu items. The sections will describe how to connect the different contributions and how to use the corresponding services for managing these items.

If you are not yet familiar with contribution points in Theia or the use of dependency injection, please consider this guide on Services and Contributions.

All the following code examples are from the Theia extension generator. You can get the same code set-up by installing the generator, selecting the โ€œHello Worldโ€ example (see here) and choosing โ€œhelloworldโ€ as the name.

Contributing Commands

By contributing a command, you can add a custom action to Theia. A command can be triggered by the user via the command palette, a keybinding or a menu entry. It can also be called programmatically.

All commands of a Theia application are managed in the CommandRegistry. To contribute commands to the command registry, modules must implement the CommandContribution interface (see code example below).

A command is an object with an id and an optional, potentially user visible label (see HelloworldCommand in the example below). The command contribution receives the CommandRegistry in the registerCommands function as a parameter. The command can then be registered by calling registerCommand on this registry. Along with the command a callback needs to be provided which is executed whenever the command is triggered (CommandHandler). In the example, the command uses the MessageService to โ€œsay helloโ€.

helloworld-contribution.ts

typescript
export const HelloworldCommand: Command = {
   id: 'Helloworld.command',
   label: "Say Hello"
};

@injectable()
export class HelloworldCommandContribution implements CommandContribution {

   constructor(
       @inject(MessageService) private readonly messageService: MessageService,
   ) { }

   registerCommands(registry: CommandRegistry): void {
       registry.registerCommand(HelloworldCommand, {
           execute: () => this.messageService.info('Hello World!')
       });
   }
}

To make the command execution context-sensitive, the CommandHandler can optionally implement isEnabled and isVisible. You can optionally register more than one CommandHandler and let the command execute one of them based on the current context. To register additional handlers for a command use the registerHandler function on the CommandRegistry
When a command is executed, the command registry checks all registered handlers. The first handler that returns true on isEnabled will be considered to be active, and it will be executed. Only one handler should be active (isEnabled === true) at the same time, though. isVisible controls the visibility of menu items and tool items that are connected to a command, as well as whether the command is shown in the command palette. If the active handler returns true, the menu item will be visible and vice versa.
Finally, by implementing isToggle a handler can optionally specify, whether menu items connected to the commands should be toggled on or off.

Binding the contribution to CommandContribution

To make our CommandContribution accessible to Theia, we need to bind the custom HelloworldCommandContribution to the respective contribution symbol CommandContribution. This is done in the helloworld-frontend-module, for more details see Services and Contributions.

helloworld-frontend-module.ts

typescript
export default new ContainerModule(bind => {
   // add your contribution bindings here
   bind(CommandContribution).to(HelloworldCommandContribution);
...
});

The CommandRegistry used to register our contributed command above, also provides an API to interact with commands. As an example, you can programmatically execute commands, you can browse through all registered commands or you can access a list of recently executed commands. Please refer to the TypeDoc for the CommandRegistry for more details. To use the CommandRegistry outside of a contribution, you can access it via dependency injection.

In the following sections, we describe how to bind commands to menu items and keybindings.

Contributing Menu Items

Theia allows you to contribute menu items that will be displayed in specific menus within your Theia application. Menu items are bound to commands and therefore allow the user to trigger actions (please see the section about commands above).

All the following code examples are from the Theia extension generator. You can get the same code set-up by installing the generator, selecting the โ€œHello Worldโ€ example (see here) and choosing โ€œhelloworldโ€ as the name.

All menu items of a Theia application are managed in the MenuModelRegistry. To contribute menu items to the registry, modules must implement the ยดMenuContributionยด interface (see code example below).

The registration of the command can be done in the function registerMenus, which will be called by the Theia framework. The function provides the MenuModelRegistry as a parameter. On this registry we can call registerMenuAction. It expects a MenuPath and a MenuAction. The MenuPath specifies the menu (and submenu) to place the menu item into. Please see here for the paths of some common menus.

The MenuAction consists of a command id, specifying which command to trigger, and an optional label, specifying the label of the menu item.

helloworld-contribution.ts

typescript
@injectable()
export class HelloworldMenuContribution implements MenuContribution {

   registerMenus(menus: MenuModelRegistry): void {
       menus.registerMenuAction(CommonMenus.EDIT_FIND, {
           commandId: HelloworldCommand.id,
           label: HelloworldCommand.label
       });
   }
}

To make our MenuContribution accessible to Theia, we need to bind the custom HelloWorldMenuContribution to the respective contribution symbol MenuContribution. This is done in the helloworld-frontend-module, for more details see Services and Contributions.

helloworld-contribution.ts

typescript
export default new ContainerModule(bind => {
   bind(MenuContribution).to(HelloworldMenuContribution);
      ...
});

Please note that you also contribute menu items without a command which allows you to create custom top level menus and sub menus. To achieve this, contribute a menu item without a command and then reference the id of this menu item as a MenuPath in other contributes. This will add menu items to your custom menu.

Contributing Keybindings

Keybindings allow the user to trigger commands using specific key combinations. Keybindings can define conditions, specifying when they are active. As an example, there can be keybindings that are only active when the text editor is focused.

Please note that the following code examples are not part of the generated template, so you will need to manually add them (see previous sections).

To contribute a keybinding, implement a KeybindingContribution (see code example below) with which you can access the KeybindingRegistry, allowing you to register a keybinding. Keybindings consist of:

  • keybinding: The key combination
  • command: The id of the command to be triggered
  • when(optional): The condition when the keybinding should be active

helloworld-keybinding-contribution.ts

typescript
export class HelloworldKeybindingContribution implements KeybindingContribution {
    registerKeybindings(keybindings: KeybindingRegistry): void {
        keybindings.registerKeybinding({
            keybinding: "alt+enter",
            command: 'Helloworld.command',
            when: 'editorFocus && editorIsOpen'
        });
    }
}

The syntax for the โ€œwhenโ€ clause follows the VS Code terminology. Modifiers are platform independent, so Modifier.M1 is Command on OS X and CTRL on Windows/Linux. Key string constants can be viewed in Key documentation.

Just as you needed to bind the contributions before, keybinding contributions also need to be bound to the symbol KeybindingContribution to make them accessible for Theia.

editor-frontend-module.ts

typescript
export default new ContainerModule(bind => {
    bind(KeybindingContribution).to(HelloworldKeybindingContribution);
    ...
});