Skip to main content

Build Your Own Language Server (LSP)

This challenge is to build your own Language Server, implementing the Language Server Protocol.

What is the Language Server Protocol?

Adding features like auto complete, go to definition, or documentation on hover for a programming language takes a lot of work. Traditionally this work was done by each tool developer, for each programming language their tool supported.

That’s a lot of duplicated work! Enter the idea of a Language Server and the Language Server Protocol.

The goal of the Language Server Protocol (LSP) is to provide a standardised mechanism for language servers and development tools to communicate. Then a single Language Server can be re-used in multiple development tools. The development tools can then in turn can support multiple languages with minimal effort.

LSP is a win for both language providers and tooling vendors because of the reduced effort. It’s a win for us as software engineers as we can use the same language server across tools and see more consistency in our tooling.

The Challenge - Building A Language Server That Implements The Language Server Protocol.

In this Coding Challenge you’re going to build your own Language Server, implementing the Language Server Protocol and performing some analysis of the document currently being edited.

For this Coding Challenge you’re going to add support for some Language Features. In LSP, Language Features provide the actual smarts in the language server protocol. They are usually executed on a [text document, position] tuple. The main language feature categories are:

  • code comprehension features like Hover or Goto Definition.
  • coding features like diagnostics, code complete or code actions.

To give you a feel for what is possible and how to do it in this Coding Challenge you’ll be implementing some basic diagnostics and code completion.

Step Zero

To get started, pick your programming language of choice and create a new project. Whilst language servers can support multiple communication channels, for this project I suggest you focus on stdio.

Once you have a project ready to start development you’ll also need some way to test your solution. I used this as a great excuse to install and learn neovim. Having installed noevim I opted to start with **kickstart.nvim.**

You can of course use a language server with other editors, see your environments documentation for how.

Step 1

In this step your goal is to create a language server that can startup, receive messages via stdio and respond to the initialise request.

To do so you’re going to need to create a command line program that start’s up, opens standard in and standard out, and is able to handle the serialisation and de-serialisation of JSON-RPC request, response and notification messages exchanged over stdio.

You should start by reading the overview of the LSP. Then the Basic JSON Structures in the LSP specification. After that read about the Server lifecycle, and implement enough of you LSP to handle the Initialize Request.

I strongly suggest you include logging in your language server so you can see what is happing, for example at this stage mine looks like:

[cclsp] CC LSP is running
[cclsp] Received method: initialize
[cclsp] Connected to: Neovim 0.11.4
[cclsp] Sent the reply
[cclsp] Received method: initialized

It’s not particular exciting, but you now have communication between the editor and the language server and are ready to start adding the more interesting features.

Step 2

In this step your goal is to handle the DidOpenTextDocument notification and the DidChangeTextDocument notification. Be sure to parse the messages and for now simply log the contents of the text document out. If you haven’t already you might need to add the relevant capabilities for text document synchronisation in the ServerCapabilities section of the response to the Initialize Request.

That might look something like this:

[cclsp] CC LSP is running
[cclsp] Received method: initialize
[cclsp] Connected to: Neovim 0.11.4
[cclsp] Sent the reply
[cclsp] Received method: initialized
[cclsp] Received method: textDocument/didOpen
[cclsp] Opened: file:///Users/john/cc/test.txt
This is a test document

Step 3

In this step your goal is to evaluate the document that has been opened or changed and provide some diagnostic information about it. As an example, for this I decided to evaluate text files and advocate for my belief that it’s better to learn by doing. To do that I looked for the phrases:

  • “Watching a Video” and where it was found, provided the feedback: "It's much better to learn by doing! Try Coding Challenges instead.”
  • “Coding Challenges” and where it was found, provided the feedback: "This is the way!”

Viewed in neovim that looks like this:

lsp1.png

lsp2.png

Step 4

In this step your goal is to support Completion Requests. These are the requests that support code completion in your editor. In my solution I added support for the Completion Item “CodingChallenges” which looks like this in neovim:

lsp3.png

You’ve now built the framework needed to implement a full language server, though there is obviously much more that is possible, just take a read through the rest of the specification!

Going Further

To take this project further you can work through the full specification adding support for all the notifications we didn’t look at.

Alternately, or as well, you can build a tool to analyse the document(s) for a programming language of your choice. If you don’t know where to start there, perhaps check out the build your own JSON parser coding challenge and then use that parser to build a language server for JSON.

Help Others by Sharing Your Solutions!

If you think your solution is an example other developers can learn from please share it, put it on GitHub, GitLab or elsewhere. Then let me know - ping me a message on the Discord Server, via Twitter or LinkedIn or just post about it there and tag me. Alternately please add a link to it in the Coding Challenges Shared Solutions Github repo.

Get The Challenges By Email

If you would like to receive the coding challenges by email, you can subscribe to the weekly newsletter on SubStack here: