言語サーバー プロトコル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. たとえば、eclipse ide 自体は Java で記述されているため、eclipse IDE での C/c + + のサポートを提供する Eclipse CDT プラグインは 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. このアプローチに従うと、TypeScript for Visual Studio コードで C/c + + ドメインモデルを実装し、C# では 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++). TypeScript で記述されたエディターに C/c + + ライブラリを統合することは、技術的には可能ですが、実行するのは困難です。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 Codeを含む複数のエディターに統合されています。Initially, OmniSharp used the HTTP protocol with a JSON payload and has been integrated into several editors including Visual Studio Code.

同時に、Microsoft は TypeScript 言語サーバーでの作業を開始しました。 Emacs や Sublime テキストなどのエディターで TypeScript をサポートするという考え方があります。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. この実装では、エディターは TypeScript サーバープロセスと共に stdin/stdout を介して通信し、要求と応答に V8 debugger プロトコルによって実現される JSON ペイロードを使用します。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 プラグインに統合されており、豊富な TypeScript 編集用に VS Code ています。The TypeScript server has been integrated into the TypeScript Sublime plugin and VS Code for rich TypeScript editing.

2つの異なる言語サーバーを統合した後、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. 言語サーバーコンシューマーは、プロトコルのクライアント側のみを実装する必要があります。A language server consumer only has to implement the client side of the protocol once. その結果、言語プロバイダーと言語コンシューマーの両方について、win win の状況が発生します。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 Code チームは、ファイルをけば (スキャン) する要求に応答し、検出された警告とエラーのセットを返す複数のリンター言語サーバーを実装することによって、プロトコルのプロトタイプを宣言しました。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. この目的は、ユーザーがドキュメントを編集したときにファイルを糸くずさせることでした。つまり、エディターセッション中に多数の要求が行われることになります。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. いくつかのリンターサーバーが実装されました。これには VS Code の機能拡張機能や、機能があります。Several linter servers were implemented, including VS Code's ESLint and TSLint extensions. これら2つのリンターサーバーは両方とも 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

言語サーバーは独自のプロセスで実行され、Visual Studio や VS Code などのツールは、JSON を介して言語プロトコルを使用してサーバーと通信します。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. 専用のプロセスで動作する言語サーバーのもう1つの利点は、単一プロセスモデルに関連するパフォーマンスの問題を回避できることです。Another advantage of the language server operating in a dedicated process is that performance issues related to a single process model are avoided. クライアントとサーバーの両方が Node.js に記述されている場合、実際のトランスポートチャネルは、stdio、ソケット、名前付きパイプ、またはノード ipc のいずれかになります。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').

  • ユーザーは、エディター内のシンボルに対して [定義へ移動] を実行し ます。このツールは、2つのパラメーターを持つ ' textdocument/Definition ' 要求を送信します。 (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/definition" 要求を詳細に見てみましょう。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
            }
        }
    }
}

Retrospect では、プログラミング言語モデルのレベルではなく、エディターのレベルでデータ型を記述することは、言語サーバープロトコルが成功した理由の1つです。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 Code は通常、各プログラミング言語の言語サーバーを起動します。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. そのため、クライアントとサーバーは、サポートされている機能セットを ' capabilities ' でアナウンスします。Therefore, the client and server announces their supported feature set through 'capabilities'. 例として、サーバーは ' textDocument/definition ' 要求を処理できることを示していますが、' workspace/symbol ' 要求を処理できない可能性があります。As an example, a server announces that it can handle the 'textDocument/definition' request, but it might not handle the 'workspace/symbol' request. 同様に、クライアントは、ドキュメントが保存される前に "about to save" 通知を提供できることを知らせることができます。これにより、編集されたドキュメントをサーバーで自動的にフォーマットするためのテキスト編集を計算できます。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.

言語サーバーの統合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. たとえば、言語サーバーと VS Code 拡張機能との統合を容易にする 言語クライアント npm モジュール や、Node.js を使用して言語サーバーを作成するための別の 言語サーバー npm モジュールがあります。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