diff --git a/lua/cmp/utils/keymap.lua b/lua/cmp/utils/keymap.lua index eede820..daf9b0d 100644 --- a/lua/cmp/utils/keymap.lua +++ b/lua/cmp/utils/keymap.lua @@ -7,7 +7,9 @@ local keymap = {} ---@param keys string ---@return string keymap.t = function(keys) - return vim.api.nvim_replace_termcodes(keys, true, true, true) + return (string.gsub(keys, '(<[A-Za-z0-9_%-%[%]%^@]->)', function(match) + return vim.api.nvim_eval(string.format([["\%s"]], match)) + end)) end ---Normalize key sequence. @@ -85,23 +87,22 @@ end keymap.listen = function(mode, lhs, callback) lhs = keymap.normalize(keymap.to_keymap(lhs)) - local existing = keymap.get_mapping(mode, lhs) + local existing = keymap.get_map(mode, lhs) local id = string.match(existing.rhs, 'v:lua%.cmp%.utils%.keymap%.set_map%((%d+)%)') if id and keymap.set_map.callbacks[tonumber(id, 10)] then return end local bufnr = existing.buffer and vim.api.nvim_get_current_buf() or -1 - local fallback = keymap.evacuate(bufnr, mode, lhs) keymap.set_map(bufnr, mode, lhs, function() if mode == 'c' and vim.fn.getcmdtype() == '=' then - return vim.api.nvim_feedkeys(keymap.t(fallback.keys), fallback.mode, true) + return keymap.feed_map(existing) end callback( lhs, misc.once(function() - vim.api.nvim_feedkeys(keymap.t(fallback.keys), fallback.mode, true) + keymap.feed_map(existing) end) ) end, { @@ -111,11 +112,11 @@ keymap.listen = function(mode, lhs, callback) }) end ----Get mapping +---Get map ---@param mode string ---@param lhs string ---@return table -keymap.get_mapping = function(mode, lhs) +keymap.get_map = function(mode, lhs) lhs = keymap.normalize(lhs) for _, map in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do @@ -163,86 +164,32 @@ keymap.get_mapping = function(mode, lhs) } end ----Evacuate existing key mapping ----@param bufnr number ----@param mode string ----@param lhs string ----@return { keys: string, mode: string } -keymap.evacuate = function(bufnr, mode, lhs) - local map = keymap.get_mapping(mode, lhs) - if not map then - return { keys = lhs, mode = 'itn' } - end - - -- Keep existing mapping as mapping. We escape fisrt recursive key sequence. See `:help recursive_mapping`) - local rhs, callback = '', nil - if map.callback then - callback = function(...) - local keys = map.callback(...) - if map.noremap then - return keys - end - - vim.api.nvim_feedkeys(keymap.t(keymap.recursive(bufnr, mode, lhs, keys)), 'i', true) - return keymap.t('') - end +---Feed mapping object. +---@param map table +keymap.feed_map = function(map) + local lhs = keymap.t(map.lhs) + local rhs + if map.callback and not map.expr then + return map.callback() + elseif map.callback and map.expr then + rhs = map.callback() + elseif map.expr then + rhs = keymap.t(vim.api.nvim_eval(map.rhs)) else - rhs = map.rhs - if not map.noremap and map.expr then - -- remap & expr mapping should evacuate as mapping with solving recursive mapping. - rhs = function() - -- Feed new key sequence to expand recursive mapping. - vim.api.nvim_feedkeys(keymap.t(keymap.recursive(bufnr, mode, lhs, vim.api.nvim_eval(map.rhs))), 'i', true) - return keymap.t('') - end - elseif map.noremap and map.expr then - -- noremap & expr mapping should always evacuate as mapping. - rhs = rhs - elseif map.script then - -- script mapping should always evacuate as mapping. - rhs = rhs - elseif not map.noremap then - -- remap & non-expr mapping should be checked if recursive or not. - rhs = keymap.recursive(bufnr, mode, lhs, rhs) - if keymap.equals(rhs, map.rhs) or map.noremap then - return { keys = rhs, mode = 'it' .. (map.noremap and 'n' or '') } - end + rhs = keymap.t(map.rhs) + end + + if map.noremap then + vim.api.nvim_feedkeys(rhs, 'itn', true) + else + if string.find(rhs, lhs, 1, true) == 1 then + rhs = string.gsub(rhs, '^' .. vim.pesc(lhs), '') + vim.api.nvim_feedkeys(rhs, 'itm', true) + vim.api.nvim_feedkeys(lhs, 'itn', true) else - -- noremap & non-expr mapping doesn't need to evacuate. - return { keys = rhs, mode = 'it' .. (map.noremap and 'n' or '') } + vim.api.nvim_feedkeys(rhs, 'itm', true) end end - - local fallback = ('(cmp.utils.keymap.evacuate:%s)'):format(map.lhs) - keymap.set_map(bufnr, mode, fallback, rhs, { - expr = map.expr, - noremap = map.noremap, - callback = callback, - script = map.script, - silent = mode ~= 'c', -- I can't understand but it solves the #427 (wilder.nvim's mapping does not work if silent=true in cmdline mode...) - }) - return { keys = fallback, mode = 'it' } -end - ----Solve recursive mapping ----@param bufnr number ----@param mode string ----@param lhs string ----@param rhs string ----@return string -keymap.recursive = function(bufnr, mode, lhs, rhs) - rhs = keymap.normalize(rhs) - - local recursive_lhs = ('(cmp.utils.keymap.recursive:%s)'):format(lhs) - local recursive_rhs = string.gsub(rhs, '^' .. vim.pesc(keymap.normalize(lhs)), recursive_lhs) - if not keymap.equals(recursive_rhs, rhs) then - keymap.set_map(bufnr, mode, recursive_lhs, lhs, { - expr = false, - noremap = true, - silent = true, - }) - end - return recursive_rhs end ---Set keymapping diff --git a/lua/cmp/utils/keymap_spec.lua b/lua/cmp/utils/keymap_spec.lua index 315220c..987c764 100644 --- a/lua/cmp/utils/keymap_spec.lua +++ b/lua/cmp/utils/keymap_spec.lua @@ -1,3 +1,4 @@ +local feedkeys = require('cmp.utils.feedkeys') local spec = require('cmp.utils.spec') local keymap = require('cmp.utils.keymap') @@ -5,13 +6,19 @@ local keymap = require('cmp.utils.keymap') describe('keymap', function() before_each(spec.before) + it('t', function() + assert.are.equal(keymap.t(''), vim.api.nvim_replace_termcodes('', true, true, true)) + assert.are.equal(keymap.t('u'), vim.api.nvim_replace_termcodes('u', true, true, true)) + assert.are.equal(keymap.t('aiueo'), vim.api.nvim_replace_termcodes('aiueo', true, true, true)) + end) + it('to_keymap', function() assert.are.equal(keymap.to_keymap('\n'), '') assert.are.equal(keymap.to_keymap(''), '') assert.are.equal(keymap.to_keymap('|'), '') end) - describe('evacuate', function() + describe('feedmap', function() before_each(spec.before) it('expr & register', function() @@ -19,8 +26,9 @@ describe('keymap', function() expr = true, noremap = false, }) - local fallback = keymap.evacuate(0, 'i', '(') - vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true) + feedkeys.call('i', 'nx', function() + keymap.feed_map(keymap.get_map('i', '(')) + end) assert.are.same({ '(' }, vim.api.nvim_buf_get_lines(0, 0, -1, true)) end) @@ -33,8 +41,9 @@ describe('keymap', function() expr = false, noremap = false, }) - local fallback = keymap.evacuate(0, 'i', '(') - vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true) + feedkeys.call('i', 'nx', function() + keymap.feed_map(keymap.get_map('i', '(')) + end) assert.are.same({ '()' }, vim.api.nvim_buf_get_lines(0, 0, -1, true)) end) @@ -46,8 +55,10 @@ describe('keymap', function() expr = true, noremap = false, }) - local fallback = keymap.evacuate(0, 'i', '') - vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true) + feedkeys.call('i', 'n', function() + keymap.feed_map(keymap.get_map('i', '')) + end) + feedkeys.call('', 'x') assert.are.same({ 'foobar' }, vim.api.nvim_buf_get_lines(0, 0, -1, true)) end) it('false', function() @@ -55,8 +66,9 @@ describe('keymap', function() expr = true, noremap = false, }) - local fallback = keymap.evacuate(0, 'i', '') - vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true) + feedkeys.call('i', 'nx', function() + keymap.feed_map(keymap.get_map('i', '')) + end) assert.are.same({ '\taiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true)) end) end) diff --git a/lua/cmp/utils/misc.lua b/lua/cmp/utils/misc.lua index cccd52a..f2c3bbd 100644 --- a/lua/cmp/utils/misc.lua +++ b/lua/cmp/utils/misc.lua @@ -77,7 +77,7 @@ misc.id = setmetatable({ group = {}, }, { __call = function(_, group) - misc.id.group[group] = misc.id.group[group] or vim.loop.now() + misc.id.group[group] = misc.id.group[group] or 0 misc.id.group[group] = misc.id.group[group] + 1 return misc.id.group[group] end,