DEV Community

Cover image for Integrating a Language Server (LSP) in a VS Code Extension
Symflower
Symflower

Posted on • Edited on • Originally published at symflower.com

Integrating a Language Server (LSP) in a VS Code Extension

If you’re working on developer assistance tooling such as auto-completion or error highlighting for a particular programming language, you probably want the functionality with more than one code editor. In the bad old days this used to be quite labor-intensive: developers had to come up with ways to share common logic across different editor platforms and technologies and write repetitive glue code for every integration. The Language Server Protocol (LSP) was invented to solve this problem.

LSP standardizes interactions between developer tooling (referred to as “servers” in the protocol) and text editors (“clients”) over a technology-neutral base layer. This completely decouples clients from servers meaning that you, as a tooling author, don’t have to write tons of glue code when targeting an editor that supports LSP. Plus, you can write your server in any language you like without having to worry about inter-process communication. LSP was first introduced in Visual Studio Code but it’s supported by a decent number of editors by now.

How does LSP work?

LSP runs over remote procedure calls using the JSON-RPC protocol (more specifically, version two of JSON-RPC). In JSON-RPC one client and one server exchange messages that either describe a request, response, or notification. Requests can be thought of as function calls with a given set of arguments that require a response. Responses either carry a return value or an error. Notifications are similar to requests, except that they don’t require a response from the other party, so they’re completely asynchronous. An example JSON-RPC request to add two numbers could look like this:

{ "jsonrpc": "2.0", "id": 1, "method": "add", "params": { "a": 2, "b": 3 } }

Followed by a response:

{ "jsonrpc": "2.0", "id": 1, "result": 5 }

Despite using the terms “client” and “server”, the protocol is entirely bidirectional. It requires a bidirectional communication channel to transmit messages, which could be anything from WebSockets to raw TCP connections, or Unix domain sockets. The LSP specification includes recommendations for handling communication channels. In practice, many language servers use standard input and output. The specification also describes the RPC layer in more detail.

On top of JSON-RPC, LSP defines a set of standard requests and notifications for servers to expose certain features to clients. The list of requests is quite long but the specification does a good job categorizing and explaining all of them. Here are some requests that we implement in the Symflower language server:

  • textDocument/didSave for triggering an analysis followed by unit test generation when a source file is saved in the editor.

  • workspace/didChangeWorkspaceFolders for keeping track of directories that should be included in future analyses.

  • textDocument/codeLens and workspace/executeCommand for letting users interactively add generated unit tests to their test files in our “test review” mode.

  • The codeLens request returns definitions for code lenses with commands to invoke when a user interacts with them in the client (usually by clicking).

  • executeCommand is called when a command is then invoked (i.e. when a user actually clicks on a code lens).

Starting a language server from a VS Code extension

Continue reading on Symflower's blog:
Integrating a language server (LSP) in a VS Code extension

Top comments (0)