Improve keymapping handling

This commit is contained in:
hrsh7th
2022-01-16 00:19:30 +09:00
parent 243d5f4a12
commit 703e915b7f
2 changed files with 85 additions and 70 deletions

View File

@@ -1,3 +1,4 @@
local cache = require('cmp.utils.cache')
local misc = require('cmp.utils.misc')
local api = require('cmp.utils.api')
@@ -101,17 +102,18 @@ keymap.listen = function(mode, lhs, callback)
end
local bufnr = existing.buffer and vim.api.nvim_get_current_buf() or -1
local fallback = keymap.evacuate(bufnr, mode, existing)
keymap.set_map(bufnr, mode, lhs, function()
if mode == 'c' and vim.fn.getcmdtype() == '=' then
return keymap.feed_map(existing)
end
vim.api.nvim_feedkeys(fallback.keys, 'it' .. (fallback.noremap and 'n' or 'm'), true)
else
callback(
lhs,
misc.once(function()
keymap.feed_map(existing)
vim.api.nvim_feedkeys(fallback.keys, 'it' .. (fallback.noremap and 'n' or 'm'), true)
end)
)
end
end, {
expr = false,
noremap = true,
@@ -119,6 +121,72 @@ keymap.listen = function(mode, lhs, callback)
})
end
---Evacuate existing mapping.
---@param map table
keymap.evacuate = setmetatable({
cache = cache.new(),
}, {
__call = function(self, bufnr, mode, map)
local fallback = self.cache:ensure({ bufnr, mode, map.lhs }, function()
return string.format('<Plug>(cmp.u.k.evacuate:%s)', misc.id('cmp.utils.keymap.evacuate'))
end)
if map.expr then
keymap.set_map(bufnr, mode, fallback, function()
local lhs = keymap.t(map.lhs)
local rhs = (function()
if map.callback then
return map.callback()
end
return vim.api.nvim_eval(keymap.t(map.rhs))
end)()
if not map.noremap then
rhs = keymap.recursive(lhs, rhs)
end
return rhs
end, {
expr = true,
noremap = map.noremap,
script = map.script,
silent = mode ~= 'c',
nowait = map.nowait,
})
elseif mode ~= 'c' then
local rhs = map.rhs
if not map.noremap then
rhs = keymap.recursive(map.lhs, rhs)
end
keymap.set_map(bufnr, mode, fallback, rhs, {
expr = false,
noremap = map.noremap,
script = map.script,
silent = mode ~= 'c',
nowait = map.nowait,
})
else
local lhs = keymap.t(map.lhs)
local rhs = keymap.t(map.rhs)
if not map.noremap then
rhs = keymap.recursive(lhs, rhs)
end
return { keys = rhs, noremap = map.noremap }
end
return { keys = keymap.t(fallback), noremap = false }
end,
})
---Solve recursive mapping.
---@param lhs string
---@param rhs string
---@return string
keymap.recursive = function(lhs, rhs)
if string.find(rhs, lhs, 1, true) == 1 then
local expr = string.format(keymap.t('<C-r>=v:lua.vim.json.decode(%s)<CR>'), vim.fn.string(vim.json.encode(lhs)))
return string.gsub(rhs, '^' .. vim.pesc(lhs), expr)
end
return rhs
end
---Get map
---@param mode string
---@param lhs string
@@ -171,30 +239,6 @@ keymap.get_map = function(mode, lhs)
}
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 = vim.api.nvim_eval(keymap.t(map.rhs))
else
rhs = keymap.t(map.rhs)
end
if not map.noremap and string.find(rhs, lhs, 1, true) == 1 then
rhs = string.gsub(rhs, '^' .. vim.pesc(lhs), '')
vim.api.nvim_feedkeys(keymap.expression(rhs), 'itm', true)
vim.api.nvim_feedkeys(lhs, 'itn', true)
else
vim.api.nvim_feedkeys(keymap.expression(rhs), 'it' .. (map.noremap and 'n' or 'm'), true)
end
end
---Set keymapping
keymap.set_map = setmetatable({
callbacks = {},
@@ -221,27 +265,4 @@ misc.set(_G, { 'cmp', 'utils', 'keymap', 'set_map' }, function(id)
return keymap.set_map.callbacks[id]() or ''
end)
---Resolve expression.
---@param expr string
---@return string
keymap.expression = function(expr)
local spans = {}
local f = 0
for i = 1, #expr do
local c = string.sub(expr, i, i)
if f == 0 and c == keymap.t('<C-r>') then
f = i
end
if f ~= 0 and c == keymap.t('<CR>') then
table.insert(spans, { s = f, e = i })
f = 0
end
end
for i = #spans, 1, -1 do
local s, e = spans[i].s, spans[i].e
expr = string.sub(expr, 1, s - 1) .. keymap.expression(vim.api.nvim_eval(string.sub(expr, s + 2, e - 1))) .. string.sub(expr, e + 1)
end
return expr
end
return keymap

View File

@@ -1,4 +1,3 @@
local feedkeys = require('cmp.utils.feedkeys')
local spec = require('cmp.utils.spec')
local keymap = require('cmp.utils.keymap')
@@ -32,7 +31,7 @@ describe('keymap', function()
assert.are.equal(keymap.to_keymap('|'), '<Bar>')
end)
describe('feedmap', function()
describe('evacuate', function()
before_each(spec.before)
it('expr & register', function()
@@ -40,9 +39,8 @@ describe('keymap', function()
expr = true,
noremap = false,
})
feedkeys.call('i', 'nx', function()
keymap.feed_map(keymap.get_map('i', '('))
end)
local fallback = keymap.evacuate(0, 'i', keymap.get_map('i', '('))
vim.api.nvim_feedkeys('i' .. fallback.keys, 'x' .. (fallback.noremap and 'n' or 'm'), true)
assert.are.same({ '(' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
end)
@@ -55,9 +53,8 @@ describe('keymap', function()
expr = false,
noremap = false,
})
feedkeys.call('i', 'nx', function()
keymap.feed_map(keymap.get_map('i', '('))
end)
local fallback = keymap.evacuate(0, 'i', keymap.get_map('i', '('))
vim.api.nvim_feedkeys('i' .. fallback.keys, 'x' .. (fallback.noremap and 'n' or 'm'), true)
assert.are.same({ '()' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
end)
@@ -69,10 +66,8 @@ describe('keymap', function()
expr = true,
noremap = false,
})
feedkeys.call('i', 'n', function()
keymap.feed_map(keymap.get_map('i', '<Tab>'))
end)
feedkeys.call('', 'x')
local fallback = keymap.evacuate(0, 'i', keymap.get_map('i', '<Tab>'))
vim.api.nvim_feedkeys('i' .. fallback.keys, 'x' .. (fallback.noremap and 'n' or 'm'), true)
assert.are.same({ 'foobar' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
end)
it('false', function()
@@ -80,9 +75,8 @@ describe('keymap', function()
expr = true,
noremap = false,
})
feedkeys.call('i', 'nx', function()
keymap.feed_map(keymap.get_map('i', '<Tab>'))
end)
local fallback = keymap.evacuate(0, 'i', keymap.get_map('i', '<Tab>'))
vim.api.nvim_feedkeys('i' .. fallback.keys, 'x' .. (fallback.noremap and 'n' or 'm'), true)
assert.are.same({ '\taiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
end)
end)