語言伺服器通訊協定Language Server Protocol

什麼是語言伺服器通訊協定?What is the Language Server Protocol?

支援豐富的編輯功能像來源的程式碼自動完成選項或移至定義編輯器或 IDE 中的程式語言是傳統上非常困難又耗時。Supporting rich editing features like source code auto-completions or Go to Definition for a programming language in an editor or IDE is traditionally very challenging and time consuming. 通常需要撰寫的編輯器或 IDE 的程式設計語言中的網域模型 (掃描器、 剖析器、 型別檢查、 產生器等等)。Usually it requires writing a domain model (a scanner, a parser, a type checker, a builder and more) in the programming language of the editor or IDE. 例如,支援 C/c + + 在 Eclipse IDE 的 Eclipse CDT 外掛程式是以 Java 撰寫因為 Eclipse IDE 本身以 Java 撰寫。For example, the Eclipse CDT plugin, which provides support for C/C++ in the Eclipse IDE is written in Java since the Eclipse IDE itself is written in Java. 此方法之後,它表示在 C# 中實作 C/c + + 中的網域模型 TypeScript for Visual Studio Code,以及一個個別的網域模型,適用於 Visual Studio。Following this approach, it would mean implementing a C/C++ domain model in TypeScript for Visual Studio Code, and a separate domain model in C# for Visual Studio.

建立語言特定的網域模型也是一種開發工具,可以重複使用現有的特定語言程式庫的更加容易。Creating language-specific domain models are also a lot easier if a development tool can reuse existing language-specific libraries. 不過,這些程式庫通常會實作在程式語言本身 (比方說,好 C/c + + 網域模型以 C/c + + 實作)。However, these libraries are usually implemented in the programming language itself (for example, good C/C++ domain models are implemented in C/C++). C/c + + 程式庫整合以 TypeScript 撰寫編輯器技術上是可行的但不困難。Integrating a C/C++ library into an editor written in TypeScript is technically possible but hard to do.

語言伺服器Language servers

另一種方法是在自己的處理序中執行的程式庫,並使用處理序間通訊與它。Another approach is to run the library in its own process and use inter-process communication to talk to it. 來回傳送的訊息會形成一種通訊協定。The messages sent back and forth form a protocol. 語言伺服器通訊協定 (LSP) 是標準化的開發工具與語言伺服器處理序之間交換訊息的乘積。The language server protocol (LSP) is the product of standardizing the messages exchanged between a development tool and a language server process. 使用語言伺服器或一個惡魔啊,不是新或新奇的主意。Using language servers or demons is not a new or novel idea. 編輯器,例如 Vim 和 Emacs 投入不少這段時間,以提供語意的自動完成支援。Editors like Vim and Emacs have been doing this for some time to provide semantic auto-completion support. LSP 的目標是要簡化這類的整合,並提供實用的架構來公開各種不同的工具的語言功能。The goal of the LSP was to simplify these sorts of integrations and provide a useful framework for exposing language features to a variety of tools.

常見的通訊協定可讓程式設計到最少的複雜的開發工具的語言功能,重複使用現有的語言的領域模型實作的整合。Having a common protocol allows the integration of programming language features into a development tool with minimal fuss by reusing an existing implementation of the language's domain model. 語言伺服器後端可以在 PHP、 Python 或 Java 撰寫並 LSP 可讓它輕易地整合到各種不同的工具。A language server back-end could be written in PHP, Python, or Java and the LSP lets it be easily integrated into a variety of tools. 通訊協定可以在一般的層級的抽象,使工具可以提供豐富的語言服務,而不需要完全了解基礎網域模型的特定細節。The protocol works at a common level of abstraction so that a tool can offer rich language services without needing to fully understand the nuances specific to the underlying domain model.

如何在啟動 LSP 上運作How work on the LSP started

LSP 已發展一段時間,目前已在 3.0 版。The LSP has evolved over time and today it is at Version 3.0. 它啟動時的語言伺服器概念已收取 OmniSharp 適用於 C# 提供豐富的編輯功能。It started when the concept of a language server was picked up by OmniSharp to provide rich editing features for C#. 一開始,OmniSharp 搭配 JSON 承載中的 HTTP 通訊協定和已整合至數個編輯器包括Visual Studio CodeInitially, OmniSharp used the HTTP protocol with a JSON payload and has been integrated into several editors including Visual Studio Code.

大約在同一時間,Microsoft 會開始運作的 TypeScript 語言伺服器上,其支援 TypeScript Emacs 和 Sublime Text 等編輯器中的概念。Around the same time, Microsoft started to work on a TypeScript language server, with the idea of supporting TypeScript in editors like Emacs and Sublime Text. 在此實作中,編輯器會透過 stdin/stdout 與 TypeScript 伺服器處理序進行通訊,並使用要求和回應的 JSON 承載所 V8 偵錯工具通訊協定的。In this implementation, an editor communicates through stdin/stdout with the TypeScript server process and uses a JSON payload inspired by the V8 debugger protocol for requests and responses. TypeScript 伺服器已整合至 TypeScript Sublime 外掛程式,VS Code 的豐富的 TypeScript 編輯。The TypeScript server has been integrated into the TypeScript Sublime plugin and VS Code for rich TypeScript editing.

