From 3a10d7a849dc9775a0efd289a944da22bede8425 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Fri, 31 Jan 2025 09:46:45 +0100 Subject: [PATCH] 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. --- nvim/.config/nvim/lua/plugins/completion.lua | 346 ++++--------------- nvim/.config/nvim/lua/plugins/languages.lua | 23 +- 2 files changed, 73 insertions(+), 296 deletions(-) diff --git a/nvim/.config/nvim/lua/plugins/completion.lua b/nvim/.config/nvim/lua/plugins/completion.lua index ba17644..60295f7 100644 --- a/nvim/.config/nvim/lua/plugins/completion.lua +++ b/nvim/.config/nvim/lua/plugins/completion.lua @@ -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({ - [""] = cmp.mapping.scroll_docs(-4), - [""] = cmp.mapping.scroll_docs(4), - [""] = 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 - }), - [""] = 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" }), - [""] = 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[""] = 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[""] = 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" }, } diff --git a/nvim/.config/nvim/lua/plugins/languages.lua b/nvim/.config/nvim/lua/plugins/languages.lua index c24cc38..26d42f8 100644 --- a/nvim/.config/nvim/lua/plugins/languages.lua +++ b/nvim/.config/nvim/lua/plugins/languages.lua @@ -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, + -- }, }, }, }