---@diagnostic disable: undefined-global -- nvim-cmp setup module local M = {} function M.setup() local cmp = require 'cmp' local luasnip = require 'luasnip' local types = require 'cmp.types' local compare = require 'cmp.config.compare' luasnip.config.setup {} cmp.setup { snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, -- Improved completion settings - more like VSCode completion = { completeopt = 'menu,menuone,noinsert', keyword_length = 1, -- Show completions after 1 character autocomplete = { types.cmp.TriggerEvent.TextChanged }, get_trigger_characters = function(trigger_characters) -- Always add '.' as a trigger character to ensure method/property completions if not vim.tbl_contains(trigger_characters, '.') then table.insert(trigger_characters, '.') end if not vim.tbl_contains(trigger_characters, ':') then table.insert(trigger_characters, ':') end return trigger_characters end, }, -- Custom sorting to prioritize fields and methods over snippets sorting = { priority_weight = 10, -- Increase weight to make priority differences more significant comparators = { -- Custom comparator that puts snippets at the end function(entry1, entry2) local types = require('cmp.types') -- Extract the kinds local kind1 = entry1:get_kind() local kind2 = entry2:get_kind() -- Define snippet kinds local snippet_kinds = { [types.lsp.CompletionItemKind.Snippet] = true } -- Check if either is a snippet local is_snippet1 = snippet_kinds[kind1] or false local is_snippet2 = snippet_kinds[kind2] or false -- If one is a snippet and the other isn't, the non-snippet comes first if is_snippet1 and not is_snippet2 then return false elseif not is_snippet1 and is_snippet2 then return true end -- Otherwise fall through to other comparators return nil end, compare.exact, -- Exact match first compare.kind, -- Sort by kind (functions first, then properties, etc) compare.sort_text, -- Sort by LSP's sortText compare.score, -- Score based compare.offset, -- Closest to cursor compare.length, -- Shorter first compare.order, -- Source order }, }, -- Enhanced VSCode-like icons with more modern symbols formatting = { format = function(entry, vim_item) -- Set icons for completion types local kind_icons = { Text = "󰉿", Method = "󰆧", Function = "󰊕", Constructor = "", Field = "󰜢", Variable = "󰀫", Class = "󰠱", Interface = "", Module = "", Property = "󰜢", Unit = "", Value = "󰎠", Enum = "", Keyword = "󰌋", Snippet = "", Color = "󰏘", File = "󰈙", Reference = "", Folder = "󰉋", EnumMember = "", Constant = "󰏿", Struct = "", Event = "", Operator = "󰆕", TypeParameter = "󰊄", TypeAlias = "", Parameter = "", StaticMethod = "", Macro = "󰁌" } -- Enhanced formatting with better icons and spacing vim_item.kind = string.format('%s %s', kind_icons[vim_item.kind] or '', vim_item.kind) -- Show more detailed source info vim_item.menu = ({ buffer = "[Buffer]", nvim_lsp = "[LSP]", luasnip = "[Snippet]", path = "[Path]", })[entry.source.name] return vim_item end, }, -- Enhanced documentation window window = { completion = cmp.config.window.bordered({ winhighlight = "Normal:CmpPmenu,FloatBorder:CmpPmenuBorder,CursorLine:PmenuSel,Search:None", scrollbar = true, col_offset = -3, side_padding = 1, }), documentation = cmp.config.window.bordered({ winhighlight = "Normal:CmpDoc", }), }, mapping = cmp.mapping.preset.insert { [''] = cmp.mapping.select_next_item(), [''] = cmp.mapping.select_prev_item(), [''] = cmp.mapping.scroll_docs(-4), [''] = cmp.mapping.scroll_docs(4), [''] = cmp.mapping.confirm { select = true }, [''] = cmp.mapping(function(fallback) if cmp.visible() then cmp.select_next_item() elseif luasnip.expand_or_locally_jumpable() then luasnip.expand_or_jump() else fallback() end end, { 'i', 's' }), [''] = 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' }), [''] = cmp.mapping.complete {}, [''] = cmp.mapping(function() if luasnip.expand_or_locally_jumpable() then luasnip.expand_or_jump() end end, { 'i', 's' }), [''] = cmp.mapping(function() if luasnip.locally_jumpable(-1) then luasnip.jump(-1) end end, { 'i', 's' }), }, -- Configure completion sources by priority with tweaked settings sources = cmp.config.sources( { { name = 'nvim_lsp', priority = 1000, max_item_count = 50 }, -- LSP comes first with more items { name = 'path', priority = 500 }, -- Then paths { name = 'buffer', priority = 250, keyword_length = 3 }, -- Then current buffer -- Snippets with extremely low priority and limited visibility { name = 'luasnip', priority = 10, max_item_count = 3, keyword_length = 4 }, } ), -- Enable experimental features for VSCode-like experience experimental = { ghost_text = true, -- Shows virtual text as preview }, } -- Cmdline completion for search (/, ?) and command (:) -- Enhanced commandline completion with better history and formatting cmp.setup.cmdline({ '/', '?' }, { mapping = cmp.mapping.preset.cmdline(), sources = { { name = 'buffer', max_item_count = 20 } }, window = { completion = cmp.config.window.bordered({ winhighlight = "Normal:CmpPmenu,FloatBorder:CmpPmenuBorder,CursorLine:PmenuSel,Search:None", scrollbar = true, }) }, completion = { completeopt = 'menu,menuone,noinsert', } }) cmp.setup.cmdline(':', { mapping = cmp.mapping.preset.cmdline({ -- Explicit key mappings for command mode completion [''] = { c = function(fallback) if cmp.visible() then cmp.select_next_item() else cmp.complete() cmp.select_next_item() end end, }, [''] = { c = function(fallback) if cmp.visible() then cmp.select_prev_item() else cmp.complete() cmp.select_prev_item() end end, }, [''] = { c = function(fallback) if cmp.visible() then cmp.select_next_item() else cmp.complete() end end, }, [''] = { c = function(fallback) if cmp.visible() then cmp.select_prev_item() else cmp.complete() end end, }, [''] = { c = cmp.mapping.abort(), }, [''] = { c = cmp.mapping.confirm({ select = true }), }, }), sources = cmp.config.sources({ { name = 'path', max_item_count = 20 }, { name = 'cmdline', max_item_count = 30, keyword_length = 1 } }), window = { completion = cmp.config.window.bordered({ winhighlight = "Normal:CmpPmenu,FloatBorder:CmpPmenuBorder,CursorLine:PmenuSel,Search:None", scrollbar = true, col_offset = -3, side_padding = 1, }) }, formatting = { format = function(entry, vim_item) -- Only show item kind for cmdline completion vim_item.kind = ' ' vim_item.menu = '' return vim_item end, }, completion = { completeopt = 'menu,menuone,noinsert', autocomplete = { cmp.TriggerEvent.TextChanged }, } }) end return M