Autocompletion
About nvim-cmp
The plugin responsable for autocompletion is nvim-cmp. nvim-cmp has a concept of "sources", these provide the actual data displayed in neovim. lsp-zero will configure the following sources if they are installed:
cmp-buffer: provides suggestions based on the current file.
cmp-path: gives completions based on the filesystem.
cmp_luasnip: it shows snippets in the suggestions.
cmp-nvim-lsp: show data send by the language server.
cmp-nvim-lua: provides completions based on neovim's lua api.
Default keybindings
Vim's defaults
<Ctrl-y>
: Confirms selection.<Ctrl-e>
: Toggles the completion. (Okay, in vim the default just cancels the completion. I set it to toggle).<Up>
: Navigate to previous item on the list.<Down>
: Navigate to the next item on the list.<Ctrl-p>
: Navigate to previous item on the list.<Ctrl-n>
: Navigate to the next item on the list.
Added mappings
<Enter>
: Confirms selection.<Ctrl-u>
: Scroll up in the item's documentation.<Ctrl-f>
: Scroll down in the item's documentation.<Ctrl-d>
: Go to the next placeholder in the snippet.<Ctrl-b>
: Go to the previous placeholder in the snippet.<Tab>
: Enables completion when the cursor is inside a word. If the completion menu is visible it will navigate to the next item in the list.<S-Tab>
: When the completion menu is visible navigate to the previous item in the list.
Override keybindings
The easiest way to modify the keybindings is using the mapping
option of .setup_nvim_cmp().
You can get more details about the mapping
option using the command :help cmp-mapping
.
Start from scratch
If you want to start with Neovim's default and then add your own, use nvim-cmp's preset. Like this:
local lsp = require('lsp-zero').preset({
name = 'minimal',
set_lsp_keymaps = true,
manage_nvim_cmp = true,
suggest_lsp_servers = false,
})
local cmp = require('cmp')
local cmp_action = require('lsp-zero').cmp_action()
lsp.setup_nvim_cmp({
mapping = cmp.mapping.preset.insert({
['<C-Space>'] = cmp.mapping.complete(),
['<C-u>'] = cmp.mapping.scroll_docs(-4),
['<C-d>'] = cmp.mapping.scroll_docs(4),
['<C-f>'] = cmp_action.luasnip_jump_forward(),
['<C-b>'] = cmp_action.luasnip_jump_backward(),
}),
})
lsp.setup()
Note: .cmp_action() has methods you can use as mappings in nvim-cmp.
Change default mapping
If you want to add/change a mapping in lsp-zero's default, do this.
local lsp = require('lsp-zero').preset({
name = 'minimal',
set_lsp_keymaps = true,
manage_nvim_cmp = true,
suggest_lsp_servers = false,
})
local cmp = require('cmp')
local cmp_action = require('lsp-zero').cmp_action()
lsp.setup_nvim_cmp({
mapping = lsp.defaults.cmp_mappings({
['<C-Space>'] = cmp_action.toggle_completion(),
['<C-e>'] = cmp.mapping.abort(),
}),
})
lsp.setup()
Disable a mapping
Just like before we are going to use lsp.defaults.cmp_mappings, but now to disable the mapping we use vim.NIL
.
Here is an example that disables tab
to autocomplete, and also disables Enter
to confirm.
local lsp = require('lsp-zero').preset({
name = 'minimal',
set_lsp_keymaps = true,
manage_nvim_cmp = true,
suggest_lsp_servers = false,
})
local cmp = require('cmp')
lsp.setup_nvim_cmp({
mapping = lsp.defaults.cmp_mappings({
['<C-Space>'] = cmp.mapping.complete(),
['<Tab>'] = vim.NIL,
['<S-Tab>'] = vim.NIL,
['<CR>'] = vim.NIL,
}),
})
lsp.setup()
Snippets
friendly-snippets is the plugin that provides the snippets. And luasnip is the "snippet engine", the thing that expands the snippet and allows you to navigate between snippet placeholders.
Both friendly-snippets and luasnip are optional. But nvim-cmp will give you a warning if you don't setup a snippet engine. If you don't use luasnip then configure a different snippet engine.
How to disable snippets?
If you already have it all setup then uninstall friendly-snippets and also cmp_luasnip.
Change to snippets with snipmate syntax
Uninstall friendly-snippets if you have it. Use onza/vim-snippets. Then add the luasnip loader somewhere in your config.
require('luasnip.loaders.from_snipmate').lazy_load()
Don't preselect first match
For those who want to use the Enter
key freely.
lsp.setup_nvim_cmp({
preselect = 'none',
completion = {
completeopt = 'menu,menuone,noinsert,noselect'
},
})
Configure a source
There is no good way to add/change/delete a source, there is an ugly way. What you need to do is use .setup_nvim_cmp() and paste the defaults right back in the sources
option.
lsp.setup_nvim_cmp({
sources = {
{name = 'path'},
{name = 'nvim_lsp'},
{name = 'buffer', keyword_length = 3},
{name = 'luasnip', keyword_length = 2},
},
})
Once you have this in your config you can manipulate them in any way you see fit.
Trigger completions menu manually
Set the completion.autocomplete
option to false.
lsp.setup_nvim_cmp({
completion = {autocomplete = false},
})
Add borders to completion menu
For this you can't use .setup_nvim_cmp(). You'll have to disable the setting manage_nvim_cmp
. After that use .defaults.cmp_config() to extend/change lsp-zero's default.
local lsp = require('lsp-zero').preset({
name = 'minimal',
set_lsp_keymaps = true,
manage_nvim_cmp = false,
})
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)
Why do I make you do this? I would like to get rid of .setup_nvim_cmp()
in the future, so any chance I can get to make you stop using it I'll take it. But why? Y'all seem to enjoy customizing nvim-cmp, so the purpose I had for .setup_nvim_cmp()
is now obsolete. Is okay because you can still use nvim-cmp directly and add lsp-zero's defaults if you want.