Platform logo

JetBrains Platform

Plugin and extension development for JetBrains products.

Development IntelliJ IntelliJ IDEA Internals Plugins

面向插件开发者的语言服务器协议

Read this post in other languages:

语言服务器协议 (LSP) 是 Microsoft 开发的一项开放标准协议,用于开发工具与语言服务器之间的通信。 我们将 LSP 整合到 IntelliJ 平台,使插件开发者能够使用特定 LSP 服务器集成引入自定义语言支持。 语言服务器可以提供针对特定语言的功能,例如代码补全、文档和格式设置,这比从头开始实现语言支持要容易得多,省时省力。 它还减少了持续维护和跟踪相关语言与工具更改的需要,使不同开发环境更易获得一致的语言支持。

但是,请记住,与处理和呈现语言服务器提供的数据相比,IntelliJ 平台提供的规范自定义语言支持仍然提供了与 IDE 功能更广泛的集成。 因此,LSP 方式不应被视为现有语言 API 的替代方案,而应被视为附加值。

从 2023.2 发布周期开始,LSP API 作为 IntelliJ 平台的一部分在以下 IDE 中公开提供:

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

下面几部分将解释如何构建与语言服务器的简单集成,发现可用的功能和文档,以及了解基于 IntelliJ 的 IDE 中 LSP 支持的当前状态。

插件配置

为了在基于 Gradle 构建系统的第三方插件中充分利用语言服务器协议 API,我们建议将 Gradle IntelliJ Plugin 升级到 1.15.0 或更高版本。 此插件会将 LSP API 源和代码文档附加到项目中。

LSP 在基于 IntelliJ 的 IDE 的 2023.2 EAP7 中可用,因此插件必须以 IntelliJ IDEA Ultimate 232-EAP-SNAPSHOT 或更高版本为目标,或者在全面推出后以 v2023.2 为目标。

示例 build.gradle.kts 配置:

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


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

对于基于 IntelliJ Platform Plugin Template 的项目,将 Gradle IntelliJ Plugin 更新到最新版本,并按如下所示修改 gradle.properties 文件:

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

plugin.xml 配置文件需要指定对 IntelliJ IDEA Ultimate 模块的依赖:

   


   com.intellij.modules.platform
   com.intellij.modules.ultimate

通过以上更改,我们可以访问 LSP API 及其源代码和文档。

LSP API 源捆绑在 IntelliJ IDEA Ultimate 中,可以在 [IDE]/lib/src/src_lsp-openapi.zip 归档中找到。 如果您使用的是 Gradle IntelliJ Plugin 1.15.0+,它们会自动解析和映射。

支持的功能

IntelliJ 平台内的初始 LSP 支持涵盖以下功能:

更多功能即将公布。

基本实现

最低限度的 LSP 集成必须实现 LspServerSupportProvider 以及服务描述符,并将其注册为 com.intellij.platform.lsp.serverSupportProvider 扩展点

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")
}

提供 LSP 服务器支持提供程序扩展点的实现后,将其注册到 plugin.xml 文件中,如下所示:

   


   com.intellij.modules.platform
   com.intellij.modules.ultimate


   
       
   

作为参考,请查看 Prisma ORM 开源插件实现:https://github.com/JetBrains/intellij-plugins/tree/master/prisma/src/org/intellij/prisma/ide/lsp

语言服务器集成

语言服务器是一个独立进程,用于分析源代码并向开发工具提供针对特定语言的功能。 在 IDE 中创建使用 LSP 的插件时,可以通过两种方式向最终用户提供语言服务器:

  • 语言服务器实现二进制文件捆绑为随插件交付的资源。
  • 为用户提供定义语言服务器二进制文件在其环境中的位置的功能。

Prisma ORM 插件采用第一种方式,分发 prisma-language-server.js 脚本并使用本地 Node.js 解释器来运行。

对于更复杂的情况,您可能需要带有专属设置实现的详细配置。

要实现最低限度但功能齐全的插件,请执行以下步骤:

  • LspServerSupportProvider.fileOpened() 方法中,启动相关 LSP 服务器描述符,它可以使用 LspServerDescriptor.isSupportedFile() 检查方法来确定给定文件是否受支持。
  • 说明如何通过实现 LspServerDescriptor.createCommandLine() 启动服务器。

自定义

  • 为了微调或禁用基于 LSP 的功能的实现,插件可以重写 LspServerDescriptor 类的相应属性。 请参阅属性文档,了解更多详细信息:
    • lspGoToDefinitionSupport
    • lspCompletionSupport
    • lspDiagnosticsSupport
    • lspCodeActionsSupport
    • lspCommandsSupport
  • 要处理来自 LSP 服务器的自定义(未记录)请求和通知,请重写 LspServerDescriptor.createLsp4jClient
  • 要将自定义(未记录)请求和通知发送到 LSP 服务器,请重写 LspServerDescriptor.lsp4jServerClass 并实现 LspClientNotification 和/或 LspRequest 类。

如需了解详情,请参见捆绑的 LSP API 源代码及其代码文档。

测试

用于测试的实用程序类尚未提取供公众使用,但我们将尽快提供合适的方法来测试基于 LSP 的第三方插件。

故障排除

所有 IDE 和 LSP 服务器通信日志均传递到 IDE 日志文件。

要将其包含以供预览,请将以下条目添加到 Help | Diagnostic Tools | Debug Log Settings…(帮助 | 诊断工具 | 调试日志设置…)配置对话框中:

#com.intellij.platform.lsp

有关详情,请参阅 IntelliJ 平台 SDK 文档中的 Logging 部分。

限制

结论

将语言服务器协议 (LSP) 集成到基于 IntelliJ 的 IDE 的插件中时,需要在简单快速的语言支持和具有 IDE 功能的复杂自定义语言支持插件之间做出权衡。

考虑基于 LSP 的方式时,务必按以下标准评估向最终用户提供的语言服务器:

  • 语言服务器的操作系统依赖。
  • 最新版本在线可用性。
  • 与版本间重大更改的兼容性。
  • 请求用户提供语言服务器二进制路径的可行性。

解决与语言服务器进程的集成问题后,查阅 IntelliJ SDK 文档仍然很重要。 这将全面概述与 IntelliJ 平台内 LSP 标准无缝集成的可用功能。

请注意,与语言服务器协议的集成作为基于 IntelliJ 的付费 IDE 的扩展而创建。 因此,使用语言服务器集成的插件可能无法在 JetBrains 产品的 Community 版本和 Google 的 Android Studio 中使用。

如果您遇到问题或需要帮助,请通过 JetBrains Platform Slack 工作区中的 #intellij-platform 频道联系我们或在 YouTrack 中提交问题。

本博文英文原作者:

Sue

Jakub Chrzanowski

Jakub Chrzanowski is a Developer Advocate and Product Manager at JetBrains Marketplace, specializing in the IntelliJ Platform. With a background in full-stack web development, he’s been actively involved in the tech industry since the early 2000s. Jakub is a former third-party plugin developer who still works on IntelliJ-based IDE plugins in his spare time. Connect with him on Twitter and GitHub for insights and expertise.

image description