Auto detect language servers

Hello, neovim community.
I have a bit of a problem. I use my neovim configuration files on a wide range of devices, and as a result, I don’t have certain language servers installed on all my devices. I have this bit of code that starts the language servers automatically:

   local servers = { 'clangd', }
   for _, lsp in ipairs(servers) do
      nvim_lsp[lsp].setup {
         on_attach = on_attach,
         flags = {
            debounce_text_changes = 150,
         }
      }
   end

…but if the system I’m on doesn’t have clangd on it, I get an error upon starting neovim. Is there some way neovim could detect what language servers are installed on the device and only auto-start the installed ones?

Maybe you can check if the corresponding executable is present using executable() or exepath(), inside the loop.

However you’ll need to actually know the name of each executable. I’m not sure if the default config values for each language server are accessible through a public API. But if they are, you could programmatically extract them from the cmd config key.

Upon further testing, I realized that having a language server in the list of servers is not problematic as I once thought it was, and only causes an error upon loading a filetype that nvim-lspconfig tries to load a language server for. The error it throws is unobtrousive and works well as a reminder the language server isn’t installed. If I could load every configuration file in the nvim-lspconfig/lua/lspconfig/ directory, minus the non-lsp configuration files (I think util.lua and configs.lua are the only two), that would be an acceptable and feasible solution.

If I were doing it in bash, it would look something like this:

#!/bin/bash
# assume the working dir is nvim-lspconfig/lua/lspconfig
for file in *.lua; do
  # if the file is not one of the ignore files, source it
  if ! [[ "$file" =~ ^(util|configs)\.lua$ ]]; then
    source "$file"
  fi
done

How would this translate to lua? The part I need the most help with is getting the list of files and removing the ones I don’t care about.

Selectively disabling parts of a plugin is going to be a lot more difficult than selectively disabling parts of your own config.

I was suggesting something like this:

local lsp_servers = {
  clangd = 'clangd',
  pyls = '/path/to/pyls',
}

for lsp, exe in pairs(lsp_servers) do
  if vim.fn.executable(exe) then
    nvim_lsp[lsp].setup {
      on_attach = on_attach,
      flags = {
        debounce_text_changes = 150,
      }
    }
  end
end
1 Like

Ah, thank you. I only had to make one small adjustment, and that was to make the if statement check if executable()'s return value is equal to 1, so it looks like this:

if (vim.fn.executable(exe) == 1) then

Thank you for your help!