diff --git a/README.md b/README.md index 196eb3d..ce44709 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,20 @@ lua <'] = cmp.mapping.item.prev(), + [''] = cmp.mapping.item.next(), + [''] = cmp.mapping.scroll.up(), + [''] = cmp.mapping.scroll.down(), + [''] = cmp.mapping.complete(), + [''] = cmp.mapping.close(), + [''] = cmp.mapping.confirm({ + behavior = cmp.ConfirmBehavior.Replace, + select = true, + }) + }, -- You should specify your *installed* sources. sources = { @@ -56,23 +70,40 @@ The default configuration can be found in [here](./lua/cmp/config/default.lua) You can use your own configuration like this: ```lua -require'cmp'.setup { - ... - completion = { - autocomplete = { .. }, - completeopt = 'menu,menuone,noselect', - keyword_pattern = [[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)]], - keyword_length = 1, - }, - sorting = { - priority_weight = 2., - comparators = { ... }, - }, - sources = { ... }, - ... +local cmp = require'cmp' +cmp.setup { + ... + completion = { + autocomplete = { .. }, + completeopt = 'menu,menuone,noselect', + keyword_pattern = [[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)]], + keyword_length = 1, + }, + sorting = { + priority_weight = 2., + comparators = { ... }, + }, + mapping = { + [''] = cmp.mapping.item.prev(), + [''] = cmp.mapping.item.next(), + [''] = cmp.mapping.scroll.up(), + [''] = cmp.mapping.scroll.down(), + [''] = cmp.mapping.complete(), + [''] = cmp.mapping.close(), + [''] = cmp.mapping.confirm({ + behavior = cmp.ConfirmBehavior.Replace, + select = true, + }) + }, + sources = { ... }, + ... } ``` +### mapping (type: table) + +Define mappings with `cmp.mapping` helper. + ### completion.autocomplete (type: cmp.TriggerEvent[]) Which events should trigger `autocompletion`. @@ -124,13 +155,13 @@ Each function must return `boolean|nil`. Default: ```lua { - compare.offset, - compare.exact, - compare.score, - compare.kind, - compare.sort_text, - compare.length, - compare.order, + compare.offset, + compare.exact, + compare.score, + compare.kind, + compare.sort_text, + compare.length, + compare.order, } ``` diff --git a/lua/cmp/config/default.lua b/lua/cmp/config/default.lua index cb43665..2cb9df4 100644 --- a/lua/cmp/config/default.lua +++ b/lua/cmp/config/default.lua @@ -29,20 +29,10 @@ return function() winhighlight = 'NormalFloat:CmpDocumentation,FloatBorder:CmpDocumentationBorder', maxwidth = math.floor((WIDE_HEIGHT * 2) * (vim.o.columns / (WIDE_HEIGHT * 2 * 16 / 9))), maxheight = math.floor(WIDE_HEIGHT * (WIDE_HEIGHT / vim.o.lines)), - mapping = { - [''] = types.cmp.ScrollDirection.Up, - [''] = types.cmp.ScrollDirection.Down, - } }, confirmation = { default_behavior = types.cmp.ConfirmBehavior.Replace, - mapping = { - [''] = { - behavior = types.cmp.ConfirmBehavior.Replace, - select = true, - }, - } }, sorting = { @@ -58,6 +48,8 @@ return function() } }, + mapping = {}, + formatting = { format = function(e, suggest_offset) local item = e:get_completion_item() diff --git a/lua/cmp/core.lua b/lua/cmp/core.lua index e84d772..18bff2b 100644 --- a/lua/cmp/core.lua +++ b/lua/cmp/core.lua @@ -71,29 +71,49 @@ end ---Keypress handler core.on_keymap = function(keys, fallback) - -- Documentation scroll - if config.get().documentation.mapping[keys] then - if config.get().documentation.mapping[keys] == types.cmp.ScrollDirection.Up then - core.menu.float:scroll(-4) - elseif config.get().documentation.mapping[keys] == types.cmp.ScrollDirection.Down then - core.menu.float:scroll(4) - end - return - end - - -- Confirm character - if config.get().confirmation.mapping[keys] then - local c = config.get().confirmation.mapping[keys] - local e = core.menu:get_selected_entry() or (c.select and core.menu:get_first_entry()) - if not e then + for key, c in pairs(config.get().mapping) do + if key == keys then + if c.type == 'confirm' then + local e = core.menu:get_selected_entry() or (c.select and core.menu:get_first_entry()) + if e then + core.confirm(e, { + behavior = c.behavior, + }, function() + core.complete(core.get_context({ reason = types.cmp.ContextReason.TriggerOnly })) + end) + return + end + elseif c.type == 'complete' then + core.complete(core.get_context({ reason = types.cmp.ContextReason.Manual })) + return + elseif c.type == 'close' then + if vim.fn.pumvisible() == 1 then + core.reset() + return + end + elseif c.type == 'scroll.up' then + if core.menu.float:is_visible() then + core.menu.float:scroll(-c.delta) + return + end + elseif c.type == 'scroll.down' then + if core.menu.float:is_visible() then + core.menu.float:scroll(c.delta) + return + end + elseif c.type == 'item.next' then + if vim.fn.pumvisible() == 1 then + keymap.feedkeys('', 'n') + return + end + elseif c.type == 'item.prev' then + if vim.fn.pumvisible() == 1 then + keymap.feedkeys('', 'n') + return + end + end return fallback() end - core.confirm(e, { - behavior = c.behavior, - }, function() - core.complete(core.get_context({ reason = types.cmp.ContextReason.TriggerOnly })) - end) - return end --Commit character. NOTE: This has a lot of cmp specific implementation to make more user-friendly. @@ -101,7 +121,7 @@ core.on_keymap = function(keys, fallback) local e = core.menu:get_selected_entry() if e and vim.tbl_contains(e:get_commit_characters(), chars) then local is_printable = char.is_printable(string.byte(chars, 1)) - return core.confirm(e, { + core.confirm(e, { behavior = is_printable and 'insert' or 'replace', }, function() local ctx = core.get_context() @@ -112,6 +132,7 @@ core.on_keymap = function(keys, fallback) core.reset() end end) + return end fallback() @@ -119,10 +140,7 @@ end ---Prepare completion core.prepare = function() - for keys in pairs(config.get().confirmation.mapping) do - keymap.listen(keys, core.on_keymap) - end - for keys in pairs(config.get().documentation.mapping) do + for keys in pairs(config.get().mapping) do keymap.listen(keys, core.on_keymap) end end diff --git a/lua/cmp/float.lua b/lua/cmp/float.lua index 0f3883c..4e4a6b4 100644 --- a/lua/cmp/float.lua +++ b/lua/cmp/float.lua @@ -102,7 +102,7 @@ end ---Close floating window float.close = async.throttle( vim.schedule_wrap(function(self) - if self.win and vim.api.nvim_win_is_valid(self.win) then + if self:is_visible() then vim.api.nvim_win_close(self.win, true) end self.entry = nil @@ -113,7 +113,7 @@ float.close = async.throttle( ) float.scroll = function(self, delta) - if self.win and vim.api.nvim_win_is_valid(self.win) then + if self:is_visible() then local info = vim.fn.getwininfo(self.win)[1] or {} local buf = vim.api.nvim_win_get_buf(self.win) local top = info.topline or 1 @@ -129,4 +129,8 @@ float.scroll = function(self, delta) end end +float.is_visible = function(self) + return self.win and vim.api.nvim_win_is_valid(self.win) +end + return float diff --git a/lua/cmp/init.lua b/lua/cmp/init.lua index b6f0352..103a2c0 100644 --- a/lua/cmp/init.lua +++ b/lua/cmp/init.lua @@ -1,5 +1,4 @@ local core = require('cmp.core') -local types = require('cmp.types') local source = require('cmp.source') local config = require('cmp.config') local autocmd = require('cmp.autocmd') @@ -13,6 +12,9 @@ end cmp.lsp = require('cmp.types.lsp') cmp.vim = require('cmp.types.vim') +---Export mapping +cmp.mapping = require('cmp.mapping') + ---Register completion sources ---@param name string ---@param s cmp.Source @@ -43,18 +45,6 @@ cmp.setup = setmetatable({ end, }) ----Invoke completion manually -cmp.complete = function() - core.complete(core.get_context({ - reason = types.cmp.ContextReason.Manual, - })) -end - ----Close completion -cmp.close = function() - core.reset() -end - ---Handle events autocmd.subscribe('InsertEnter', function() core.prepare() diff --git a/lua/cmp/mapping.lua b/lua/cmp/mapping.lua new file mode 100644 index 0000000..f5a8133 --- /dev/null +++ b/lua/cmp/mapping.lua @@ -0,0 +1,59 @@ +local types = require('cmp.types') + +local mapping = {} + +---Create complete mapping +mapping.complete = function() + return { + type = 'complete', + } +end + +---Create close mapping +mapping.close = function() + return { + type = 'close', + } +end + +---Create scroll mapping +mapping.scroll = { + up = function(delta) + return { + type = 'scroll.up', + delta = delta or 4, + } + end, + down = function(delta) + return { + type = 'scroll.down', + delta = delta or 4, + } + end, +} + +---Create item mapping +mapping.item = { + prev = function() + return { + type = 'item.prev', + } + end, + next = function() + return { + type = 'item.next', + } + end, +} + +---Create confirm mapping +mapping.confirm = function(option) + option = option or {} + return { + type = 'confirm', + select = option.select or false, + behavior = option.behavior or types.cmp.ConfirmBehavior.Insert, + } +end + +return mapping diff --git a/lua/cmp/types/cmp.lua b/lua/cmp/types/cmp.lua index b64f73c..334cca0 100644 --- a/lua/cmp/types/cmp.lua +++ b/lua/cmp/types/cmp.lua @@ -51,6 +51,7 @@ cmp.ScrollDirection.Down = 'down' ---@field public sorting cmp.SortingConfig ---@field public formatting cmp.FormattingConfig ---@field public snippet cmp.SnippetConfig +---@field public mapping table ---@field public sources cmp.SourceConfig[] ---@class cmp.CompletionConfig @@ -64,15 +65,9 @@ cmp.ScrollDirection.Down = 'down' ---@field public winhighlight string ---@field public maxwidth number|nil ---@field public maxheight number|nil ----@field public mapping table ---@class cmp.ConfirmationConfig ---@field public default_behavior cmp.ConfirmBehavior ----@field public mapping table - ----@class cmp.ConfirmMappingConfig ----@field behavior cmp.ConfirmBehavior ----@field select boolean ---@class cmp.SortingConfig ---@field public priority_weight number @@ -88,5 +83,34 @@ cmp.ScrollDirection.Down = 'down' ---@field public name string ---@field public opts table +---@alias cmp.MappingConfig cmp.ConfirmMapping | cmp.CompleteMapping | cmp.CloseMapping | cmp.ItemNextMapping | cmp.ItemPrevMapping | cmp.ScrollUpMapping | cmp.ScrollDownMapping + +---@class cmp.ConfirmMapping +---@field public type '"confirm"' +---@field public select boolean +---@field public behavior cmp.ConfirmBehavior + +---@class cmp.CompleteMapping +---@field public type '"complete"' + +---@class cmp.CloseMapping +---@field public type '"close"' + +---@class cmp.ItemNextMapping +---@field public type '"item.next"' +---@field public delta number + +---@class cmp.ItemPrevMapping +---@field public type '"item.prev"' +---@field public delta number + +---@class cmp.ScrollUpMapping +---@field public type '"scroll.up"' +---@field public delta number + +---@class cmp.ScrollDownMapping +---@field public type '"scroll.down"' +---@field public delta number + return cmp diff --git a/plugin/cmp.lua b/plugin/cmp.lua index fc30162..c9df80a 100644 --- a/plugin/cmp.lua +++ b/plugin/cmp.lua @@ -26,13 +26,3 @@ if vim.fn.hlexists('CmpDocumentationBorder') == 0 then vim.cmd [[highlight link CmpDocumentationBorder NormalFloat]] end -misc.set(_G, { 'cmp', 'complete' }, function() - cmp.complete() - return vim.api.nvim_replace_termcodes('', true, true, true) -end) - -misc.set(_G, { 'cmp', 'close' }, function() - cmp.close() - return vim.api.nvim_replace_termcodes('', true, true, true) -end) -