local has = require("core.plugin").has local feedkey = function(key, mode) vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) end return { "https://github.com/hrsh7th/nvim-cmp", event = { "InsertEnter", "CmdlineEnter" }, cond = not is_vscode(), dependencies = { { "tani/vim-artemis" }, { "onsails/lspkind.nvim" }, { "hrsh7th/cmp-buffer" }, { "https://codeberg.org/FelipeLema/cmp-async-path" }, { "hrsh7th/cmp-cmdline" }, { "hrsh7th/cmp-calc" }, { "hrsh7th/cmp-emoji" }, { "lukas-reineke/cmp-rg" }, { "lukas-reineke/cmp-under-comparator" }, { "f3fora/cmp-spell" }, { "petertriho/cmp-git", dependencies = { "nvim-lua/plenary.nvim" }, opts = { filetypes = { "gitcommit", "octo" }, }, config = true, }, { "hrsh7th/cmp-nvim-lsp" }, { "hrsh7th/cmp-nvim-lsp-document-symbol" }, { "hrsh7th/cmp-nvim-lsp-signature-help", enabled = false, }, { "ray-x/cmp-treesitter" }, }, init = function() require("core.plugin").on_load("nvim-cmp", function() vim.api.nvim_exec_autocmds("User", { pattern = "CmpLoaded" }) end) end, config = function() vim.o.completeopt = "menuone,noinsert,noselect" -- Setup dependencies local cmp = require("cmp") local luasnip_status, luasnip = pcall(require, "luasnip") local denippet = vimx.fn.denippet local has_words_before = function() if vim.api.nvim_get_option_value("buftype", { buf = 0 }) == "prompt" then return false end 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 snippet_jumpable = function(dir) if luasnip_status then return tb(luasnip.jumpable(dir)) elseif denippet ~= nil then return tb(denippet.jumpable(dir)) end return false end local snippet_jump = function(dir) if luasnip_status then luasnip.jump(dir) elseif denippet ~= nil then denippet.jump(dir) end end local setup_opt = { snippet = { -- REQUIRED - you must specify a snippet engine expand = function(args) if luasnip_status then luasnip.lsp_expand(args.body) elseif denippet ~= nil then if args.body ~= "" then denippet.anonymous(args.body) end end end, }, mapping = cmp.mapping.preset.insert({ [""] = cmp.mapping({ i = cmp.mapping.abort(), c = cmp.mapping.close(), }), [""] = cmp.config.disable, [""] = cmp.mapping(function(fallback) if denippet ~= nil and tb(denippet.choosable()) then feedkey("(denippet-choice-next)", "i") elseif cmp.visible() then cmp.select_next_item({ behavior = cmp.SelectBehavior.Select }) else fallback() end end), [""] = cmp.mapping(function(fallback) if denippet ~= nil and tb(denippet.choosable()) then feedkey("(denippet-choice-prev)", "i") elseif cmp.visible() then cmp.select_prev_item() else fallback() end end), [""] = cmp.mapping.scroll_docs(4), [""] = cmp.mapping.scroll_docs(-4), [""] = cmp.mapping(function(fallback) fallback() end, { "i", "s" }), [""] = cmp.mapping(function(fallback) local entry = cmp.get_selected_entry() if cmp.visible() and entry ~= nil then local confirm_option = { select = false, } cmp.confirm(confirm_option) else fallback() -- If you are using vim-endwise, this fallback function will be behaive as the vim-endwise. end end, { "i", "s" }), [""] = cmp.mapping(function(fallback) if cmp.visible() then cmp.select_next_item({ behavior = cmp.SelectBehavior.Select }) elseif snippet_jumpable(1) then snippet_jump(1) elseif has_words_before() then cmp.complete() else fallback() end end, { "i", "s" }), [""] = cmp.mapping(function(fallback) if cmp.visible() and has_words_before() then cmp.select_prev_item() elseif snippet_jumpable(-1) then snippet_jump(-1) else fallback() end end, { "i", "s" }), }), sorting = { comparators = { cmp.config.compare.offset, cmp.config.compare.exact, cmp.config.compare.score, -- cmp.config.compare.scopes, require("cmp-under-comparator").under, cmp.config.compare.kind, cmp.config.compare.recently_used, cmp.config.compare.locality, cmp.config.compare.sort_text, cmp.config.compare.length, cmp.config.compare.order, }, }, sources = cmp.config.sources({ -- { name = "rg" }, { name = "luasnip", priority = 20 }, { name = "denippet", priority = 20 }, { name = "nvim_lsp", priority = 100, trigger_characters = { "-", ".", "/", ":" } }, { name = "async_path", priority = 100 }, { name = "emoji", insert = true, priority = 50 }, { name = "nvim_lua", priority = 50 }, }, { { name = "buffer" }, { name = "omni" }, { name = "calc" }, { name = "spell" }, { name = "treesitter" }, { name = "look", keyword_length = 2, option = { convert_case = true, loud = true } }, -- { name = "cmp_tabnine" }, }), completion = { completeopt = "menu,menuone,noinsert,noselect", }, experimental = { ghost_text = false, -- this feature conflict to the copilot.vim's preview. }, } local menu = { nvim_lsp = "[LSP]", buffer = "[Buffer]", async_path = "[Path]", nvim_lua = "[Lua]", ultisnips = "[UltiSnips]", luasnip = "[LuaSnip]", treesitter = "[TS]", spell = "[Spell]", calc = "[Calc]", emoji = "[Emoji]", neorg = "[Neorg]", rg = "[rg]", omni = "[Omni]", -- cmp_tabnine = "[Tabnine]", cmdline_history = "[History]", look = "[Look]", git = "[Git]", ghq = "[GHQ]", -- cmp_openai_codex = "[Codex]", } setup_opt.formatting = { format = function(entry, item) local color_item = nil if has("nvim-highlight-colors") then color_item = require("nvim-highlight-colors").format(entry, { kind = item.kind }) end item = require("lspkind").cmp_format({ mode = "symbol_text", maxwidth = 50, before = function(_entry, vim_item) if entry.source.name == "nvim_lsp" then vim_item.menu = "{" .. _entry.source.source.client.name .. "}" else vim_item.menu = menu[_entry.source.name] or _entry.source.name end return vim_item end, })(entry, item) if color_item ~= nil and color_item.abbr_hl_group then item.kind_hl_group = color_item.abbr_hl_group item.kind = color_item.abbr end return item end, } cmp.setup(setup_opt) cmp.setup.filetype({ "gitcommit", "octo", "markdown" }, { sources = cmp.config.sources({ { name = "git" }, { name = "ghq" }, -- { name = "rg" }, { name = "luasnip" }, { name = "nvim_lsp" }, { name = "async_path" }, { name = "emoji", insert = true }, { name = "codecompanion" }, }, { { name = "buffer" }, { name = "omni" }, { name = "spell" }, { name = "calc" }, { name = "treesitter" }, { name = "look", keyword_length = 2, option = { convert_case = true, loud = true } }, }), }) cmp.setup.cmdline("/", { mapping = cmp.mapping.preset.cmdline(), sources = cmp.config.sources({ { name = "nvim_lsp_document_symbol" }, { name = "cmdline" }, { name = "ghq" }, }, { { name = "buffer" }, }), completion = { completeopt = "menu,menuone,noselect", }, }) cmp.setup.cmdline(":", { mapping = cmp.mapping.preset.cmdline(), sources = cmp.config.sources( { { name = "async_path" } }, { { name = "cmdline" }, { { name = "cmdline_history" } } } ), completion = { completeopt = "menu,menuone,noselect", }, }) vim.cmd([[highlight! default link CmpItemKind CmpItemMenuDefault]]) end, }