Протокол языкового сервераLanguage Server Protocol

Что такое протокол языкового сервера?What is the Language Server Protocol?

Поддержка расширенных возможностей редактирования, таких как автозавершение исходного кода или Переход к определению для языка программирования в редакторе или интегрированной среде разработки, обычно очень сложна и занимает много времени.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. Обычно требуется написать модель предметной области (средство проверки, средство синтаксического анализа, средство проверки типов, конструктор и т. д.) на языке программирования редактора или интегрированной среды разработки.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 КДТ, который обеспечивает поддержку C/C++ в интегрированной среде разработки Eclipse, написан на языке Java, так как сама интегрированная среда разработки Eclipse написана на 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++ в TypeScript для Visual Studioном коде и отдельной модели предметной области в 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++). Интеграция библиотеки 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. Протокол Language Server (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.

Начало работы с LSPHow 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 использовал протокол HTTP с полезной нагрузкой JSON и был интегрирован в несколько редакторов, включая Visual Studio Code.Initially, OmniSharp used the HTTP protocol with a JSON payload and has been integrated into several editors including Visual Studio Code.

В то же время корпорация Майкрософт приступила к работе на сервере языка TypeScript, с представлением о поддержке TypeScript в таких редакторах, как Emacs и подтравяной текст.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 и 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. Потребитель серверного языка должен только один раз реализовать клиентскую сторону протокола.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 затронула протокол, реализуя несколько языковых серверов средство поиска ошибок, которые отвечают на запросы к 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. Имеет смысл поддерживать работу сервера, чтобы новый процесс linting не приходился для каждого пользователя.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. Были реализованы несколько серверов средство поиска ошибок, включая расширения ESLint и TSLint для VS Code.Several linter servers were implemented, including VS Code's ESLint and TSLint extensions. Эти два сервера средство поиска ошибок реализуются в 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.

Принцип работы LSPHow the LSP works

Языковой сервер работает в собственном процессе, а такие средства, как Visual Studio или VS Code, взаимодействуют с сервером, используя языковой протокол через JSON-RPC.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/дидопен").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/дидчанже"), а семантические сведения о программе обновляются сервером языка.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/Публишдиагностикс").As this happens, the language server analyzes this information and notifies the tool with the detected errors and warnings ('textDocument/publishDiagnostics').

  • Пользователь выполняет команду "переход к определению" на символ в редакторе: инструмент отправляет запрос "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/дидклосе", информирующее языковой сервер о том, что документ больше не находится в памяти и что текущее содержимое теперь находится в актуальном состоянии в файловой системе.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
            }
        }
    }
}

В ретроспективе описание типов данных на уровне редактора, а не на уровне модели языка программирования, является одной из причин успеха протокола языкового сервера.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 и Sasser.The example below shows a session where the user works on Java and SASS files.

Java и Sasser

Возможности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/Definition", но он может не работать с запросом "Рабочая область или символ".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.

Интеграция языкового сервера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 и другой модуль NPM Language Server для написания языкового сервера с помощью 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 StudioUsing the Language Server Protocol in Visual Studio