Skip to main content

nvim-cmp and Completion

nvim-cmp is the most widely used Neovim completion engine. It aggregates completion sources (LSP, buffer words, file paths, snippets) and displays them in a popup menu.

Core Idea

nvim-cmp is a framework — it shows completions from multiple sources. Each source (LSP, buffer, path, luasnip) is a separate plugin registered with nvim-cmp.

Required Plugins​

lua/plugins/completion.lua
return {
{
"hrsh7th/nvim-cmp",
event = "InsertEnter",
dependencies = {
-- Sources
"hrsh7th/cmp-nvim-lsp", -- LSP completions
"hrsh7th/cmp-buffer", -- Buffer words
"hrsh7th/cmp-path", -- File paths
"hrsh7th/cmp-cmdline", -- Command-line completion
"saadparwaiz1/cmp_luasnip", -- Snippet completions

-- Snippets engine
{
"L3MON4D3/LuaSnip",
version = "v2.*",
build = "make install_jsregexp",
dependencies = {
"rafamadriz/friendly-snippets", -- pre-made snippets
},
config = function()
require("luasnip.loaders.from_vscode").lazy_load()
end,
},

-- Icons in completion menu
"onsails/lspkind.nvim",
},
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
local lspkind = require("lspkind")

cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},

-- Key mappings
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 = false }),
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { "i", "s" }),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { "i", "s" }),
}),

-- Sources (in priority order)
sources = cmp.config.sources({
{ name = "nvim_lsp", priority = 1000 },
{ name = "luasnip", priority = 750 },
{ name = "path", priority = 500 },
}, {
{ name = "buffer", priority = 250, keyword_length = 3 },
}),

-- Appearance
formatting = {
format = lspkind.cmp_format({
mode = "symbol_text",
maxwidth = 50,
ellipsis_char = "...",
symbol_map = {
Text = "󰉿",
Method = "󰆧",
Function = "󰊕",
Constructor = "",
Variable = "󰀫",
Class = "󰠱",
Interface = "",
Module = "",
Property = "󰜢",
Snippet = "",
Color = "󰏘",
File = "󰈙",
Folder = "󰉋",
},
}),
},

-- Window
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
},

experimental = {
ghost_text = true, -- show preview inline
},
})

-- Cmdline completions
cmp.setup.cmdline({ "/", "?" }, {
mapping = cmp.mapping.preset.cmdline(),
sources = { { name = "buffer" } },
})

cmp.setup.cmdline(":", {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = "path" },
{ name = "cmdline" },
}),
})
end,
},
}

Completion Key Bindings​

KeyAction
<C-Space>Force open completion menu
<Tab>Next item / expand snippet
<S-Tab>Previous item / jump back in snippet
<CR>Confirm selection
<C-e>Abort / close menu
<C-b> / <C-f>Scroll docs up/down

Completion Sources Reference​

Source PluginNameProvides
cmp-nvim-lspnvim_lspLanguage server completions
cmp-bufferbufferWords in open buffers
cmp-pathpathFile system paths
cmp-cmdlinecmdlineCmdline completion
cmp_luasnipluasnipSnippet completions
cmp-nvim-luanvim_luaNeovim Lua API
cmp-calccalcMath expressions

Writing Custom Snippets with LuaSnip​

local ls = require("luasnip")
local s = ls.snippet
local t = ls.text_node
local i = ls.insert_node
local f = ls.function_node

-- Define a custom PHP snippet
ls.add_snippets("php", {
s("func", {
t("function "),
i(1, "functionName"),
t("("),
i(2, "$param"),
t({ ") {", "\t" }),
i(0),
t({ "", "}" }),
}),
})

In LazyVim​

LazyVim Context

As of late 2024, LazyVim switched from nvim-cmp to blink.cmp as its default completion engine. blink.cmp is significantly faster as it is written in Rust. The concepts remain the same, but the configuration syntax is completely different.

AspectVanilla NeovimLazyVim
Enginenvim-cmpblink.cmp (default)
SnippetsLuaSnip or nativeNative snippets via blink.cmp
SourcesManual cmp-nvim-lsp, etc.Bundled in blink.cmp providers

What's Next​