Neovim: nvim-lsp + elixir

For code highlighting I use treesitter.

1 Like

Thanks a lot. I was confused by that tutorial, because it showed Elixir code that was syntax highlighted and didn’t mention the fact that I have to install some extra plugin for that. :slight_smile:

Vim do not provide Elixir highlighting OOtB so you either need to use TreeSitter in NeoVim or use “classic” syntax definition from vim-elixir in Vim or NeoVim.


Hey everyone. Converting my configs from vim to neovim with Lua.

As far as elixir experience goes, I don’t get very accurate references, even though it’s in the list of references in elixir_sense lsp. Im for sure getting a better experience with the coc-nvim plugin, but I’ve decided to switch – the programmable lsp is pretty amazing and is the clear path forward. I plan on testing the lsp, coc, and nvim-lsp to see what the deal is and if I can help in any way.

For anyone who’s converted to nvim-lsp, maybe throw out what issues you’re having on the supported features (from the list of provided functionality).

A plug for a library that I didn’t write, but just committed some elixir queries to, is nvim-treesitter-textobjects, allowing you to make text-objects out of the supported ts-capture (hopefully it auto-merges tonight). I’ve made captures for parameters, comments, and a few more things.

Hey, I’ve been using neovim LSP for a while now too. Just recently discovered/decided to try using a dialyzer, but I failed to make it work just by enabling the correct flags. Has anyone had any success with that so far?

I’ve just decided to jump back into vim (nvim to be exact) after a few years of vscode. I’ve gone with NVChad as a base config as I didn’t want to have to start from scratch. Mostly it all seems fine but I’m not seeing any completions from the Ex LSP. According to my LspInfo it’s running, but I’m at a bit of a loss.

I installed it using Mason and configured it as so:

