Improve keymap handling (#720)
* Improve keymap handling * Simplify * Fix tests * Fix callback no expr mapping * Rename functions * Use manual key conversion logic * fmt
This commit is contained in:
@@ -7,7 +7,9 @@ local keymap = {}
|
|||||||
---@param keys string
|
---@param keys string
|
||||||
---@return string
|
---@return string
|
||||||
keymap.t = function(keys)
|
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
|
end
|
||||||
|
|
||||||
---Normalize key sequence.
|
---Normalize key sequence.
|
||||||
@@ -85,23 +87,22 @@ end
|
|||||||
keymap.listen = function(mode, lhs, callback)
|
keymap.listen = function(mode, lhs, callback)
|
||||||
lhs = keymap.normalize(keymap.to_keymap(lhs))
|
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+)%)')
|
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
|
if id and keymap.set_map.callbacks[tonumber(id, 10)] then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local bufnr = existing.buffer and vim.api.nvim_get_current_buf() or -1
|
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()
|
keymap.set_map(bufnr, mode, lhs, function()
|
||||||
if mode == 'c' and vim.fn.getcmdtype() == '=' then
|
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
|
end
|
||||||
|
|
||||||
callback(
|
callback(
|
||||||
lhs,
|
lhs,
|
||||||
misc.once(function()
|
misc.once(function()
|
||||||
vim.api.nvim_feedkeys(keymap.t(fallback.keys), fallback.mode, true)
|
keymap.feed_map(existing)
|
||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
end, {
|
end, {
|
||||||
@@ -111,11 +112,11 @@ keymap.listen = function(mode, lhs, callback)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get mapping
|
---Get map
|
||||||
---@param mode string
|
---@param mode string
|
||||||
---@param lhs string
|
---@param lhs string
|
||||||
---@return table
|
---@return table
|
||||||
keymap.get_mapping = function(mode, lhs)
|
keymap.get_map = function(mode, lhs)
|
||||||
lhs = keymap.normalize(lhs)
|
lhs = keymap.normalize(lhs)
|
||||||
|
|
||||||
for _, map in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do
|
for _, map in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do
|
||||||
@@ -163,86 +164,32 @@ keymap.get_mapping = function(mode, lhs)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
---Evacuate existing key mapping
|
---Feed mapping object.
|
||||||
---@param bufnr number
|
---@param map table
|
||||||
---@param mode string
|
keymap.feed_map = function(map)
|
||||||
---@param lhs string
|
local lhs = keymap.t(map.lhs)
|
||||||
---@return { keys: string, mode: string }
|
local rhs
|
||||||
keymap.evacuate = function(bufnr, mode, lhs)
|
if map.callback and not map.expr then
|
||||||
local map = keymap.get_mapping(mode, lhs)
|
return map.callback()
|
||||||
if not map then
|
elseif map.callback and map.expr then
|
||||||
return { keys = lhs, mode = 'itn' }
|
rhs = map.callback()
|
||||||
|
elseif map.expr then
|
||||||
|
rhs = keymap.t(vim.api.nvim_eval(map.rhs))
|
||||||
|
else
|
||||||
|
rhs = keymap.t(map.rhs)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Keep existing mapping as <Plug> 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
|
if map.noremap then
|
||||||
return keys
|
vim.api.nvim_feedkeys(rhs, 'itn', true)
|
||||||
end
|
|
||||||
|
|
||||||
vim.api.nvim_feedkeys(keymap.t(keymap.recursive(bufnr, mode, lhs, keys)), 'i', true)
|
|
||||||
return keymap.t('<Ignore>')
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
rhs = map.rhs
|
if string.find(rhs, lhs, 1, true) == 1 then
|
||||||
if not map.noremap and map.expr then
|
rhs = string.gsub(rhs, '^' .. vim.pesc(lhs), '')
|
||||||
-- remap & expr mapping should evacuate as <Plug> mapping with solving recursive mapping.
|
vim.api.nvim_feedkeys(rhs, 'itm', true)
|
||||||
rhs = function()
|
vim.api.nvim_feedkeys(lhs, 'itn', true)
|
||||||
-- 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('<Ignore>')
|
|
||||||
end
|
|
||||||
elseif map.noremap and map.expr then
|
|
||||||
-- noremap & expr mapping should always evacuate as <Plug> mapping.
|
|
||||||
rhs = rhs
|
|
||||||
elseif map.script then
|
|
||||||
-- script mapping should always evacuate as <Plug> 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
|
|
||||||
else
|
else
|
||||||
-- noremap & non-expr mapping doesn't need to evacuate.
|
vim.api.nvim_feedkeys(rhs, 'itm', true)
|
||||||
return { keys = rhs, mode = 'it' .. (map.noremap and 'n' or '') }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local fallback = ('<Plug>(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 = ('<Plug>(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
|
end
|
||||||
|
|
||||||
---Set keymapping
|
---Set keymapping
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
local feedkeys = require('cmp.utils.feedkeys')
|
||||||
local spec = require('cmp.utils.spec')
|
local spec = require('cmp.utils.spec')
|
||||||
|
|
||||||
local keymap = require('cmp.utils.keymap')
|
local keymap = require('cmp.utils.keymap')
|
||||||
@@ -5,13 +6,19 @@ local keymap = require('cmp.utils.keymap')
|
|||||||
describe('keymap', function()
|
describe('keymap', function()
|
||||||
before_each(spec.before)
|
before_each(spec.before)
|
||||||
|
|
||||||
|
it('t', function()
|
||||||
|
assert.are.equal(keymap.t('<Tab>'), vim.api.nvim_replace_termcodes('<Tab>', true, true, true))
|
||||||
|
assert.are.equal(keymap.t('<C-g>u'), vim.api.nvim_replace_termcodes('<C-g>u', true, true, true))
|
||||||
|
assert.are.equal(keymap.t('<Cmd>aiueo<CR>'), vim.api.nvim_replace_termcodes('<Cmd>aiueo<CR>', true, true, true))
|
||||||
|
end)
|
||||||
|
|
||||||
it('to_keymap', function()
|
it('to_keymap', function()
|
||||||
assert.are.equal(keymap.to_keymap('\n'), '<CR>')
|
assert.are.equal(keymap.to_keymap('\n'), '<CR>')
|
||||||
assert.are.equal(keymap.to_keymap('<CR>'), '<CR>')
|
assert.are.equal(keymap.to_keymap('<CR>'), '<CR>')
|
||||||
assert.are.equal(keymap.to_keymap('|'), '<Bar>')
|
assert.are.equal(keymap.to_keymap('|'), '<Bar>')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('evacuate', function()
|
describe('feedmap', function()
|
||||||
before_each(spec.before)
|
before_each(spec.before)
|
||||||
|
|
||||||
it('expr & register', function()
|
it('expr & register', function()
|
||||||
@@ -19,8 +26,9 @@ describe('keymap', function()
|
|||||||
expr = true,
|
expr = true,
|
||||||
noremap = false,
|
noremap = false,
|
||||||
})
|
})
|
||||||
local fallback = keymap.evacuate(0, 'i', '(')
|
feedkeys.call('i', 'nx', function()
|
||||||
vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true)
|
keymap.feed_map(keymap.get_map('i', '('))
|
||||||
|
end)
|
||||||
assert.are.same({ '(' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ '(' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -33,8 +41,9 @@ describe('keymap', function()
|
|||||||
expr = false,
|
expr = false,
|
||||||
noremap = false,
|
noremap = false,
|
||||||
})
|
})
|
||||||
local fallback = keymap.evacuate(0, 'i', '(')
|
feedkeys.call('i', 'nx', function()
|
||||||
vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true)
|
keymap.feed_map(keymap.get_map('i', '('))
|
||||||
|
end)
|
||||||
assert.are.same({ '()' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ '()' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -46,8 +55,10 @@ describe('keymap', function()
|
|||||||
expr = true,
|
expr = true,
|
||||||
noremap = false,
|
noremap = false,
|
||||||
})
|
})
|
||||||
local fallback = keymap.evacuate(0, 'i', '<Tab>')
|
feedkeys.call('i', 'n', function()
|
||||||
vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true)
|
keymap.feed_map(keymap.get_map('i', '<Tab>'))
|
||||||
|
end)
|
||||||
|
feedkeys.call('', 'x')
|
||||||
assert.are.same({ 'foobar' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ 'foobar' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
it('false', function()
|
it('false', function()
|
||||||
@@ -55,8 +66,9 @@ describe('keymap', function()
|
|||||||
expr = true,
|
expr = true,
|
||||||
noremap = false,
|
noremap = false,
|
||||||
})
|
})
|
||||||
local fallback = keymap.evacuate(0, 'i', '<Tab>')
|
feedkeys.call('i', 'nx', function()
|
||||||
vim.api.nvim_feedkeys('i' .. keymap.t(fallback.keys), fallback.mode .. 'x', true)
|
keymap.feed_map(keymap.get_map('i', '<Tab>'))
|
||||||
|
end)
|
||||||
assert.are.same({ '\taiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ '\taiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ misc.id = setmetatable({
|
|||||||
group = {},
|
group = {},
|
||||||
}, {
|
}, {
|
||||||
__call = function(_, 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
|
misc.id.group[group] = misc.id.group[group] + 1
|
||||||
return misc.id.group[group]
|
return misc.id.group[group]
|
||||||
end,
|
end,
|
||||||
|
|||||||
Reference in New Issue
Block a user