How to add a custom LSP server to nvim-lspconfig

5 Jul 2024

Normally the nvim-lspconfig package has every language server I could possibly want, so I’ve found it pretty low maintenance. Currently I’m testing out the server for my own programming language, and I needed to add the LSP to my editor’s config. It was a surprisingly annoying task! I wanted to share the little snippet I used to get it configured.

Briefly I considered forking nvim-lspconfig and adding my LSP’s config to the lua/lsp/server_configurations/ folder, where all the official implementations live. I decided against because that seemed like an unnecessary hassle. Instead I searched out a way to do it from outside the plugin.

First I found a Reddit thread which linked to a header within the nvim-lspconfig readme. That header… does not appear in the readme. A bit of trawling through the Wayback Machine got me the snippet I wanted!

local lspconfig = require'lspconfig'
local configs = require'lspconfig/configs'
-- Check if it's already defined for when reloading this file.
if not lspconfig.foo_lsp then
  configs.foo_lsp = {
    default_config = {
      cmd = {'/home/ashkan/works/3rd/lua-language-server/run.sh'};
      filetypes = {'lua'};
      root_dir = function(fname)
        return lspconfig.util.find_git_ancestor(fname) or vim.loop.os_homedir()
      end;
      settings = {};
    };
  }
end
lspconfig.foo_lsp.setup{}

But that snippet didn’t do anything. Ugh!

After some reading through nvim-lspconfig’s source code, I took a wild guess: nvim-lspconfig internally imports the config file through require('lspconfig.configs'), but this snippet is using require'lspconfig/configs'. Could that possibly make a difference? The answer appears to be yes, because the following is what worked for me:

require('lspconfig.configs').my_custom_lsp = {
    default_config = {
        cmd = {'path/to/my/binary'},
        filetypes = {'filetype'};
        root_dir = function(fname)
            return lsp.util.find_git_ancestor(fname)
        end;
        settings = {};
    };
}

Now all I have to do is write the rest of the language server—how hard could that be?