Development IntelliJ IntelliJ IDEA Internals Plugins

Language Server Protocol for Plugin Developers

Read this post in other languages:

The Language Server Protocol (LSP) is an open-standard protocol developed by Microsoft that enables communication between development tools and Language Servers. By incorporating LSP into the IntelliJ Platform, we enable plugin developers to introduce support for a custom language by using specific LSP server integration. The Language Server can provide language-specific features such as code completion, documentation, and formatting, which is far easier than implementing language support from scratch and saves time and effort. It also reduces the need for constant maintenance and tracking of changes in relevant languages and tools, making it easier to bring consistent language support to various development environments.

However, we must remember that canonical custom language support provided by IntelliJ Platform still offers a wider range of integration with IDE features than handling and presenting data provided by a Language Server. Therefore, the LSP approach shouldn’t be considered as a replacement for the existing language API, but rather as an added value.

Starting with the 2023.2 release cycle, the LSP API is publicly available as part of the IntelliJ Platform in the following IDEs:

  • IntelliJ IDEA Ultimate
  • WebStorm
  • PhpStorm
  • PyCharm Professional
  • DataSpell
  • RubyMine
  • CLion
  • Aqua
  • DataGrip
  • GoLand
  • Rider

The following sections explain how we can build a simple integration with Language Server, discover available features and documentation, and find out about the current state of LSP support in IntelliJ-based IDEs.

Plugin configuration

To fully utilize the Language Server Protocol API in a third-party plugin based on the Gradle build system, we recommend upgrading the Gradle IntelliJ Plugin to version 1.15.0 or higher. This plugin will attach the LSP API sources and code documentation to your project.

As LSP became available in the 2023.2 EAP7 of IntelliJ-based IDEs, the plugin must target IntelliJ IDEA Ultimate 232-EAP-SNAPSHOT or later, or v2023.2 once it becomes generally available.

Example build.gradle.kts configuration:

plugins {
   // ...
   id("org.jetbrains.intellij") version "1.15.0"
}   


intellij {
   version = "232-EAP-SNAPSHOT"
   type = "IU"
}

For projects based on the IntelliJ Platform Plugin Template, update the Gradle IntelliJ Plugin to the latest version, and amend the gradle.properties file as follows:

platformType = IU
platformVersion = 232-EAP-SNAPSHOT
pluginSinceBuild = 232
pluginUntilBuild = 232.*

The plugin.xml configuration file needs to specify the dependency on the IntelliJ IDEA Ultimate module:

<idea-plugin>
   <!-- ... -->


   <depends>com.intellij.modules.platform</depends>
   <depends>com.intellij.modules.ultimate</depends>
</idea-plugin>

With the above changes, we can access the LSP API along with its source code and documentation.

The LSP API sources are bundled in IntelliJ IDEA Ultimate and can be found within the [IDE]/lib/src/src_lsp-openapi.zip archive. If you use Gradle IntelliJ Plugin 1.15.0+, they become automatically resolved and mapped.

Supported features

The initial LSP support within the IntelliJ Platform covers the following features:

More features will be announced soon.

Basic implementation

A minimal LSP integration must implement LspServerSupportProvider along with a service descriptor and register it as a com.intellij.platform.lsp.serverSupportProvider extension point:

import com.intellij.platform.lsp.api.LspServerSupportProvider
import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor


class FooLspServerSupportProvider : LspServerSupportProvider {
   override fun fileOpened(project: Project, file: VirtualFile, serverStarter: LspServerStarter) {
       if (file.extension == "foo") {
           serverStarter.ensureServerStarted(FooLspServerDescriptor(project))
       }
   }
}


private class FooLspServerDescriptor(project: Project) : ProjectWideLspServerDescriptor(project, "Foo") {
   override fun isSupportedFile(file: VirtualFile) = file.extension == "foo"
   override fun createCommandLine() = GeneralCommandLine("foo", "--stdio")
}

