2

I would like to set up the Elixir language server in Neovim using the built-in language server client and nvim-lspconfig.

Documentation for this seems to be spread out in multiple places:

  1. nvim-lspconfig README
  2. nvim-lspconfig wiki about autocomplete
  3. nvim-lspconfig elixir-ls server configuration documentation
  4. elixir-ls installation instructions

I am a little overwhelmed and have made multiple attempts to do this, but always give up without success. I also found a useful looking guide: How to Set Up Neovim for Elixir Development, but it makes quite a few assumptions, seems to eroneously do some configuration twice, and also switches config format halfway through, so wasn't a usable summary for me (after following the instructions, documentation popups were not working, and I was unable to scroll inside the autocomplete popups - I also had a lot of copy/pasted config I didn't understand).

So far I understand the required steps are:

  1. Install neovim
  2. Install elixir-ls manually (it doesn't seem to be possible currently to install via asdf due to a lack of ability to ask elixir-ls for its version)
  3. Install required neovim plugins: nvim-lspconfig + whatever is required for autocomplete
  4. Set up necessary config for nvim-lspconfig and autocomplete.

I have managed to do up to part-way through step 3, but have not sucesfully worked out the required dependencies and configuration for autocomplete.

What do I need to do to have a working elixir-ls setup in neovim, with autocomplete, using nvim-lspconfig and neovim's built-in language server client?

Adam Millerchip
  • 20,844
  • 5
  • 51
  • 74
  • 1
    Just as another reference, there is a detailed guide for setting up neovim and Elixir in the ElixirForum: https://elixirforum.com/t/neovim-elixir-setup-configuration-from-scratch-guide/46310 – Philip Sampaio Mar 04 '22 at 21:09

2 Answers2

5

I managed to get everything working with the combination of some plugins for completion in neovim. They are:

This is the same list of plugins from the nvim-cmp recommendation of neovim's Wiki.

I'm using packer to manage the plugins. I'm also installing the plugins before configuring them. Here is the installation lines:

use 'neovim/nvim-lspconfig'
use 'nvim-treesitter/completion-treesitter' -- Only if you are using TS
use 'hrsh7th/nvim-cmp' -- Autocompletion plugin
use 'hrsh7th/cmp-nvim-lsp' -- LSP source for nvim-cmp
use 'saadparwaiz1/cmp_luasnip' -- Snippets source for nvim-cmp
use 'L3MON4D3/LuaSnip' -- Snippets plugin

And then the configuration part:

-- Add additional capabilities supported by nvim-cmp
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)

-- Configure ElixirLS as the LSP server for Elixir.
require'lspconfig'.elixirls.setup{
  cmd = { "/home/my-user/path-to/elixir-ls/release/language_server.sh" },
  -- on_attach = custom_attach, -- this may be required for extended functionalities of the LSP
  capabilities = capabilities,
  flags = {
    debounce_text_changes = 150,
  },
  elixirLS = {
    dialyzerEnabled = false,
    fetchDeps = false,
  };
}

local luasnip = require 'luasnip'
-- nvim-cmp
local cmp = require 'cmp'

cmp.setup {
  snippet = {
    expand = function(args)
      require('luasnip').lsp_expand(args.body)
    end,
  },
  mapping = {
    ['<C-p>'] = cmp.mapping.select_prev_item(),
    ['<C-n>'] = cmp.mapping.select_next_item(),
    ['<C-d>'] = cmp.mapping.scroll_docs(-4),
    ['<C-f>'] = cmp.mapping.scroll_docs(4),
    ['<C-Space>'] = cmp.mapping.complete(),
    ['<C-e>'] = cmp.mapping.close(),
    ['<CR>'] = cmp.mapping.confirm {
      behavior = cmp.ConfirmBehavior.Replace,
      select = true,
    },
    ['<Tab>'] = function(fallback)
      if cmp.visible() then
        cmp.select_next_item()
      elseif luasnip.expand_or_jumpable() then
        luasnip.expand_or_jump()
      else
        fallback()
      end
    end,
    ['<S-Tab>'] = function(fallback)
      if cmp.visible() then
        cmp.select_prev_item()
      elseif luasnip.jumpable(-1) then
        luasnip.jump(-1)
      else
        fallback()
      end
    end,
  },
  sources = {
    { name = 'nvim_lsp' },
    { name = 'luasnip' },
  },
}

You can find all my neovim config in my dotfiles repo: https://github.com/philss/dotfiles

For reference, here is the nvim version I'm using (installed from source):

NVIM v0.7.0-dev+864-g2818de8b7
Build type: RelWithDebInfo
LuaJIT 2.1.0-beta3
Features: +acl +iconv +tui
Philip Sampaio
  • 672
  • 6
  • 9
  • Thanks Philip. The configuration is the killer for me. I eventually went with coc.nvim as it has a lot of defaults that suited me and only required ~5 lines of config (and no lua) to get it working how I liked. I think a wrapper on top of lspconfig with sensible defaults / simpler config might be the sweet spot. – Adam Millerchip May 20 '22 at 09:54
1

In addition to the accepted answer, I suggest installing language server via Homebrew or similar instead of cloning repo and compiling. Philip's answer is correct. Just make your life easier by using a package manager for language server installation.

If you use LunarVim, which is like a NeoVim IDE, the configuration is minimal.

Nearly all dependencies for completion are already installed.

I also found it is better for Mac users to use Homebrew to install Elixir Language Server like so:

brew install elixir-ls

You don't need to keep track of the updates manually. It is usually upgraded along with other dev dependencies via brew update && brew upgrade.

Try this for LunarVim:

-- generic LSP settings
--
-- ---@usage disable automatic installation of servers
-- lvim.lsp.automatic_servers_installation = false
local lspconfig = require("lspconfig")

-- Neovim doesn't support snippets out of the box, so we need to mutate the
-- capabilities we send to the language server to let them know we want snippets.
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true


-- ---configure a server manually. !!Requires `:LvimCacheReset` to take effect!!
-- ---see the full default list `:lua print(vim.inspect(lvim.lsp.automatic_configuration.skipped_servers))`
-- vim.list_extend(lvim.lsp.automatic_configuration.skipped_servers, { "pyright" })
-- local opts = {} -- check the lspconfig documentation for a list of all possible options
-- require("lvim.lsp.manager").setup("pyright", opts)
-- Elixir LS
lspconfig.elixirls.setup {
  cmd = { "/usr/local/Cellar/elixir-ls/0.10.0/libexec/language_server.sh" },
  -- on_attach = custom_attach, -- this may be required for extended functionalities of the LSP
  capabilities = capabilities,
  flags = {
    debounce_text_changes = 150,
  },
  elixirLS = {
    dialyzerEnabled = false,
    fetchDeps = false,
  };
}
kgpdeveloper
  • 2,099
  • 4
  • 25
  • 34