Cason Adams
Posted on February 16, 2021
I have been using coc for a few years now as my link to the various language servers out there. It is awesome! However, recently I have been tinkering with the built in LSP in neovim 0.5.0+. At the time of writing this, it is almost stable. I just wanted to share how I got things setup and working for me.
Requirements:
This brings in the tools that I use. I have included an upgrade.sh
file that auto installs the needed language servers (macOS or fedora). There are a few more deps needed if one wants to run this. Most packages managers will have these.
Once everything is setup a few shortcut keys defined below will be as follows:
keys | description |
---|---|
gd | goto definition |
gD | goto declaration |
gr | show references |
gi | goto implementation |
K | show doc |
SPACE rn | rename |
SPACE e | show line diagnostics |
[d | diagnostic previous |
]d | diagnostic next |
init.vim
-
~/.config/nvim/init.vim
"------------------------------------------------
" Plugins START
call plug#begin()
Plug 'neovim/nvim-lspconfig'
Plug 'nvim-lua/completion-nvim'
Plug 'nvim-lua/lsp-status.nvim'
Plug 'nvim-lua/diagnostic-nvim'
Plug 'airblade/vim-gitgutter'
Plug 'sbdchd/neoformat'
call plug#end()
" Plugins END
"------------------------------------------------
" Loads lua config
lua require('init')
" Use <Tab> and <S-Tab> to navigate through popup menu
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
"------------------------------------------------
" Settings START
filetype plugin on
set updatetime=300
set completeopt=menuone
set completeopt+=noinsert
set completeopt-=preview
set shortmess+=c
" Settings END
"------------------------------------------------
"------------------------------------------------
" Status Line START
set statusline=%<%f\ %h%m%r
set statusline+=%=%-10.60{LspStatus()}\ %-.(%l,%c%V%)\ %P
function! LspStatus() abort
if luaeval('#vim.lsp.buf_get_clients() > 0')
return luaeval("require('lsp-status').status()")
endif
return ''
endfunction
" Status Line END
"------------------------------------------------
init.lua
-
~/.config/nvim/lua/init.lua
local nvim_lsp = require "lspconfig"
local lsp_status = require("lsp-status")
-- function to attach completion when setting up lsp
local on_attach = function(client)
lsp_status.register_progress()
lsp_status.config(
{
status_symbol = "LSP ",
indicator_errors = "E",
indicator_warnings = "W",
indicator_info = "I",
indicator_hint = "H",
indicator_ok = "ok"
}
)
require "completion".on_attach(client)
local function buf_set_keymap(...)
vim.api.nvim_buf_set_keymap(bufnr, ...)
end
local function buf_set_option(...)
vim.api.nvim_buf_set_option(bufnr, ...)
end
buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc")
-- Mappings.
local opts = {noremap = true, silent = true}
buf_set_keymap("n", "gD", "<Cmd>lua vim.lsp.buf.declaration()<CR>", opts)
buf_set_keymap("n", "gd", "<Cmd>lua vim.lsp.buf.definition()<CR>", opts)
buf_set_keymap("n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>", opts)
buf_set_keymap("n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", opts)
buf_set_keymap("n", "K", "<Cmd>lua vim.lsp.buf.hover()<CR>", opts)
buf_set_keymap("n", "<space>rn", "<cmd>lua vim.lsp.buf.rename()<CR>", opts)
buf_set_keymap("n", "<space>e", "<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>", opts)
buf_set_keymap("n", "[d", "<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>", opts)
buf_set_keymap("n", "]d", "<cmd>lua vim.lsp.diagnostic.goto_next()<CR>", opts)
-- Set some keybinds conditional on server capabilities
if client.resolved_capabilities.document_formatting then
buf_set_keymap("n", "<space>f", "<cmd>lua vim.lsp.buf.formatting()<CR>", opts)
elseif client.resolved_capabilities.document_range_formatting then
buf_set_keymap("n", "<space>f", "<cmd>lua vim.lsp.buf.range_formatting()<CR>", opts)
end
-- Set autocommands conditional on server_capabilities
if client.resolved_capabilities.document_highlight then
vim.api.nvim_exec([[
augroup lsp_document_highlight
autocmd! * <buffer>
autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
" autocmd CursorHold *.* :lua vim.lsp.diagnostic.show_line_diagnostics()
autocmd BufWritePre * lua vim.lsp.buf.formatting_sync(nil, 300)
augroup END
]],
false
)
else
vim.api.nvim_exec([[
autocmd!
autocmd BufWritePre * Neoformat
augroup END
]], false)
end
end
-- Use a loop to conveniently both setup defined servers
-- and map buffer local keybindings when the language server attaches
local servers = {
"gopls",
"dockerls",
"tsserver",
"bashls",
"cmake",
"pyright",
"rust_analyzer",
"clangd"
}
for _, lsp in ipairs(servers) do
nvim_lsp[lsp].setup {
on_attach = on_attach,
capabilities = lsp_status.capabilities
}
end
-- Setup diagnostics formaters and linters for non LSP provided files
nvim_lsp.diagnosticls.setup {
on_attach = on_attach,
capabilities = lsp_status.capabilities,
cmd = {"diagnostic-languageserver", "--stdio"},
filetypes = {
"lua",
"sh",
"markdown",
"json",
"yaml",
"toml"
},
init_options = {
linters = {
shellcheck = {
command = "shellcheck",
debounce = 100,
args = {"--format", "json", "-"},
sourceName = "shellcheck",
parseJson = {
line = "line",
column = "column",
endLine = "endLine",
endColumn = "endColumn",
message = "${message} [${code}]",
security = "level"
},
securities = {
error = "error",
warning = "warning",
info = "info",
style = "hint"
}
},
markdownlint = {
command = "markdownlint",
isStderr = true,
debounce = 100,
args = {"--stdin"},
offsetLine = 0,
offsetColumn = 0,
sourceName = "markdownlint",
formatLines = 1,
formatPattern = {
"^.*?:\\s?(\\d+)(:(\\d+)?)?\\s(MD\\d{3}\\/[A-Za-z0-9-/]+)\\s(.*)$",
{
line = 1,
column = 3,
message = {4}
}
}
}
},
filetypes = {
sh = "shellcheck",
markdown = "markdownlint"
},
formatters = {
shfmt = {
command = "shfmt",
args = {"-i", "2", "-bn", "-ci", "-sr"}
},
prettier = {
command = "prettier",
args = {"--stdin-filepath", "%filepath"},
}
},
formatFiletypes = {
sh = "shfmt",
json = "prettier",
yaml = "prettier",
toml = "prettier",
markdown = "prettier",
lua = "prettier"
}
}
}
-- Enable diagnostics
vim.lsp.handlers["textDocument/publishDiagnostics"] =
vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics,
{
underline = true,
virtual_text = false,
signs = true,
update_in_insert = true
}
)
upgrade.sh
-
~/.config/nvim/upgrade.sh
#!/usr/bin/env bash
set -e
system_type=$(uname -s)
if [ "$system_type" = "Darwin" ]; then
brew install shellcheck
brew install shfmt
brew install llvm
fd 'clangd$' /usr/local/ --exec ln -s '{}' "$HOME"/.local/bin | :
curl -L https://github.com/rust-analyzer/rust-analyzer/releases/download/nightly/rust-analyzer-mac.gz | gunzip -f > "$HOME"/.local/bin/rust-analyzer && chmod +x "$HOME"/.local/bin/rust-analyzer
else
sudo dnf install -y \
ShellCheck \
clang-tools-extra \
;
curl -L https://github.com/rust-analyzer/rust-analyzer/releases/download/nightly/rust-analyzer-linux.gz | gunzip -f > "$HOME"/.local/bin/rust-analyzer && chmod +x "$HOME"/.local/bin/rust-analyzer
fi
sudo npm install -g -f \
pyright \
lua-fmt \
prettier \
prettier-plugin-toml \
markdownlint \
diagnostic-languageserver \
typescript typescript-language-server \
dockerfile-language-server-nodejs \
;
pip install --upgrade \
cmake-language-server \
;
GO111MODULE=on go get golang.org/x/tools/gopls@latest
💖 💪 🙅 🚩
Cason Adams
Posted on February 16, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.