在之後需要整合兩個不同的語言伺服器,VS Code 小組已開始探索編輯器和 Ide 的常見語言伺服器通訊協定。After having integrated two different language servers, the VS Code team started to explore a common language server protocol for editors and IDEs. 常見的通訊協定可讓您建立可供不同的 Ide 的單一語言伺服器的語言提供者。A common protocol enables a language provider to create a single language server that can be consumed by different IDEs. Language server 取用者只會有一次實作的通訊協定的用戶端。A language server consumer only has to implement the client side of the protocol once. 這會導致雙贏的情況下的語言提供者和語言取用者。This results in a win-win situation for both the language provider and the language consumer.

語言伺服器通訊協定開始使用 TypeScript 伺服器中,展開與靈感來自於 VS Code 語言 API 的更多語言功能所使用的通訊協定。The language server protocol started with the protocol used by the TypeScript server, expanding it with more language features inspired by the VS Code language API. 通訊協定支援 JSON rpc 遠端引動過程,由於其簡易性和現有的程式庫。The protocol is backed with JSON-RPC for remote invocation due to its simplicity and existing libraries.

在 VS 程式碼的小組原型的通訊協定,藉由實作數個 linter 語言伺服器回應要求到 lint (掃描) 檔案,並傳回一組的偵測到的警告和錯誤。The VS Code team prototyped the protocol by implementing several linter language servers which respond to requests to lint (scan) a file and return a set of detected warnings and errors. 目標是要 lint 的檔案做為文件,這表示編輯器工作階段期間將會多 linting 要求中的使用者編輯。The goal was to lint a file as the user edits in a document, which means that there will be many linting requests during an editor session. 它不二人選讓註冊的伺服器和執行,因此不需要為每個使用者編輯啟動新的 lint 處理程序。It made sense to keep a server up and running so that a new linting process did not need to be started for each user edit. 已實作數個 linter 伺服器,包括 VS 程式碼的 ESLint 和 TSLint 延伸模組。Several linter servers were implemented, including VS Code's ESLint and TSLint extensions. 這兩部 linter 伺服器同時實作 TypeScript/javascript 撰寫並在 Node.js 上執行。These two linter servers are both implemented in TypeScript/JavaScript and run on Node.js. 它們會共用程式庫實作之通訊協定的用戶端和伺服器的一部分。They share a library that implements the client and server part of the protocol.

LSP 的運作方式How the LSP works

語言伺服器執行自己的處理序中,並透過 JSON RPC 使用的語言通訊協定與伺服器通訊的工具,例如 Visual Studio 或 VS Code。A language server runs in its own process, and tools like Visual Studio or VS Code communicate with the server using the language protocol over JSON-RPC. 語言伺服器作業系統專用的處理序中的另一個優點是可避免單一處理序模型相關的效能問題。Another advantage of the language server operating in a dedicated process is that performance issues related to a single process model are avoided. 實際的傳輸通道可以 stdio、 通訊端、 具名的管道或節點 ipc 如果用戶端和伺服器以 Node.js 撰寫。The actual transport channel can either be stdio, sockets, named pipes, or node ipc if both the client and server are written in Node.js.

以下的範例工具和語言伺服器通訊期間常式的方式編輯工作階段:Below is an example for how a tool and a language server communicate during a routine editing session:

lsp 流程圖

  • 使用者在工具中開啟檔案 (稱為文件): 此工具會告知語言伺服器已開啟的文件 (' textDocument/didOpen ')。The user opens a file (referred to as a document) in the tool: The tool notifies the language server that a document is open ('textDocument/didOpen'). 從現在起,文件內容的真相不再位於檔案系統,但保留在記憶體中的工具。From now on, the truth about the contents of the document is no longer on the file system but kept by the tool in memory.

  • 使用者可編輯: 此工具會通知伺服器與變更相關的文件 (' textDocument/didChange ') 和程式的語意資訊會由語言伺服器更新。The user makes edits: The tool notifies the server about the document change ('textDocument/didChange') and the semantic information of the program is updated by the language server. 發生這種情況,語言伺服器會分析這項資訊,並通知使用偵測到的錯誤和警告 (' textDocument/publishDiagnostics ') 的工具。As this happens, the language server analyzes this information and notifies the tool with the detected errors and warnings ('textDocument/publishDiagnostics').

  • 使用者在編輯器中的符號上執行 移至定義 」: 此工具會傳送 'textDocument/定義' 要求具有兩個參數: (1) 文件 URI 和 移至定義要求起始伺服器所在 (2) 的文字位置。The user executes "Go to Definition" on a symbol in the editor: The tool sends a 'textDocument/definition' request with two parameters: (1) the document URI and (2) the text position from where the Go to Definition request was initiated to the server. 伺服器會回應文件 URI 和內文件的符號定義的位置。The server responds with the document URI and the position of the symbol's definition inside the document.

  • 在使用者關閉文件 (檔案): 從工具,通知文件是現在不再記憶體,且目前的內容是現在最新的檔案系統上的語言伺服器會傳送 'textDocument/didClose' 通知。The user closes the document (file): A 'textDocument/didClose' notification is sent from the tool, informing the language server that the document is now no longer in memory and that the current contents is now up to date on the file system.

