TITLE: Toggle nvim-cmp keybinding DATE: 2025-07-27 AUTHOR: John L. Godlee ==================================================================== I use nvim-cmp in neovim for auto-completion. It's great for programming, but if I'm writing prose I only rarely want completion, for example if there's a difficult to spell species name that I want to repeat. I added to my nvim-cmp lua config a function and keybinding which toggles completion in any buffer. [nvim-cmp]: These are the relevant bits of the config. Full config at the end: This function goes in the config = function() part of the config, and defines the function to toggle completion in the local buffer, with a notification showing the status of the toggle. Below this is a keybinding () which runs the function in normal and visual modes. -- Define custom function to toggle cmp local function toggle_cmp_buffer() local buf_enabled = cmp.get_config().enabled cmp.setup.buffer({ enabled = not buf_enabled }) vim.notify("nvim-cmp " .. (not buf_enabled and "enabled" or "disabled") .. " in buffer", vim.log.levels.INFO) end vim.keymap.set({ "n", "v" }, "", toggle_cmp_buffer, { noremap = true, silent = true }) This next part goes in the cmp.setup({ ... mapping = { section alongside other cmp keybindings. It simply runs the function using the same keybinding as above, but in insert and select modes, since nvim-cmp is only active in these modes. -- Toggle cmp in insert and select modes [""] = cmp.mapping(function() toggle_cmp_buffer() end, { "i", "s" }), Full nvim-cmp config: -- Completion return { { "hrsh7th/nvim-cmp", event = { "InsertEnter", "CmdlineEnter" }, dependencies = { "hrsh7th/cmp-buffer", "hrsh7th/cmp-path", "hrsh7th/cmp-cmdline", { "L3MON4D3/LuaSnip", version = "v2.*", }, "saadparwaiz1/cmp_luasnip", "onsails/lspkind.nvim", "R-nvim/cmp-r", }, config = function() local cmp = require("cmp") local luasnip = require("luasnip") local lspkind = require("lspkind") -- Load SnipMate style snippets require("luasnip.loaders.from_snipmate").lazy_load() -- Define custom function to only expand if preceded local has_words_before = function() unpack = unpack or table.unpack 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 -- Define custom function to toggle cmp local function toggle_cmp_buffer() local buf_enabled = cmp.get_config().enabled cmp.setup.buffer({ enabled = not buf_enabled }) vim.notify("nvim-cmp " .. (not buf_enabled and "enabled" or "disabled") .. " in buffer", vim.log.levels.INFO) end vim.keymap.set({ "n", "v" }, "", toggle_cmp_buffer, { noremap = true, silent = true }) cmp.setup({ completion = { completeopt = "menu,menuone,preview,noselect", }, snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, mapping = { -- Toggle cmp in insert and select modes [""] = cmp.mapping(function() toggle_cmp_buffer() end, { "i", "s" }), -- will insert return unless item selected -- Insert snippet if selected [''] = cmp.mapping({ i = function(fallback) if cmp.visible() and cmp.get_active_entry() then if luasnip.expandable() then luasnip.expand() else cmp.confirm({ select = true, }) end else fallback() end end, s = cmp.mapping.confirm({ select = false }), c = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = false }), }), -- will select next item in list [""] = cmp.mapping(function(fallback) if cmp.visible() then cmp.select_next_item() elseif luasnip.locally_jumpable(1) then luasnip.jump(1) elseif has_words_before() then cmp.complete() if #cmp.get_entries() == 1 then cmp.confirm({ select = true }) end else fallback() end end, { "i", "s" }), -- will select previous item in list [""] = cmp.mapping(function(fallback) if cmp.visible() then cmp.select_prev_item() elseif luasnip.locally_jumpable(-1) then luasnip.jump(-1) else fallback() end end, { "i", "s" }), }, sources = cmp.config.sources({ { name = "luasnip" }, { name = "buffer" }, { name = "path" }, { name = "cmp_r" }, }), formatting = { format = function(entry, vim_item) if vim.tbl_contains({ 'path' }, entry.source.name) then local icon, hl_group = require('nvim-web-devicons').get_icon(entry:get_completion_item().la bel) if icon then vim_item.kind = icon vim_item.kind_hl_group = hl_group return vim_item end end return require('lspkind').cmp_format({ with_text = true })(entry, vim_item) end }, }) -- Use buffer source for `/` and `?` cmp.setup.cmdline({ '/', '?' }, { mapping = cmp.mapping.preset.cmdline(), sources = { { name = 'buffer' } } }) -- Use cmdline & path source for ':' cmp.setup.cmdline(':', { mapping = cmp.mapping.preset.cmdline(), sources = cmp.config.sources({ { name = 'path' } }, { { name = 'cmdline' } }), matching = { disallow_symbol_nonprefix_matching = false } }) end, }, }