Gerrit's Blog

04.12.2023

Cypher Language Server support for Neovim

Bring some Cypher-love to Neovim

A few weeks ago, my colleagues released the cypher-language-support. Besides a vs-code plugin it contains the cypher-language-server. This means that it is possible to have Cypher support within the IDE/editor of your choice if it supports the language server protocol. The question came up in my head: How hard can it be to get this into Neovim? Disclaimer: I am a regular user of (Neo)vim but not for the projects I am working on during the (work) day.

First baby steps

To figure out what was needed to get a out-of-the-box experience in the end, I started with the installation of the cypher-language-server.

npm i -g @neo4j-cypher/language-server

And an empty Neovim configuration to see if things(tm) will work.

vim.lsp.start({
  name = 'cypher-ls',
  cmd = { 'cypher-language-server', '--stdio' },
})

Opening a file containing Cypher and attaching the LSP server manually (where 1 is the buffer and the LSP server id)

:lua vim.lsp.buf_attach_client(1, 1);

successfully shows us that the detection works and, if we create invalid content, the errors/warnings appear.

Make it convenient to use

Of course I did not want my journey end in this PoC state. My current setup started off with nvim-lua/kickstart for getting nvim-lspconfig, mason and mason-lspconfig set up in context of language server support. This means that I already have the most used plugins in place to benefit from LSP support:

nvim-lspconfig

To get a working auto-configuration it was time to approach my first Neovim plugin contribution. Cloning nvim-lspconfig and making the clone the source of truth via

vim.opt.runtimepath:prepend("/<path-to-nvim-lspconfig.nvim>")

on top of my config lua, I was ready to create the needed changes.

The nvim-lspconfig plugin, as also the other ones I have seen, is really well structured. Adding the support for cypher-language-server was very straight forward. I just needed to create a new file with the name of the language server I plan to provide (cypher_ls) and added the following content.

local util = require 'lspconfig.util'

return {
  default_config = {
    cmd = { 'cypher-language-server', '--stdio' },
    filetypes = { 'cypher' },
    root_dir = util.find_git_ancestor,
    single_file_support = true,
  },
  docs = {
    description = [[
<desription content>
]],
  },
}

Et voila, with the following line I could activate "my" language server.

require('lspconfig').cypher_ls.setup{}

Now the time of truth came to figure out how the community and maintainers in Neovim(-lspconfig) land are willing to accept a random PR without previous issue or some kind of discussion upfront. To my surprise this was really not a problem at all, and after opening nvim-lspconfig#2883 just within 4 hours.

This encouraged me to go further down the Neovim plugin world, and for a really convenient experience, add support in mason and mason-lspconfig.

mason

The mason project helps to install various LSP servers, as well as linters and formatters. A potential future user of the cypher-language-server should not care about the installation process of the server but it should, if missing, get installed automatically. To make mason and its users aware of the existence of the cypher-language-server, the information has to be placed in the mason-registry (registry site). After investigating what other language server did, I was confident enough to raise a PR there mason-registry#3427. Since I did not want to fake the registry page in the mason module, it was now time to wait for me until the page got updated. Again after just a few days, the PR got merged with some changes, and the registry got an update.

At this point it was already possible to remove my manual installation of the cypher-language-server and just type

:MasonInstall cypher-language-server

to get the language server installed.

On checkmark more on my virtual list. Let's go to the next plugin.

mason-lspconfig

Mason provides the installation of the language server and nvim-lspconfig knows about how to use it. To combine those, I needed to add this bridging information to the mason-lspconfig plugin. As with the mason registry PR before, I looked for a already existing LSP server that is similar to the one I plan to add. To ensure everything works with my changes, I set up mason-lspconfig to be sourced from my local machine. Like before with nvim-lspconfig, I put this on top of my Neovim config.

vim.opt.runtimepath:prepend("/<path-to-mason-lspconfig.nvim>")

And then the moment of truth came, I uninstalled the language server once again and added cypher_ls to the mason LSP server list. Opening a cypher file, I could see that the installation process started and everything was working as expected.

Again after just a few hours, mason-lspconfig#312 got merged with some fixes and the cypher-language-server was available for every Neovim user.

File type support

Something I did not mention, but you have spotted in the nvim-lspconfig section already, is the file type the language server should work with: cypher. At the moment you have to either

au BufNewFile,BufRead *.cypher setf cypher

Of course it would be fantastic to get also the file type support into Neovim. After a little bit of research I figured out that the addition of file types are handled by merging changes from vim to Neovim.

And here it is: my first PR for the vim project to introduce cypher as a file type: vim#13516.

After this got merged, the Neovim project took the changes and added the file type support neovim_84688ec7.

If you're not building Neovim from the sources, you have to wait for the next release to also benefit from the file type support.

Conclusion

Taken that the heavy lifting of creating a LSP server was already done by my colleagues, it was a very nice experience to contribute to their efforts and the Neovim ecosystem. I did not know upfront what or whom to expect to have interaction with. But I was positive surprised about the fast merging and the interaction in general.

If you plan to add your LSP support for those projects, I can only encourage to do so.