此範例說明通訊協定與層級的編輯器功能,例如 [移至定義],[尋找所有參考] 的語言伺服器通訊的方式。This example illustrates how the protocol communicates with the language server at the level of editor features like "Go to Definition", "Find all References". 通訊協定所使用的資料類型是編輯器或 IDE '資料類型' 目前開啟的文字文件等資料指標的位置。The data types used by the protocol are editor or IDE 'data types' like the currently open text document and the position of the cursor. 資料類型不是層級的程式設計語言領域模型通常提供抽象語法樹狀結構和編譯器符號 (例如,解析型別,命名空間,...)。這可大幅簡化的通訊協定。The data types are not at the level of a programming language domain model which would usually provide abstract syntax trees and compiler symbols (for example, resolved types, namespaces, ...). This simplifies the protocol significantly.

現在讓我們看看 'textDocument/定義' 要求在更多詳細資料。Now let's look at the 'textDocument/definition' request in more detail. 以下是用戶端工具和 c + + 文件中的 移至定義 」 要求的語言伺服器之間的裝載。Below are the payloads that go between the client tool and the language server for the "Go to Definition" request in a C++ document.

這是要求:This is the request:

{
    "jsonrpc": "2.0",
    "id" : 1,
    "method": "textDocument/definition",
    "params": {
        "textDocument": {
            "uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/use.cpp"
        },
        "position": {
            "line": 3,
            "character": 12
        }
    }
}

這是回應:This is the response:

{
    "jsonrpc": "2.0",
    "id": "1",
    "result": {
        "uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/provide.cpp",
        "range": {
            "start": {
                "line": 0,
                "character": 4
            },
            "end": {
                "line": 0,
                "character": 11
            }
        }
    }
}

回顧以往,描述在編輯器的層級,而非程式設計的語言模型的層級的資料類型是其中一個語言伺服器通訊協定成功的原因。In retrospect, describing the data types at the level of the editor rather than at the level of the programming language model is one of the reasons for the success of the language server protocol. 就簡單多了標準化文字文件 URI 或資料指標位置,相較於標準化不同的程式設計語言中的抽象語法樹狀結構和編譯器的符號。It is much simpler to standardize a text document URI or a cursor position compared with standardizing an abstract syntax tree and compiler symbols across different programming languages.

當使用者正在使用不同的語言時,VS 程式碼通常會啟動每一種程式設計語言的語言伺服器。When a user is working with different languages, VS Code typically starts a language server for each programming language. 下列範例會顯示發生於 Java 和 SASS 檔案使用者工作階段。The example below shows a session where the user works on Java and SASS files.

java 和 sass

功能Capabilities

並非所有語言伺服器可以都支援的通訊協定所定義的所有功能。Not every language server can support all features defined by the protocol. 因此,用戶端和伺服器就會宣告其支援的功能集,透過 [功能]。Therefore, the client and server announces their supported feature set through 'capabilities'. 例如,伺服器會宣告,它可以處理 textDocument/定義 '的要求,但它可能會處理' 工作區/symbol ' 要求。As an example, a server announces that it can handle the 'textDocument/definition' request, but it might not handle the 'workspace/symbol' request. 同樣地,用戶端可以宣布他們就能夠提供 ' 將儲存為' 通知之前儲存的文件,以便在伺服器可以計算要編輯的文件時自動格式化的文字編輯。Similarly, clients can announce that they are able to provide 'about to save' notifications before a document is saved, so that a server can compute textual edits to automatically format the edited document.

Language server 整合Integrating a language server

語言伺服器實際整合到特定的工具不由語言伺服器通訊協定定義,且保留為工具實作者。The actual integration of a language server into a particular tool is not defined by the language server protocol and is left to the tool implementors. 某些工具整合語言伺服器以一般方式有了延伸模組,可以啟動,並與任何一種語言伺服器通訊。Some tools integrate language servers generically by having an extension that can start and talk to any kind of language server. 其他項目,例如 VS Code 中,建立每個語言伺服器的自訂延伸模組,以便延伸模組是仍然能夠提供一些自訂的語言功能。Others, like VS Code, create a custom extension per language server, so that an extension is still able to provide some custom language features.

若要簡化語言伺服器和用戶端實作,有程式庫或 Sdk 用戶端和伺服器組件。To simplify the implementation of language servers and clients, there are libraries or SDKs for the client and server parts. 這些程式庫可供不同的語言。These libraries are provided for different languages. 比方說,沒有語言用戶端 npm 模組以便與 VS Code 延伸模組,而另一個語言伺服器的整合language server npm 模組撰寫與使用 Node.js 語言伺服器通訊。For example, there is a language client npm module to ease the integration of a language server into a VS Code extension and another language server npm module to write a language server using Node.js. 這是目前清單支援程式庫。This is the current list of support libraries.

使用 Visual Studio 中的語言伺服器通訊協定Using the Language Server Protocol in Visual Studio