From 017d3abc57f37b6ac160f5025929212a622a147f Mon Sep 17 00:00:00 2001 From: hrsh7th Date: Tue, 2 Nov 2021 11:59:26 +0900 Subject: [PATCH] Organize keymap implementation (#454) * Organize keymap implementation * Pass tests * Improve * Restore tests --- lua/cmp/utils/keymap.lua | 195 ++++++++++++++-------------------- lua/cmp/utils/keymap_spec.lua | 14 +-- 2 files changed, 82 insertions(+), 127 deletions(-) diff --git a/lua/cmp/utils/keymap.lua b/lua/cmp/utils/keymap.lua index 5430b88..92ab77d 100644 --- a/lua/cmp/utils/keymap.lua +++ b/lua/cmp/utils/keymap.lua @@ -1,6 +1,4 @@ local misc = require('cmp.utils.misc') -local str = require('cmp.utils.str') -local cache = require('cmp.utils.cache') local api = require('cmp.utils.api') local keymap = {} @@ -12,21 +10,6 @@ keymap.t = function(keys) return vim.api.nvim_replace_termcodes(keys, true, true, true) end ----Escape keymap with -keymap.escape = function(keys) - local i = 1 - while i <= #keys do - if string.sub(keys, i, i) == '<' then - if not vim.tbl_contains({ '', '', '', '' }, string.sub(keys, i, i + 3)) then - keys = string.sub(keys, 1, i - 1) .. '' .. string.sub(keys, i + 1) - i = i + 3 - end - end - i = i + 1 - end - return keys -end - ---Normalize key sequence. ---@param keys string ---@return string @@ -109,56 +92,30 @@ keymap.equals = function(a, b) end ---Register keypress handler. -keymap.listen = setmetatable({ - cache = cache.new(), -}, { - __call = function(self, mode, keys_or_chars, callback) - local keys = keymap.normalize(keymap.to_keymap(keys_or_chars)) - local existing = keymap.get_mapping(mode, keys) - if not existing then - return - end - local bufnr = existing.buffer and vim.api.nvim_get_current_buf() or -1 - self.cache:set({ 'id', mode, keys, bufnr }, misc.id('cmp.utils.keymap.listen')) +keymap.listen = function(mode, lhs, callback) + lhs = keymap.normalize(keymap.to_keymap(lhs)) - local fallback = keymap.evacuate(mode, keys, bufnr) - if bufnr == -1 then - vim.api.nvim_set_keymap(mode, keys, ('call v:lua.cmp.utils.keymap.listen.run(%s)'):format(self.cache:get({ 'id', mode, keys, bufnr })), { - expr = false, - noremap = true, - silent = true, - }) - else - vim.api.nvim_buf_set_keymap(0, mode, keys, ('call v:lua.cmp.utils.keymap.listen.run(%s)'):format(self.cache:get({ 'id', mode, keys, bufnr })), { - expr = false, - noremap = true, - silent = true, - }) - end - - self.cache:set({ 'definition', self.cache:get({ 'id', mode, keys, bufnr }) }, { - mode = mode, - keys = keys, - bufnr = bufnr, - callback = callback, - fallback = fallback, - existing = existing, - }) - end, -}) -misc.set(_G, { 'cmp', 'utils', 'keymap', 'listen', 'run' }, function(id) - local definition = keymap.listen.cache:get({ 'definition', id }) - if definition.mode == 'c' and vim.fn.getcmdtype() == '=' then - return vim.api.nvim_feedkeys(keymap.t(definition.fallback.keys), definition.fallback.mode, true) + local existing = keymap.get_mapping(mode, lhs) + if string.match(existing.rhs, vim.pesc('v:lua.cmp.utils.keymap')) then + return end - definition.callback( - definition.keys, - misc.once(function() - vim.api.nvim_feedkeys(keymap.t(definition.fallback.keys), definition.fallback.mode, true) - end) - ) - return keymap.t('') -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) + end + + callback(lhs, misc.once(function() + vim.api.nvim_feedkeys(keymap.t(fallback.keys), fallback.mode, true) + end)) + end, { + expr = false, + noremap = true, + silent = true, + }) +end ---Get mapping ---@param mode string @@ -169,9 +126,6 @@ keymap.get_mapping = function(mode, lhs) for _, map in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do if keymap.equals(map.lhs, lhs) then - if string.match(map.rhs, vim.pesc('v:lua.cmp.utils.keymap.listen.run')) then - return nil - end return { lhs = map.lhs, rhs = map.rhs, @@ -187,9 +141,6 @@ keymap.get_mapping = function(mode, lhs) for _, map in ipairs(vim.api.nvim_get_keymap(mode)) do if keymap.equals(map.lhs, lhs) then - if string.match(map.rhs, vim.pesc('v:lua.cmp.utils.keymap.listen.run')) then - return nil - end return { lhs = map.lhs, rhs = map.rhs, @@ -202,6 +153,7 @@ keymap.get_mapping = function(mode, lhs) } end end + return { lhs = lhs, rhs = lhs, @@ -215,21 +167,23 @@ keymap.get_mapping = function(mode, lhs) end ---Evacuate existing key mapping ----@param mode string ----@param keys string ---@param bufnr number +---@param mode string +---@param lhs string ---@return { keys: string, mode: string } -keymap.evacuate = function(mode, keys, bufnr) - local map = keymap.get_mapping(mode, keys) +keymap.evacuate = function(bufnr, mode, lhs) + local map = keymap.get_mapping(mode, lhs) if not map then - return { keys = keys, mode = 'itn' } + return { keys = lhs, mode = 'itn' } end -- Keep existing mapping as mapping. We escape fisrt recursive key sequence. See `:help recursive_mapping`) local rhs = map.rhs if not map.noremap and map.expr then -- remap & expr mapping should evacuate as mapping with solving recursive mapping. - rhs = string.format('v:lua.cmp.utils.keymap.evacuate.expr("%s", "%s", "%s", "%s")', mode, str.escape(keymap.escape(keys), { '"' }), bufnr, str.escape(keymap.escape(rhs), { '"' })) + rhs = function() + return keymap.t(keymap.recursive(bufnr, mode, lhs, vim.api.nvim_eval(map.rhs))) + end elseif map.noremap and map.expr then -- noremap & expr mapping should always evacuate as mapping. rhs = rhs @@ -238,7 +192,7 @@ keymap.evacuate = function(mode, keys, bufnr) rhs = rhs elseif not map.noremap then -- remap & non-expr mapping should be checked if recursive or not. - rhs = keymap.recursive(mode, keys, bufnr, rhs) + rhs = keymap.recursive(bufnr, mode, lhs, rhs) if rhs == map.rhs or map.noremap then return { keys = rhs, mode = 'it' .. (map.noremap and 'n' or '') } end @@ -247,54 +201,61 @@ keymap.evacuate = function(mode, keys, bufnr) return { keys = rhs, mode = 'it' .. (map.noremap and 'n' or '') } end - local fallback = ('(cmp-utils-keymap-evacuate-rhs:%s)'):format(map.lhs) - if bufnr == -1 then - vim.api.nvim_set_keymap(mode, fallback, rhs, { - expr = map.expr, - noremap = map.noremap, - 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...) - }) - else - vim.api.nvim_buf_set_keymap(bufnr, mode, fallback, rhs, { - expr = map.expr, - noremap = map.noremap, - 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...) - }) - end + local fallback = ('(cmp.utils.keymap.evacuate:%s)'):format(map.lhs) + keymap.set_map(bufnr, mode, fallback, rhs, { + expr = map.expr, + noremap = map.noremap, + 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 -misc.set(_G, { 'cmp', 'utils', 'keymap', 'evacuate', 'expr' }, function(mode, keys, bufnr, rhs) - return keymap.t(keymap.recursive(mode, keys, bufnr, vim.api.nvim_eval(rhs))) -end) ---Solve recursive mapping ----@param mode string ----@param keys string ---@param bufnr number +---@param mode string +---@param lhs string ---@param rhs string ---@return string -keymap.recursive = function(mode, keys, bufnr, rhs) +keymap.recursive = function(bufnr, mode, lhs, rhs) rhs = keymap.normalize(rhs) - local fallback_lhs = ('(cmp-utils-keymap-listen-lhs:%s)'):format(keys) - local new_rhs = string.gsub(rhs, '^' .. vim.pesc(keymap.normalize(keys)), fallback_lhs) - if not keymap.equals(new_rhs, rhs) then - if bufnr == '-1' then - vim.api.nvim_set_keymap(mode, fallback_lhs, keys, { - expr = false, - noremap = true, - silent = true, - }) - else - vim.api.nvim_buf_set_keymap(0, mode, fallback_lhs, keys, { - expr = false, - noremap = true, - silent = true, - }) - end + + 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 new_rhs + return recursive_rhs end +---Set keymapping +keymap.set_map = setmetatable({ + callbacks = {} +}, { + __call = function(self, bufnr, mode, lhs, rhs, opts) + if type(rhs) == 'function' then + local id = misc.id('cmp.utils.keymap.set_map') + self.callbacks[id] = rhs + if opts.expr then + rhs = ('v:lua.cmp.utils.keymap.set_map(%s)'):format(id) + else + rhs = ('call v:lua.cmp.utils.keymap.set_map(%s)'):format(id) + end + end + + if bufnr == -1 then + vim.api.nvim_set_keymap(mode, lhs, rhs, opts) + else + vim.api.nvim_buf_set_keymap(bufnr, mode, lhs, rhs, opts) + end + end +}) +misc.set(_G, { 'cmp', 'utils', 'keymap', 'set_map' }, function(id) + return keymap.set_map.callbacks[id]() +end) + return keymap diff --git a/lua/cmp/utils/keymap_spec.lua b/lua/cmp/utils/keymap_spec.lua index 2c705fb..7270b5e 100644 --- a/lua/cmp/utils/keymap_spec.lua +++ b/lua/cmp/utils/keymap_spec.lua @@ -11,12 +11,6 @@ describe('keymap', function() assert.are.equal(keymap.to_keymap('|'), '') end) - it('escape', function() - assert.are.equal(keymap.escape(''), 'C-d>') - assert.are.equal(keymap.escape(''), 'C-d>C-f>') - assert.are.equal(keymap.escape('C-d>'), 'C-d>') - end) - describe('evacuate', function() before_each(spec.before) @@ -25,7 +19,7 @@ describe('keymap', function() expr = true, noremap = false, }) - local fallback = keymap.evacuate('i', '(') + local fallback = keymap.evacuate(0, 'i', '(') vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true) assert.are.same({ '(' }, vim.api.nvim_buf_get_lines(0, 0, -1, true)) end) @@ -39,7 +33,7 @@ describe('keymap', function() expr = false, noremap = false, }) - local fallback = keymap.evacuate('i', '(') + local fallback = keymap.evacuate(0, 'i', '(') vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true) assert.are.same({ '()' }, vim.api.nvim_buf_get_lines(0, 0, -1, true)) end) @@ -52,7 +46,7 @@ describe('keymap', function() expr = true, noremap = false, }) - local fallback = keymap.evacuate('i', '') + local fallback = keymap.evacuate(0, 'i', '') vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true) assert.are.same({ 'foobar' }, vim.api.nvim_buf_get_lines(0, 0, -1, true)) end) @@ -61,7 +55,7 @@ describe('keymap', function() expr = true, noremap = false, }) - local fallback = keymap.evacuate('i', '') + local fallback = keymap.evacuate(0, 'i', '') vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true) assert.are.same({ '\taiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true)) end)