lspconfig.elixirls.setup {
  on_attach = on_attach,
  capabilities = capabilities,
  cmd = { "/Users/blah/.local/share/nvim/mason/packages/elixir-ls/" }

Any help appreciated!

The elixirls is not attached to the current buffer, so it’s not actually running. Try execute :LspStart elixirls to first check if the lsp server can be started correctly.


Thanks for that. That helped me trace the problen. I was using the wrong launch cmd, just for future reference. Should’ve been

1 Like

I’ve just ported my old Neovim vimscript config to Lua, and the elixir-ls config was really tripping me up. I finally realized that using the ~/ alias in my cmd path was tripping it up. Hope this helps anyone else who comes across this.

require('lspconfig').elixirls.setup {
    cmd = {"/Full/Path/to/elixir-ls/release/"},
    on_attach = on_attach

This is my LunarVim Elixir init section:

local root_pattern = require("lspconfig").util.root_pattern

require 'lspconfig'.elixirls.setup {
  filetypes = { "elixir", "eelixir", "heex", "surface" },
  root_dir = root_pattern("mix.exs", ".git") or vim.loop.os_homedir(),
  cmd = { "/Users/dimi/bin/elixir-ls/release/" },

I’m trying to get the lsp server working with Neovim.
I installed it with Mason, added some configuration for lspconfig.
When I launch a .ex file and if I look to LspInfo, I have the elixirls launched.
But I only get the “hover” functionality, no “go to definition”, no “rename” etc.
How did you guys do?

It’s a fairly standard config, have you followed the tutorials? I never had a partially working ElixirLS and I am pretty noob at NeoVim.

I’d recommend using a distro like GitHub - LazyVim/LazyVim: Neovim config for the lazy .

My Neovim config is based around LazyVim. The awesome part is that nvim-treesitter, nvim-lspconfig and all the configuration to make LSP work is already in place.

I’ve added elixir support to LazyVim and it was just a matter of extending the configs a little bit:


ahahah thanks for saying I’m a noob :smiling_face_with_tear:, I’m definitely not an expert.
Ok so this is some of my lsp file with the keymaps and I’m registering lsp servers
(I’ve been doing a few tests that’s why there is a for loop then elixirls. etc)

    local servers = {
      -- "tailwindcss",

    local server_settings = {
      ["lua_ls"] = {
        Lua = {
          runtime = { version = "LuaJIT" },
          diagnostics = { globals = { "vim" } },
          workspace = {
            library = vim.api.nvim_get_runtime_file("", true),
      ["yamlls"] = {
        yaml = {
          keyOrdering = false,

    local server_with_disabled_formatting = {
      ["tsserver"] = true,
      ["lua_ls"] = true,
      ["tailwindcss"] = true,
      ["cssls"] = true,

    mason_lsp.setup({ ensure_installed = servers })

    local capabilities = vim.lsp.protocol.make_client_capabilities()
    capabilities = cmp_lsp.default_capabilities(capabilities)

    -- The biding for every servers
    local on_attach = function(client, bufnr)
      local bufopts = { noremap = true, silent = true, buffer = bufnr }

      bufopts.desc = "Go to definition"
      vim.keymap.set("n", "gd", vim.lsp.buf.definition, bufopts)

      bufopts.desc = "Go to type definition"
      vim.keymap.set("n", "gt", vim.lsp.buf.type_definition, bufopts)

      bufopts.desc = "Go to implementation"
      vim.keymap.set("n", "gi", vim.lsp.buf.implementation, bufopts)

      bufopts.desc = "Show diagnostics"
      vim.keymap.set("n", "gl", vim.diagnostic.open_float, bufopts)

      bufopts.desc = "Go to next diagnostics"
      vim.keymap.set("n", "]d", vim.diagnostic.goto_next, bufopts)

      bufopts.desc = "Go to prev diagnostics"
      vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, bufopts)

      bufopts.desc = "Telescope diagnostics"
      vim.keymap.set("n", "<leader>ld", ":Telescope diagnostics<CR>", bufopts)

      bufopts.desc = "Rename"
      vim.keymap.set("n", "<leader>lr", vim.lsp.buf.rename, bufopts)

      bufopts.desc = "Code action"
      vim.keymap.set("n", "<leader>lc", vim.lsp.buf.code_action, bufopts)

      if server_with_disabled_formatting[] then
        client.server_capabilities.documentFormattingProvider = false
        client.server_capabilities.documentRangeFormattingProvider = false

    for _, server in pairs(servers) do
        capabilities = capabilities,
        on_attach = on_attach,
        settings = server_settings[server],
      filetypes = { "elixir", "eelixir", "heex", "surface" },
      capabilities = capabilities,
      cmd = {
      on_attach = on_attach,
      root_dir = null_ls_utils.root_pattern("mix.exs", ".git")
        or vim.loop.os_homedir(),
      settings = {
        elixirLS = {
          fetchDeps = true,

I can’t upload image because I’m new but LspInfo gives me

 Language client log: /Users/pielov/.local/state/nvim/lsp.log
 Detected filetype:   elixir
 1 client(s) attached to this buffer: 
 Client: elixirls (id: 1, bufnr: [2])
 	filetypes:       elixir, eelixir, heex, surface
 	autostart:       true
 	root directory:  /Users/pielov/workspace/personal/elixir/elixirapp
 	cmd:             /Users/pielov/.local/share/nvim/mason/packages/elixir-ls/
 Configured servers list: elixirls, graphql, gopls, taplo, dockerls, jsonls, cssls, lua_ls, svelte, tsserver, html

I tried to download elixirls from github and cmd into it, with no luck
I tried elixirtools.nvim doesn’t work as well

I did not. :flushed: I said that I am a noob but had no trouble setting it up.

1 Like

Thanks for the info, I’ve been working on my config for a year or so, not sure I want to let it go. But I’ll give it a go after work and may be I’ll change my mind.

No worries I’m just messing with you

1 Like

I’m super impressed by what mister folke did with LazyVim it’s working perfectly indeed.
I’m just fighting against his keymaps unfortunately.

1 Like

I love the LazyVim setup vs anything else I’ve used and the keymaps are documented and easy to discover with which-key provided out of the box.

I use the nightly build of nvim because it’s proven to be more stable in practice, where with O/S provided nvim package and even stable I would still get nvim crashes.

I didn’t like the LazyVim default use of the ‘s’ keymap for search as I have too many decades of using s to delete the character under the cursor and enter insert mode.

But its is trivial to configure due to clean approach and documentation.


s is the unassuming key of choice for a bunch of plugins to override. They always say “just use cc.” Oddly I use s a lot and would always rather hit one key than two, thank-you-much! :sweat_smile: I’ve remapped cc to “delete line but leave it blank,” ie, a less aggressive dd, though that’s neither here nor there.

1 Like