kickstart.nvim/lua/plugins/cmp/setup.lua

261 lines
8.4 KiB
Lua

---@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 {
['<C-n>'] = cmp.mapping.select_next_item(),
['<C-p>'] = cmp.mapping.select_prev_item(),
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-y>'] = cmp.mapping.confirm { select = true },
['<Tab>'] = 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' }),
['<S-Tab>'] = 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' }),
['<C-Space>'] = cmp.mapping.complete {},
['<C-l>'] = cmp.mapping(function()
if luasnip.expand_or_locally_jumpable() then
luasnip.expand_or_jump()
end
end, { 'i', 's' }),
['<C-h>'] = 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
['<Tab>'] = {
c = function(fallback)
if cmp.visible() then
cmp.select_next_item()
else
cmp.complete()
cmp.select_next_item()
end
end,
},
['<S-Tab>'] = {
c = function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
cmp.complete()
cmp.select_prev_item()
end
end,
},
['<C-n>'] = {
c = function(fallback)
if cmp.visible() then
cmp.select_next_item()
else
cmp.complete()
end
end,
},
['<C-p>'] = {
c = function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
cmp.complete()
end
end,
},
['<C-e>'] = {
c = cmp.mapping.abort(),
},
['<CR>'] = {
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