After providing the implementation of the LSP Server support provider extension point, register it in the plugin.xml file as follows:

<idea-plugin>
   <!-- ... -->


   <depends>com.intellij.modules.platform</depends>
   <depends>com.intellij.modules.ultimate</depends>


   <extensions defaultExtensionNs="com.intellij">
       <platform.lsp.serverSupportProvider implementation="FooLspServerSupportProvider"/>
   </extensions>
</idea-plugin>

As a reference, check out the Prisma ORM open-source plugin implementation: https://github.com/JetBrains/intellij-plugins/tree/master/prisma/src/org/intellij/prisma/ide/lsp

Language Server integration

Language Server is a separate process that analyzes source code and provides language-specific features to development tools. When creating a plugin that utilizes LSP within the IDE, there are two possibilities for providing a Language Server to end-users:

  • Bundle a Language Server implementation binary as a resource delivered with a plugin.
  • Provide a possibility for users to define the location of the Language Server binary in their environment.

The Prisma ORM plugin presents the first approach, which distributes the prisma-language-server.js script and uses a local Node.js interpreter to run it.

For more complex cases, you may request to provide a detailed configuration with a dedicated Settings implementation.

To implement a minimal but fully functional plugin, perform the following steps:

  • Within the LspServerSupportProvider.fileOpened() method, spin up the relevant LSP server descriptor, which can decide if the given file is supported by using the LspServerDescriptor.isSupportedFile() check method.
  • Tell how to start the server by implementing LspServerDescriptor.createCommandLine().

Customization

  • To fine-tune or disable the implementation of LSP-based features, the plugins may override the corresponding properties of the LspServerDescriptor class. See the properties documentation for more details:
    • lspGoToDefinitionSupport
    • lspCompletionSupport
    • lspDiagnosticsSupport
    • lspCodeActionsSupport
    • lspCommandsSupport
  • To handle custom (undocumented) requests and notifications from the LSP server, override LspServerDescriptor.createLsp4jClient.
  • To send custom (undocumented) requests and notifications to the LSP server, override LspServerDescriptor.lsp4jServerClass and implement the LspClientNotification and/or LspRequest classes.

See bundled LSP API source code and its code documentation for more information.

Testing

Utility classes used for testing are not yet extracted for public use, but we’ll provide suitable methods for testing LSP-based third-party plugins as soon as possible.

Troubleshooting

All the IDE and LSP server communication logs are passed to the IDE log file.

To include them for preview, add the following entry to the Help | Diagnostic Tools | Debug Log Settings… configuration dialog:

#com.intellij.platform.lsp

For more information, see the Logging section in the IntelliJ Platform SDK documentation.

Limitations

  • The current LSP API implementation assumes that the IDE <-> LSP server communication channel is stdio.
  • The IDE doesn’t send workspace/didChangeWatchedFiles notifications to the server.

Conclusion

Integrating the Language Server Protocol (LSP) into a plugin for IntelliJ-based IDEs involves a trade-off between simple and fast language support and a complex custom language support plugin with IDE capabilities.

When considering the LSP-based approach, it is important to assess the following criteria for providing a Language Server to end users:

  • OS dependency of the Language Server.
  • Availability of the latest version online.
  • Compatibility with breaking changes between versions.
  • Feasibility of requesting the user to provide the Language Server binary path.

Once the integration with the Language Server process is resolved, referring to the IntelliJ SDK Documentation is still important. This will provide a comprehensive overview of the available features for seamless integration with the LSP standard within the IntelliJ Platform.

Please note that the integration with the Language Server Protocol is created as an extension to the paid IntelliJ-based IDEs. Therefore, plugins utilizing Language Server integration may not be available in Community releases of JetBrains products and Android Studio from Google.

If you encounter any issues or need assistance, please provide feedback by reaching out to us through the #intellij-platform channel in our JetBrains Platform Slack workspace or by submitting an issue in YouTrack.