adamu
(Neo)vim and language servers
There is a thread about vim already, but I wanted to start one specifically about the language server.
What configuration do people use for integrating elixir-ls in vim? Are the integrations worth using?
Specifically, the elixir-ls readme suggests three options:
I’ve already read Mitchell Hanberg (ALE), Hauleth (vim-lsp) and David Bernheisel (coc-elixir)'s blog posts - but they are from 2018, 2019 and 2020 respectively, and all recommend different implementations. I was wondering if there have been more developments in the last few years that may change the recommendations, and if there is any de-facto standard?
Personally I was thinking of trying vim-lsp, but thought I’d check for a consensus before I go down the rabbit hole.
Most Liked
TunkShif
I just made a guide on how to set up neovim for elixir development from scratch, hope that you will find it helpful!
wingyplus
If you’re using neovim, the neovim lsp is recommended since it’s built-in lsp integration for neovim since 0.5. It’s quite faster than the list above since it’s written in C + some Lua code (i believe). You just to install the neovim-lspconfig GitHub - neovim/nvim-lspconfig: Quickstart configurations for the Nvim LSP client to pre config lsp server that you want to use, elixir-ls already included.
But if you’re prefer vim-lsp, vim-lsp-settings GitHub - mattn/vim-lsp-settings: Auto configurations for Language Server for vim-lsp is recommended.
AstonJ
I started writing a quick start guide (as mentioned here) but ran into issues with lspconfig too, so ended up installing Spacevim, which works but feels slower than Spacemacs (so I uninstalled it). The reason I wanted to use Neovim is because I wanted something that felt really snappy!
So currently I am leaning towards Doom Emacs - it’s much easier to set-up, feels pretty solid, and Emacs actually has some really nice features (as mentioned in this DT thread). People (or at least the person in that vid) says that once you know the basics of Vim then it’s a good time to transition to Emacs in Evil mode (i.e Doom Emacs or Spacemacs).
I’d still be interested in trying to get a working ‘fast’ Neovim set up tho… perhaps we can start a thread in members-only to work on putting together a step-by-step guide?
adamu
After months of head-scratching and procrastinating over getting nvim-lspconfig set up property, I set up coc.nvim this morning in a couple of hours and it all works magically. Most of the defaults were what I wanted, and I only had to add about 5 lines of config. :h coc-nvim has all the documentation you need. It was also only a couple of commands to get language servers working with with json, typescript, etc. too. I wish I’d started with coc.nvim from the beginning!
Also: I tend to run two buffers side-by-side, and the way warnings are displayed with the built-in lsp support is that they are appended to the end of the line - often falling of the screen. coc.nvim’s implementation shows the warnings in a popup when you focus on them, which works better. The native version is probably configurable, but coc.vim’s defaults worked well for me.
krstfk
short abstract of my init.lua :
local use = require('packer').use
require('packer').startup(function()
use 'neovim/nvim-lspconfig' -- Collection of configurations for built-in LSP client
use 'hrsh7th/cmp-nvim-lsp'
use 'hrsh7th/cmp-buffer'
use 'hrsh7th/cmp-path'
use 'hrsh7th/cmp-cmdline'
use 'hrsh7th/nvim-cmp'
use 'hrsh7th/cmp-vsnip'
use 'hrsh7th/vim-vsnip'
use 'hrsh7th/cmp-nvim-lua'
use { 'nvim-treesitter/nvim-treesitter', run = { 'TSUpdate' } }
use 'nvim-treesitter/nvim-treesitter-textobjects'
end)
local nvim_lsp = require('lspconfig')
local on_attach = function(_client, bufnr)
vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
local opts = { noremap = true, silent = true }
-- vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gD', '<Cmd>lua vim.lsp.buf.declaration()<CR>', opts)
-- vim q
end
local cmp = require 'cmp'
cmp.setup({
snippet = {
-- REQUIRED - you must specify a snippet engine
expand = function(args)
vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users.
-- require('luasnip').lsp_expand(args.body) -- For `luasnip` users.
-- require('snippy').expand_snippet(args.body) -- For `snippy` users.
-- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users.
end,
},
window = {
completion = {
winhighlight = "Normal:Normal,FloatBorder:Pmenu,Search:None",
col_offset = -3,
side_padding = 0,
},
-- completion = cmp.config.window.bordered(),
-- documentation = cmp.config.window.bordered(),
},
formatting = {
fields = { "kind", "abbr", "menu" },
format = function(entry, vim_item)
if vim_item.kind == 'Color' and entry.completion_item.documentation then -- tailwind swatches
local _, _, r, g, b = string.find(entry.completion_item.documentation, '^rgb%((%d+), (%d+), (%d+)')
if r then
local color = string.format('%02x', r) .. string.format('%02x', g) .. string.format('%02x', b)
local group = 'Tw_' .. color
if vim.fn.hlID(group) < 1 then
vim.api.nvim_set_hl(0, group, { bg = '#' .. color })
-- vim.api.nvim_set_hl(0, CmpItemKindColor, {bg = '#' .. color})
end
local kind = require("lspkind").cmp_format({ mode = "symbol_text", maxwidth = 50 })(entry, vim_item)
local strings = vim.split(kind.kind, "%s", { trimempty = true })
kind.kind = " " .. (strings[1] or "") .. " "
kind.kind_hl_group = group
kind.menu = " (" .. (strings[2] or "") .. ")"
return kind
end
end
local kind = require("lspkind").cmp_format({ mode = "symbol_text", maxwidth = 50 })(entry, vim_item)
local strings = vim.split(kind.kind, "%s", { trimempty = true })
kind.kind = " " .. (strings[1] or "") .. " "
kind.menu = " (" .. (strings[2] or "") .. ")"
return kind
end,
},
mapping = cmp.mapping.preset.insert({
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
}),
sources = cmp.config.sources({
{ name = 'nvim_lsp' },
{ name = 'vsnip' }, -- For vsnip users.
{ name = 'nvim_lua' },
-- { name = 'luasnip' }, -- For luasnip users.
-- { name = 'ultisnips' }, -- For ultisnips users.
-- { name = 'snippy' }, -- For snippy users.
}, {
{ name = 'buffer' },
})
})
-- Set configuration for specific filetype.
--
cmp.setup.filetype('gitcommit', {
sources = cmp.config.sources({
{ name = 'cmp_git' }, -- You can specify the `cmp_git` source if you were installed it.
}, {
{ name = 'buffer' },
})
})
-- Use buffer source for `/` and `?` (if you enabled `native_menu`, this won't work anymore).
cmp.setup.cmdline({ '/', '?' }, {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = 'buffer' }
}
})
-- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).
cmp.setup.cmdline(':', {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = 'path' }
}, {
{ name = 'cmdline' }
})
})
-- Set up lspconfig.
local capabilities = require('cmp_nvim_lsp').default_capabilities()
-- Enable the following language servers
local servers = { 'clangd', 'rust_analyzer', 'pyright', 'tsserver', 'hls', 'ocamllsp' }
for _, lsp in ipairs(servers) do
nvim_lsp[lsp].setup { on_attach = on_attach, capabilities = capabilities }
end
nvim_lsp['elixirls'].setup {
-- Unix
cmd = { os.getenv("HOME") .. "/elixir-ls/language_server.sh" },
on_attach = on_attach,
capabilities = capabilities
}
nvim_lsp['lua_ls'].setup {
on_attach = on_attach,
capabilities = capabilities,
settings = {
Lua = {
runtime = {
-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
version = 'LuaJIT',
},
diagnostics = {
-- Get the language server to recognize the `vim` global
globals = { 'vim' },
},
-- Do not send telemetry data containing a randomized but unique identifier
telemetry = {
enable = false,
},
},
},
}
nvim_lsp.tailwindcss.setup({
capabilities = capabilities,
filetypes = { "html", "elixir", "eelixir", "heex" },
init_options = {
userLanguages = {
elixir = "html-eex",
eelixir = "html-eex",
heex = "html-eex",
},
},
settings = {
tailwindCSS = {
experimental = {
classRegex = {
'class[:]\\s*"([^"]*)"',
},
},
},
},
})
nvim_lsp.emmet_ls.setup({
-- on_attach = on_attach,
capabilities = capabilities,
filetypes = { 'html', 'typescriptreact', 'javascriptreact', 'css', 'sass', 'scss', 'less', 'elixir', 'eelixir', 'heex' },
init_options = {
userLanguages = {
elixir = "html-eex",
eelixir = "html-eex",
heex = "html-eex",
},
html = {
options = {
-- For possible options, see: https://github.com/emmetio/emmet/blob/master/src/config.ts#L79-L267
["bem.enabled"] = true,
},
},
}
})
require("lsp-colors").setup({
Error = "#db4b4b",
Warning = "#e0af68",
Information = "#0db9d7",
Hint = "#10B981"
})
require("trouble").setup()
require('nvim-lightbulb').update_lightbulb()
-- Map :Format to vim.lsp.buf.formatting()
vim.cmd([[ command! Format execute 'lua vim.lsp.buf.format({async = true})' ]])
-- Set completeopt to have a better completion experience
vim.o.completeopt = "menuone,noinsert"









