LSP4IJ provides the capability to consume any language server without developing
an IntelliJ plugin via a User-defined language server.
The main idea is to:
- install the language server and its requirements(ex :
Node.jsto execute a language server written in JavaScript/TypeScript), - declare the command that starts the language server.
- associate the language server with the proper files (identified by IntelliJ Language, File Type or file name pattern)
In order to create a new User-defined language server, you need to open the New Language Server dialog, either:
- from the menu on the right of the LSP console:
- or with the
[+]on the top of the language server settings:
Once you clicked on either of them, the dialog will appear:
When you click the OK button, the language server will be created.
If the language server definition declares an installer
in the Installer tab, the installer will run once when the language server starts.
You can reinstall the server at any time (e.g. to fetch the latest version) using the Reinstall action:
The Server tab requires the server name and command fields to be set.
Here is a sample with the typescript-language-server:
The environment variables accessible by the process are populated with EnvironmentUtil.getEnvironmentMap() which retrieves system variables.
It is also possible to add custom environment variables via the Environment variables field:
Depending on your OS, the environment variables may not be accessible. To make sure they are accessible, you can fill out the order fields:
- with
Windows OS:cmd /c command_to_start_your_ls - with
Linux,Mac OS:sh -c command_to_start_your_ls
You can use built-in macros in your command. You could, for instance, store the language server in your project (to share it with your team) and write a command that references it in a portable way to start it.
That command might look like this:
$PROJECT_DIR$/path/to/your/start/command
Here are some useful standard built-in macros that you can use:
$WORKSPACE_DIR$: The path to the workspace where the current project belongs. The workspace is the root of the open file hierarchy and can include multiple projects.$PROJECT_DIR$: The root of the project where run.json is located. A project is typically a collection of files for developing and building an application such as a Maven or Node.js project.$USER_HOME$: User home directory.
Here is an example with Scala Language Server's metals.bat stored at the root of the project:
When commands contain macros, their resolved value is visible below the Command field.
The Mappings tab provides the capability to associate the language server with the proper files identified by:
- IntelliJ Language
- IntelliJ File type
File name pattern
Here are mappings samples with the typescript-language-server:
- The existing
JavaScriptfile type is used to associate the file to the language server:
- Since IntelliJ (Community) doesn't provide file type by default
TypeScript,Reactfile name patterns are used:
NOTE: it is better to use file name pattern instead of creating custom file type for TypeScript, since by default
IntelliJ Community support TypeScript syntax coloration with TextMate. If you define a file type, you will
lose syntax coloration.
When you declare mapping, you can fill the Language ID column which is used to declare the LSP TextDocumentItem#languageId
to identify the document on the server side.
For instance the vscode-css-languageservice (used by the vscode CSS language server) expects the languageId to be css or less.
To do that, you can declare it with the languageId attribute:
The Configuration tab allows to configure the language server with the expected (generally JSON format) configuration.
Here are configuration sample with the typescript-language-server:
The Debug tab is available when you have created the language server definition. It allows to customize the
level Trace used in LSP console.
The Installer tab provides the capability to declare with JSON, the language server installation
with task to execute like:
execto execute a command (ex:npm install typescript-language-serverto install the typescript-language-server).downloadto download the language server from a given url according the OS.
This installer content will be used when you will create the language server. See here to understand how to write this JSON installer descriptor.
Workspace folders define the scope of files that the language server will analyze. They can be sent to the language server:
- At startup via the initialize request
- Dynamically via workspace/didChangeWorkspaceFolders notifications
This tab allows you to configure and preview how workspace folders will be discovered and sent to your language server.
The rootType defines which IntelliJ project structure to use as workspace folders:
PROJECT_BASE(default): Uses content rootsSOURCE_ROOTS: Uses source rootsNONE: No workspace folders
{
"rootType": "SOURCE_ROOTS"
}This configuration will populate InitializeParams.workspaceFolders when the language server starts:
{
"workspaceFolders": [
{
"name": "my-module",
"uri": "file:///path/to/my-module"
}
]
}If rootType strategies don't match your project structure, use markers to define workspace folders based on specific files. This is useful for:
- Mono-repos with multiple independent modules
- Python projects with
pyproject.tomlorsetup.py - Projects where workspace boundaries are defined by configuration files
{
"markers": [
"pyproject.toml",
"pom.xml",
".git"
]
}When a file is opened, LSP4IJ walks up the directory tree to find the closest marker file and uses its directory as the workspace folder.
Note: Marker-based discovery always uses lazy mode.
By default (eager mode), all workspace folders are sent at startup via the initialize request. For large projects, this can cause the language server to load and analyze many files unnecessarily.
Lazy mode defers workspace folder discovery: folders are sent progressively via workspace/didChangeWorkspaceFolders notifications as you open files.
{
"rootType": "PROJECT_BASE",
"lazy": true
}Example: Consider a project with 2 content roots: client and common.
Eager mode (lazy: false):
- All folders sent at startup in the
initializerequest:{ "workspaceFolders": [ { "uri": "file:///.../client", "name": "client" }, { "uri": "file:///.../common", "name": "common" } ] } - Language server loads all files from both
clientandcommon, even if you only open a file incommon
You can see this behavior with the following configuration:
Lazy mode (lazy: true):
- Empty workspace folders at startup:
{ "workspaceFolders": [] } - When you open a file from
common, only that folder is sent viaworkspace/didChangeWorkspaceFolders:{ "event": { "added": [{ "uri": "file:///.../common", "name": "common" }], "removed": [] } }
The preview panel on the right shows how workspace folders will be sent based on your configuration.
Checkbox unchecked - Shows all discovered workspace folders (discovery mode):
Checkbox checked - Shows only folders sent at initialization (empty in lazy mode):
Drag & drop or click "Open file..." to simulate opening a file and see which workspace folder it belongs to:
If a file is not included in any workspace folder, it will appear under a "(No root)" node:
Template can be used to quickly create user defined language server pre-filled with server name, command, mappings and potential configurations.
The Template combo-box provides some default language servers templates (located in templates directory classpath),
pre-filled with server name, command, mappings and potential configuration.
- Ada Language Server
- Apache Camel Language Server
- Astro Language Server
- CSS Language Server
- Clangd
- Clojure LSP
- Dart LSP
- Docker Language Server
- EO LSP Server
- Erlang Language Server
- ESLint Language Server
- Go Language Server
- Harper Language Server
- JQ Language Server
- Julia Language Server
- Perl Language Server
- Ruby LSP
- Rust Language Server
- Scala Language Server (Metals)
- SourceKit-LSP
- Stylelint-LSP
- Svelte Language Server
- Terraform Language Server
- TypeScript Language Server
- Vue Language Server
- WGSL Analyzer
If the template directory contains a README.md file, you can open the instructions by pressing the help icon.
The Import from custom template... item from the Template combo-box can be used to select a directory from
the file system to load a custom language server template,
these templates can be pre-filled with server name, command, mappings and potential configuration.
The selected directory contents should match the custom template structure.
If the template directory contains a README.md file, you can open the instructions by pressing the help icon.
Custom templates can be created by exporting templates.
Users can export their own language servers to a zip file, where each language server is a separate directory. This can be done from the LSP console, by selecting one or more language servers and selecting the export option from the context menu.
These directories can then be used as a template for a new language server by importing a custom template.
By default, each directory contains the following files, but only template.json is required.
template.jsonsettings.jsoninitializationOptions.json
A README.md file can be added manually to each of the language server directories to provide instructions
for the corresponding language server.
See here for more information about those JSON structures.



















