nvim: Switch nvim-cmp for blink.cmp completion

Switched completion engines. Blink is supposedly faster and so on but
what I really love is the batteries-included style.
I don't have to set up the basic lsp, path, buffer and cmdline
completions, nor styling for basic kind displays and more.
Hundreds of lines of config shrink down to 60, very nice.
This commit is contained in:
Marty Oehme 2025-01-31 09:46:45 +01:00
parent 2618eb55b8
commit 3a10d7a849
Signed by: Marty
GPG key ID: EDBF2ED917B2EF6A
2 changed files with 73 additions and 296 deletions
nvim/.config/nvim/lua/plugins

View file

@ -1,287 +1,63 @@
local is_available = require("core.util").is_available
local completion_engine = {
{
"hrsh7th/nvim-cmp",
branch = "main",
version = false, -- new releases (>2022) are sadly not versioned
dependencies = {
-- TODO: Move to lsp
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-nvim-lsp-signature-help",
"hrsh7th/cmp-path",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-calc",
"hrsh7th/cmp-cmdline",
-- TODO: Move me into a separate load?
"cbarrete/completion-vcard",
"f3fora/cmp-spell",
"jc-doyle/cmp-pandoc-references",
-- TODO: Decide: get rid or just enable in very specific circumstances
"lukas-reineke/cmp-rg",
-- TODO: Move to treesitter
{ "ray-x/cmp-treesitter", dependencies = { "nvim-treesitter/nvim-treesitter" } },
},
opts = function()
local cmp = require("cmp")
-- style 'ghosttext' which appears behind cursor, showing current completion
vim.api.nvim_set_hl(0, "CmpGhostText", { link = "Comment", default = true })
local has_words_before = function()
---@diagnostic disable-next-line:deprecated
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0
and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
local kind_icons = {
Text = "",
Method = "",
Function = "󰊕",
Constructor = "",
Field = "",
Variable = "",
Class = "",
Interface = "",
Module = "",
Property = "",
Unit = "",
Value = "V",
Enum = "",
Keyword = "",
Snippet = "",
Color = "",
File = "",
Reference = "",
Folder = "",
EnumMember = "",
Constant = "",
Struct = "",
Event = "",
Operator = "",
TypeParameter = "",
}
-- `/` cmdline setup.
cmp.setup.cmdline("/", {
completion = { completeopt = "menu,menuone,noinsert,noselect" },
preselect = cmp.PreselectMode.None,
mapping = cmp.mapping.preset.cmdline(),
sources = { { name = "buffer" } },
})
-- `:` cmdline setup.
cmp.setup.cmdline(":", {
completion = { completeopt = "menu,menuone,noinsert,noselect" },
preselect = cmp.PreselectMode.None,
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({ { name = "path" } }, {
{ name = "cmdline", option = { ignore_cmds = { "Man", "!" } } },
}),
})
return {
window = { documentation = cmp.config.window.bordered() },
-- add noselect to not automatically select first item
completion = { completeopt = "menu,menuone,noinsert" },
preselect = cmp.PreselectMode.Item, -- not sure what this changes diff than above?
experimental = {
ghost_text = {
hl_group = "CmpGhostText",
},
},
sources = {
{ name = "nvim_lsp" },
{ name = "nvim_lsp_signature_help" },
{ name = "pandoc_references" },
{ name = "calc" },
{ name = "path" },
{ name = "buffer", keyword_length = 3 },
{ name = "spell", keyword_length = 3 },
-- { name = 'rg', keyword_length = 5 },
{ name = "vCard" },
},
mapping = cmp.mapping.preset.insert({
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<CR>"] = cmp.mapping({
i = function(fallback)
if cmp.visible() and cmp.get_active_entry() then
cmp.confirm({
behavior = cmp.ConfirmBehavior.Replace,
select = false,
})
else
fallback()
end
end,
s = cmp.mapping.confirm({ select = true }),
c = cmp.mapping.confirm({
behavior = cmp.ConfirmBehavior.Replace,
select = false,
}), -- disable selection in cmd mode
}),
["<Tab>"] = cmp.mapping(function(fallback)
-- expand_or_jumpable() will always jump
-- expand_or_locally_jumpable() only jumps when still inside snippet region
if cmp.visible() then
cmp.select_next_item()
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end, { "i", "s" }),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end, { "i", "s" }),
}),
formatting = {
expandable_indicator = true,
fields = { "kind", "abbr", "menu" },
format = function(entry, vim_item)
-- Kind icons, removing kind text leaving only icon
-- vim_item.kind = string.format('%s %s', kind_icons[vim_item.kind], vim_item.kind)
vim_item.kind = string.format("%s", kind_icons[vim_item.kind])
-- Source
vim_item.menu = ({
buffer = "",
calc = "󰃬",
digraphs = "",
latex_symbols = "𝓧",
luasnip = "",
nvim_lsp = "",
nvim_lua = "󰢱",
pandoc_references = "",
spell = "󰓆",
vCard = "󰛋",
})[entry.source.name]
return vim_item
end,
},
}
end,
event = { "InsertEnter", "CmdlineEnter" },
},
}
--
-- TODO: Enable more lazy loaded startup? And integrate into
-- cmp as insert source instead of in its setup config below.
local snippet_engine = {
"nvim-cmp",
dependencies = {
"L3MON4D3/LuaSnip",
"rafamadriz/friendly-snippets",
"saadparwaiz1/cmp_luasnip",
{
"benfowler/telescope-luasnip.nvim",
dependencies = { { "nvim-telescope/telescope.nvim", optional = true } },
config = function()
if is_available("telescope") then
require("telescope").load_extension("luasnip")
end
end,
},
{ "hrsh7th/nvim-cmp", optional = true },
},
event = { "InsertEnter" },
build = "make install_jsregexp",
opts = function(_, opts)
local cmp = require("cmp")
local luasnip = require("luasnip")
require("luasnip.loaders.from_vscode").lazy_load({ exclude = { "markdown", "quarto" } })
require("luasnip.loaders.from_snipmate").lazy_load()
opts.snippet = {
expand = function(item)
require("luasnip").lsp_expand(item.body)
end,
}
local has_words_before = function()
---@diagnostic disable-next-line:deprecated
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
table.insert(opts.sources, { name = "luasnip", keyword_length = 1 })
opts.mapping["<Tab>"] = cmp.mapping(function(fallback) -- expand_or_jumpable() will always jump
-- expand_or_locally_jumpable() only jumps when still inside snippet region
if luasnip.expand_or_locally_jumpable() then
luasnip.expand_or_jump()
elseif cmp.visible() then
cmp.select_next_item()
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end, { "i", "s" })
opts.mapping["<S-Tab>"] = cmp.mapping(function(fallback)
if luasnip.locally_jumpable(-1) then
luasnip.jump(-1)
elseif cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end, { "i", "s" })
end,
}
local beancount_cmp = {
"nvim-cmp",
dependencies = {
"crispgm/cmp-beancount",
},
ft = "beancount",
opts = function(_, opts)
-- FIXME: Find a way to dynamically derive environment
vim.g.python3_host_prog = os.getenv("HOME") .. "/.local/pipx/venvs/beancount/bin/python"
table.insert(opts.sources, {
name = "beancount",
})
end,
}
local latex_cmp = {
"nvim-cmp",
dependencies = {
-- TODO: Needs better lazy loading
"kdheepak/cmp-latex-symbols",
},
event = "CursorHold",
opts = function(_, opts)
table.insert(opts.sources, { name = "latex_symbols" })
end,
}
local markdown_cmp = {}
-- FIXME: Currently always set to true but should ideally
-- recognize if we have render-markdown available or not
if is_available("render-markdown") or true then
markdown_cmp = {
"nvim-cmp",
dependencies = {
{ "MeanderingProgrammer/render-markdown.nvim", main = "render-markdown", optional = true },
},
ft = "markdown",
opts = function(_, opts)
table.insert(opts.sources, {
name = "render-markdown",
})
end,
}
end
return {
completion_engine,
snippet_engine,
beancount_cmp,
latex_cmp,
markdown_cmp,
-- full documentation here: https://cmp.saghen.dev/
"saghen/blink.cmp",
dependencies = {
"saghen/blink.compat",
"rafamadriz/friendly-snippets",
"hrsh7th/cmp-calc",
"f3fora/cmp-spell",
{ "aspeddro/cmp-pandoc.nvim", opts = {
filetypes = { "pandoc", "markdown", "quarto", "rmd" },
} },
},
lazy = false,
opts = {
keymap = { preset = "default" },
appearance = {
-- Sets the fallback highlight groups to nvim-cmp's highlight groups
-- Useful for when your theme doesn't support blink.cmp
-- Will be removed in a future release
use_nvim_cmp_as_default = true,
-- Set to 'mono' for 'Nerd Font Mono' or 'normal' for 'Nerd Font'
-- Adjusts spacing to ensure icons are aligned
nerd_font_variant = "mono",
},
completion = {
documentation = { auto_show = true, auto_show_delay_ms = 500 },
-- ghost_text = { enabled = true },
},
signature = {
enabled = true,
},
-- Default list of enabled providers defined so that you can extend it
-- elsewhere in your config, without redefining it, due to `opts_extend`
sources = {
default = {
"pandoc",
"calc",
"lsp",
"path",
"snippets",
"buffer",
"spell",
},
providers = {
calc = {
name = "calc",
module = "blink.compat.source",
},
spell = {
name = "spell",
module = "blink.compat.source",
},
pandoc = {
name = "cmp_pandoc",
module = "blink.compat.source",
},
},
},
},
opts_extend = { "sources.default" },
}

View file

@ -12,17 +12,18 @@ return {
},
dependencies = {
{ "Bilal2453/luvit-meta" }, -- optional `vim.uv` typings
{ -- optional completion source for require statements and module annotations
"hrsh7th/nvim-cmp",
opts = function(_, opts)
opts.sources = opts.sources or {}
table.insert(opts.sources, {
name = "lazydev",
group_index = 0, -- set group index to 0 to skip loading LuaLS completions
})
end,
optional = true,
},
-- FIXME: Set up to work with blink.cmp
-- { -- optional completion source for require statements and module annotations
-- "hrsh7th/nvim-cmp",
-- opts = function(_, opts)
-- opts.sources = opts.sources or {}
-- table.insert(opts.sources, {
-- name = "lazydev",
-- group_index = 0, -- set group index to 0 to skip loading LuaLS completions
-- })
-- end,
-- optional = true,
-- },
},
},
}