As you have seen in the Programmatic Language Features topic, it's possible to implement Language Features by directly using 9 API. Language Server Extension, however, provides an alternative way of implementing such language support. Show
This topic:
Why Language Server?Language Server is a special kind of Visual Studio Code extension that powers the editing experience for many programming languages. With Language Servers, you can implement autocomplete, error-checking (diagnostics), jump-to-definition, and many other language features supported in VS Code. However, while implementing support for language features in VS Code, we found three common problems: First, Language Servers are usually implemented in their native programming languages, and that presents a challenge in integrating them with VS Code, which has a Node.js runtime. Additionally, language features can be resource intensive. For example, to correctly validate a file, Language Server needs to parse a large amount of files, build up Abstract Syntax Trees for them and perform static program analysis. Those operations could incur significant CPU and memory usage and we need to ensure that VS Code's performance remains unaffected. Finally, integrating multiple language toolings with multiple code editors could involve significant effort. From language toolings' perspective, they need to adapt to code editors with different APIs. From code editors' perspective, they cannot expect any uniform API from language toolings. This makes implementing language support for 1 languages in 2 code editors the work of 3.To solve those problems, Microsoft specified Language Server Protocol, which standardizes the communication between language tooling and code editor. This way, Language Servers can be implemented in any language and run in their own process to avoid performance cost, as they communicate with the code editor through the Language Server Protocol. Furthermore, any LSP-compliant language toolings can integrate with multiple LSP-compliant code editors, and any LSP-compliant code editors can easily pick up multiple LSP-compliant language toolings. LSP is a win for both language tooling providers and code editor vendors! In this guide, we will:
Implementing a Language ServerOverviewIn VS Code, a language server has two parts:
As briefly stated above there are two benefits of running the Language Server in a separate process:
Here is an illustration of VS Code running two Language Server extensions. The HTML Language Client and PHP Language Client are normal VS Code extensions written in TypeScript. Each of them instantiates a corresponding Language Server and communicates with them through LSP. Although the PHP Language Server is written in PHP, it can still communicate with the PHP Language Client through LSP. This guide will teach you how to build a Language Client / Server using our Node SDK. The remaining document assumes that you are familiar with VS Code Extension API. LSP Sample - A simple Language Server for plain text filesLet's build a simple Language Server extension that implements autocomplete and diagnostics for plain text files. We will also cover the syncing of configurations between Client / Server. If you prefer to jump right into the code:
Clone the repository Microsoft/vscode-extension-samples and open the sample:
The above installs all dependencies and opens the lsp-sample workspace containing both the client and server code. Here is a rough overview of the structure of lsp-sample:
Explaining the 'Language Client'Let's first take a look at 4, which describes the capabilities of the Language Client. There are two interesting sections:First, look at the section:
This section contributes 5 settings to VS Code. The example will explain how these settings are sent over to the language server on startup and on every change of the settings.
The actual Language Client source code and the corresponding 1 are in the 2 folder. The interesting part in the 3 file is that it references the 4 extension host API through the 5 field and adds a dependency to the 6 library:
As mentioned, the client is implemented as a normal VS Code extension, and it has access to all VS Code namespace API. Below is the content of the corresponding extension.ts file, which is the entry of the lsp-sample extension:
Explaining the 'Language Server'
In the example, the server is also implemented in TypeScript and executed using Node.js. Since VS Code already ships with a Node.js runtime, there is no need to provide your own, unless you have specific requirements for the runtime. The source code for the Language Server is at 8. The interesting section in the server's 1 file is:
This pulls in the 0 libraries.Below is a server implementation that uses the provided text document manager that synchronizes text documents by always sending incremental deltas from VS Code to the server.
Adding a Simple ValidationTo add document validation to the server, we add a listener to the text document manager that gets called whenever the content of a text document changes. It is then up to the server to decide when the best time is to validate a document. In the example implementation, the server validates the plain text document and flags all occurrences of words that use ALL CAPS. The corresponding code snippet looks like this:
Diagnostics Tips and Tricks
To run the Language Server, do the following steps:
The Extension Development Host instance will then look like this: Debugging both Client and ServerDebugging the client code is as easy as debugging a normal extension. Set a breakpoint in the client code and debug the extension by pressing F5. Since the server is started by the 2 running in the extension (client), we need to attach a debugger to the running server. To do so, switch to the Run and Debug view and select the launch configuration Attach to Server and press F5. This will attach the debugger to the server.Logging Support for Language ServerIf you are using 6 to implement the client, you can specify a setting 4 that instructs the Client to log communications between Language Client / Server to a channel of the Language Client's 5.For lsp-sample, you can set this setting: 6. Now head to the channel "Language Server Example". You should see the logs:Using Configuration Settings in the ServerWhen writing the client part of the extension, we already defined a setting to control the maximum numbers of problems reported. We also wrote code on the server side to read these settings from the client: 0The only thing we need to do now is to listen to configuration changes on the server side and if a setting changes, revalidate the open text documents. To be able to reuse the validate logic of the document change event handling, we extract the code into a 7 function and modify the code to honor a 8 variable: 1The handling of the configuration change is done by adding a notification handler for configuration changes to the connection. The corresponding code looks like this: 2Starting the client again and changing the setting to maximum report 1 problem results in the following validation: Adding additional Language FeaturesThe first interesting feature a language server usually implements is validation of documents. In that sense, even a linter counts as a language server and in VS Code linters are usually implemented as language servers (see eslint and jshint for examples). But there is more to language servers. They can provide code completion, Find All References, or Go To Definition. The example code below adds code completion to the server. It proposes the two words 'TypeScript' and 'JavaScript'. 3The 9 fields are used to uniquely identify a completion item in the resolve handler. The data property is transparent for the protocol. Since the underlying message passing protocol is JSON-based, the data field should only hold data that is serializable to and from JSON.All that is missing is to tell VS Code that the server supports code completion requests. To do so, flag the corresponding capability in the initialize handler: 4The screenshot below shows the completed code running on a plain text file: Testing The Language ServerTo create a high-quality Language Server, we need to build a good test suite covering its functionalities. There are two common ways of testing Language Servers:
It is possible to do Unit Test in any testing framework of your choice. Here we describe how to do End-to-End testing for Language Server Extension. Open 1, and you can find a 2 test target: 5If you run this debug target, it will launch a VS Code instance with 3 as the active workspace. VS Code will then proceed to execute all tests in 4. As a debugging tip, you can set breakpoints in TypeScript files in 4 and they will be hit.Let's take a look at the 6 file: 6In this test, we:
Let's dive a bit deeper into the 8 function. It is defined in 9: 7In the activation part, we:
After the preparation, we can run the VS Code Commands corresponding to each language feature, and assert against the returned result. There is one more test that covers the diagnostics feature that we just implemented. Check it out at 2.Advanced TopicsSo far, this guide covered:
There are some more advanced topics we could not fit in to this guide. We will include links to these resources for further studying of Language Server development. Additional Language Server featuresThe following language features are currently supported in a language server along with code completions:
The Programmatic Language Features topic describes each of the language features above and provides guidance on how to implement them either through the language server protocol or by using the extensibility API directly from your extension. Incremental Text Document SynchronizationThe example uses the simple text document manager provided by the 0 module to synchronize documents between VS Code and the language server.This has two drawbacks:
The protocol therefore supports incremental document synchronization as well. To make use of incremental document synchronization, a server needs to install three notification handlers:
Below is a code snippet that illustrates how to hook these notification handlers on a connection and how to return the right capability on initialize: 8Using VS Code API directly to implement Language FeaturesWhile Language Servers have many benefits, they are not the only option for extending the editing capabilities of VS Code. In the cases when you want to add some simple language features for a type of document, consider using 4 as an option.Here is a 5 using 6 to add a few snippets as completions for plain text files.More samples illustrating the usage of VS Code API can be found at https://github.com/microsoft/vscode-extension-samples. Error Tolerant Parser for Language ServerMost of the time, the code in the editor is incomplete and syntactically incorrect, but developers would still expect autocomplete and other language features to work. Therefore, an error tolerant parser is necessary for a Language Server: The parser generates meaningful AST from partially complete code, and the Language Server provides language features based on the AST. When we were improving PHP support in VS Code, we realized the official PHP parser is not error tolerant and cannot be reused directly in the Language Server. Therefore, we worked on Microsoft/tolerant-php-parser and left detailed notes that might help Language Server authors who need to implement an error tolerant parser. Common questionsWhen I try to attach to the server, I get "cannot connect to runtime process (timeout after 5000 ms)"?You will see this timeout error if the server isn't running when you try to attach the debugger. The client starts the language server so make sure you have started the client in order to have a running server. You may also need to disable your client breakpoints if they are interfering with starting the server. |