After setting the preset you are allowed to override the icons shown in the gutter for diagnostics.
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.set_preferences({
sign_icons = {
error = 'E',
warn = 'W',
hint = 'H',
info = 'I'
}
})
Be careful though. If you are going to override a preset do it right after calling .preset()
.
If you have any questions you can stop by the discussions page.
To pass custom options to a server you have the functions .configure()
and setup_servers()
. You can install servers at startup with .ensure_installed()
. Finally, you can use .on_attach()
to define a callback that will be executed when a language server is attached to a buffer.
Here's an example config.
local lsp = require('lsp-zero')
-- use recommended settings
lsp.preset('recommended')
-- make sure these servers are installed
lsp.ensure_installed({
'html',
'cssls',
'angularls',
'tsserver'
})
-- share options between serveral servers
local lsp_opts = {
flags = {
debounce_text_changes = 150,
}
}
lsp.setup_servers({
'html',
'cssls',
opts = lsp_opts
})
-- configure an individual server
lsp.configure('tsserver', {
flags = {
debounce_text_changes = 150,
},
on_attach = function(client, bufnr)
print('hello tsserver')
end
})
-- the function below will be executed whenever
-- a language server is attached to a buffer
lsp.on_attach(function(client, bufnr)
local noremap = {buffer = bufnr, remap = false}
local bind = vim.keymap.set
bind('n', '<leader>r', '<cmd>lua vim.lsp.buf.rename()<cr>', noremap)
bind('n', 'Q', function() print('Hello') end, {buffer = bufnr, desc = 'Say hello'})
-- more code ...
end)
-- setup must be the last function
-- this one does all the things
lsp.setup()
The easiest way I can think of is using a global function. Somewhere in your config you declare a function with your keybindings.
function! LspAttached() abort
nnoremap <buffer> <leader>r <cmd>lua vim.lsp.buf.rename()<cr>
nnoremap <buffer> <leader>k <cmd>lua vim.lsp.buf.signature_help()<CR>
" and many more ...
endfunction
Next you call that function when the LSP server is attached to a buffer.
local lsp = require('lsp-zero')
lsp.preset('recommended')
-- Disable default keybindings (optional)
lsp.set_preferences({
set_lsp_keymaps = false
})
lsp.on_attach(function(client, bufnr)
vim.call('LspAttached')
end)
lsp.setup()
Yes, call the function .configure()
and set the option force_setup
to true
.
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.configure('dartls', {
force_setup = true,
on_attach = function()
print('hello dartls')
end,
})
lsp.setup()
Using setup_nvim_cmp
will allow you to override some options of nvim-cmp
. Here's a few useful things you can do.
You want to modify completion.completeopt
. For this to work write all the defaults and then add noselect
. Then make sure "preselect mode" is set to none
. Like this.
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.setup_nvim_cmp({
preselect = 'none',
completion = {
completeopt = 'menu,menuone,noinsert,noselect'
},
})
lsp.setup()
In theory, you should use preselect = require('cmp').PreselectMode.None
. But for now is the same as 'none'
.
Using the sources
option you can specify the priority of each source by changing the order. You could also include new ones. Check out nvim-cmp
's documentation to know what are the possibilities.
Here is an example that recreates the default configuration for sources.
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.setup_nvim_cmp({
sources = {
{name = 'path'},
{name = 'nvim_lsp', keyword_length = 3},
{name = 'buffer', keyword_length = 3},
{name = 'luasnip', keyword_length = 2},
}
})
-- still, setup must be the last function
lsp.setup()
This you do with the formatting
option. It is kind of a complex topic because it requires some knowledge about nvim-cmp
and lua. Again, you should check out nvim-cmp
docs.
Anyway, here is an example changing the names of the sources with some icons.
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.setup_nvim_cmp({
formatting = {
-- changing the order of fields so the icon is the first
fields = {'menu', 'abbr', 'kind'},
-- here is where the change happens
format = function(entry, item)
local menu_icon = {
nvim_lsp = 'λ',
luasnip = '⋗',
buffer = 'Ω',
path = '🖫',
nvim_lua = 'Π',
}
item.menu = menu_icon[entry.source.name]
return item
end,
},
})
lsp.setup()
We can change that too. There's the documentation
option. Is the same as nvim-cmp
's window.documentation
option. And these are the defaults.
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.setup_nvim_cmp({
documentation = {
max_height = 15,
max_width = 60,
border = 'rounded',
col_offset = 0,
side_padding = 1,
winhighlight = 'Normal:Normal,FloatBorder:Normal,CursorLine:Visual,Search:None',
zindex = 1001
}
})
lsp.setup()
You could also disable it if you set it to false
.
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.setup_nvim_cmp({
documentation = false
})
lsp.setup()
The option you want is mapping
. The trickiest. Here you are going to find yourself in an all or nothing situation, if you choose to use it then you are in charge of all mappings, all the defaults will disappear. But don't worry, you can access those defaults with the function lsp.defaults.cmp_mappings()
.
Here is an example that adds <C-Space>
to trigger completion and makes <C-e>
cancel the completion instead of toggling.
local lsp = require('lsp-zero')
lsp.preset('recommended')
local cmp = require('cmp')
local cmp_mappings = lsp.defaults.cmp_mappings({
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
})
lsp.setup_nvim_cmp({
mapping = cmp_mappings
})
lsp.setup()
Want to know how much fun you can have creating your own mappings? Check out the wiki section Under the hood and scroll down all the way where it says Autocompletion
.
You can use the preset that comes with nvim-cmp
.
local lsp = require('lsp-zero')
lsp.preset('recommended')
local cmp = require('cmp')
lsp.setup_nvim_cmp({
mapping = cmp.mapping.preset.insert({
['<C-Space>'] = cmp.mapping.complete(),
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
})
})
lsp.setup()
What about the navigating through snippets placeholder? That's not a part vim's default, I don't know what those should be. But here, I suggest these:
-- go to next placeholder in the snippet
['<C-g>'] = cmp.mapping(function(fallback)
if luasnip.jumpable(1) then
luasnip.jump(1)
else
fallback()
end
end, {'i', 's'}),
-- go to previous placeholder in the snippet
['<C-d>'] = cmp.mapping(function(fallback)
if luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, {'i', 's'}),
You can disable any default keymap by overriding the mapping
property in nvim-cmp
. Use lsp.defaults.cmp_mappings()
to expose the default keybindings then "delete" the one you want. Let's make an example with Tab
.
local lsp = require('lsp-zero')
lsp.preset('recommended')
local cmp_mapping = lsp.defaults.cmp_mappings()
-- "unmap" <Tab>
cmp_mapping['<Tab>'] = nil
lsp.setup_nvim_cmp({
mapping = cmp_mapping
})
lsp.setup()
You can extend the sources by overriding the sources
property. Use lsp.defaults.cmp_sources()
to expose the default sources and then insert the new source.
local lsp = require('lsp-zero')
lsp.preset('recommended')
local cmp_sources = lsp.defaults.cmp_sources()
table.insert(cmp_sources, {name = 'name-of-new-source'})
lsp.setup_nvim_cmp({
sources = cmp_sources
})
lsp.setup()
Not a fan of constant completion suggestions? Don't worry there is a way to invoke the completion only demand. If you set completion.autocomplete
to false
, the menu will only show up when you press tab
or ctrl + e
.
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.setup_nvim_cmp({
completion = {autocomplete = false}
})
lsp.setup()
Welp, that's interesting. Maybe this is a good time to setup nvim-cmp
yourself. If you are using the recommended
preset, change it to lsp-compe
and then use the function lsp.defaults.cmp_config()
to extend or change the default configuration table.
local lsp = require('lsp-zero')
lsp.preset('lsp-compe')
lsp.setup()
vim.opt.completeopt = {'menu', 'menuone', 'noselect'}
local cmp = require('cmp')
local cmp_config = lsp.defaults.cmp_config({
window = {
completion = cmp.config.window.bordered()
}
})
cmp.setup(cmp_config)
Finally, in case no one has told you this today... you should read nvim-cmp
's documentation. You are awesome. Have a nice day.
Reuse the Mason registered formatters and LSP server in null-ls, easiest way is the use of jay-babu/mason-null-ls.nvim
package.
local lsp = require('lsp-zero')
local null_ls = require('null-ls')
lsp.preset('recommended')
lsp.setup()
-- see documentation of null-null-ls for more configuration options!
local mason_nullls = require("mason-null-ls")
mason_nullls.setup({
automatic_installation = true,
automatic_setup = true,
})
mason_nullls.setup_handlers({})
Whenever you have the feeling the buffers gets formatted twice, this can happen because of the formatting capabilities of the LSP server and the installed formatter controlled by null-ls.
In this case you can disable the LSP server formatting capabilities:
lsp.configure("tsserver", {
on_init = function(client)
client.server_capabilities.documentFormattingProvider = false
client.server_capabilities.documentFormattingRangeProvider = false
end
})
or if you have a custom lsp.on_attach
:
lsp.on_attach(function(client, bufnr)
-- Disable LSP server formatting, to prevent formatting twice.
-- Once by the LSP server, second time by NULL-ls.
if client.name == "volar" or client.name == "tsserver" then
client.server_capabilities.documentFormattingProvider = false
client.server_capabilities.documentFormattingRangeProvider = false
end
-- your other configuration here
end)
When prefered to have a standalone instance of null-ls that doesn't uses the Mason installed formatters and LSP servers:
local lsp = require('lsp-zero')
local null_ls = require('null-ls')
lsp.preset('recommended')
lsp.setup()
null_ls.setup({
-- any other configuration
sources = {
--- do whatever you need to do
}
})
Make sure the
build_options
is afterlsp.setup()
. see #60