Neovim: nvim-lsp + elixir

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/language_server.sh" },
}

Hello,
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:

2 Likes

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 = {
      "cssls",
      "dockerls",
      "elixirls",
      "gopls",
      "html",
      "jsonls",
      "lua_ls",
      "svelte",
      -- "tailwindcss",
      "taplo",
      "tsserver",
    }

    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.setup()
    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[client.name] then
        client.server_capabilities.documentFormattingProvider = false
        client.server_capabilities.documentRangeFormattingProvider = false
      end
    end

    for _, server in pairs(servers) do
      lsp[server].setup({
        capabilities = capabilities,
        on_attach = on_attach,
        settings = server_settings[server],
      })
    end
    lsp.elixirls.setup({
      filetypes = { "elixir", "eelixir", "heex", "surface" },
      capabilities = capabilities,
      cmd = {
        "/Users/pielov/.local/share/nvim/mason/packages/elixir-ls/language_server.sh",
      },
      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/language_server.sh
 
 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.

3 Likes

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

Oh? How?

I just do nnoremap cc S<esc>

So S does the “softer” dd?

S is delete the whole line and enter insert mode at the proper indent. I’m essentially saving one keystroke with cc, heh, but I still use it. I use S a whole lot and have been meaning to figure out a way to remap it so that it does a black hole delete instead of yanking as I often S on a blank line which messes with my paste registers.

1 Like

Ok LazyVim is clearly amazing, I had stuff broken in my config for ages, never been able to fix them and LazyVim works like a breeze.

I’m wondering though if you have closing tags in heex files ? I haven’t looked yet to treesitter’s config but at the moment it’s not closing and it’s not autocompleting (I think I have to trigger html-lsp or something).

I also noticed (I’m learning Elixir and still very new to this language), with the Phoenix framwork I was following the guide, Request life-cycle — Phoenix v1.7.10

We get to create a view file called hello_html.ex
When I started writing defmodule I got the autocomplete AppName.HelloHtml name but it should be AppName.HelloHTML, is there a config option or something ?
Or do I have to be careful each time I wanna create a view?

Lastly I noticed the completion menu is “aggressive”, opening with so many entries with very long description, how do you manage this menu?
Do you remove snippets? Do you truncate description?

1 Like