Improve keymap (#768)
* Improve keymap * improve * rename * Add tests * Use nightly for testing * Enable failing test * Add manual test case
This commit is contained in:
1
.github/workflows/integration.yaml
vendored
1
.github/workflows/integration.yaml
vendored
@@ -26,6 +26,7 @@ jobs:
|
|||||||
- name: Setup neovim
|
- name: Setup neovim
|
||||||
uses: rhysd/action-setup-vim@v1
|
uses: rhysd/action-setup-vim@v1
|
||||||
with:
|
with:
|
||||||
|
version: nightly
|
||||||
neovim: true
|
neovim: true
|
||||||
|
|
||||||
- name: Setup lua
|
- name: Setup lua
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ async.throttle = function(fn, timeout)
|
|||||||
stop = function()
|
stop = function()
|
||||||
time = nil
|
time = nil
|
||||||
timer:stop()
|
timer:stop()
|
||||||
end
|
end,
|
||||||
}, {
|
}, {
|
||||||
__call = function(self, ...)
|
__call = function(self, ...)
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
local cache = require('cmp.utils.cache')
|
|
||||||
local misc = require('cmp.utils.misc')
|
local misc = require('cmp.utils.misc')
|
||||||
local api = require('cmp.utils.api')
|
local api = require('cmp.utils.api')
|
||||||
|
|
||||||
@@ -105,14 +104,9 @@ keymap.listen = function(mode, lhs, callback)
|
|||||||
local fallback = keymap.fallback(bufnr, mode, existing)
|
local fallback = keymap.fallback(bufnr, mode, existing)
|
||||||
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
|
||||||
vim.api.nvim_feedkeys(fallback.keys, 'it' .. (fallback.noremap and 'n' or 'm'), true)
|
fallback()
|
||||||
else
|
else
|
||||||
callback(
|
callback(lhs, misc.once(fallback))
|
||||||
lhs,
|
|
||||||
misc.once(function()
|
|
||||||
vim.api.nvim_feedkeys(fallback.keys, 'it' .. (fallback.noremap and 'n' or 'm'), true)
|
|
||||||
end)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end, {
|
end, {
|
||||||
expr = false,
|
expr = false,
|
||||||
@@ -121,76 +115,57 @@ keymap.listen = function(mode, lhs, callback)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---Fallback existing mapping.
|
---Fallback
|
||||||
--- NOTE:
|
keymap.fallback = function(bufnr, mode, map)
|
||||||
--- In insert-mode, we should all mapping fallback to the `<Plug>` because `<C-r>=` will display gabage message.
|
return function()
|
||||||
--- In cmdline-mode, we shouldn't re-map as `<Plug>` because `cmap <Tab> <Plug>(map-to-tab)` will broke native behavior.
|
|
||||||
--- We should resolve recursive mapping because existing mapping will feed by `feedkeys` that doesn't solve recursive mapping.
|
|
||||||
--- We use `<C-r>=` to solve recursive mapping.
|
|
||||||
---@param map table
|
|
||||||
keymap.fallback = 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.fallback:%s)', misc.id('cmp.utils.keymap.fallback'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
if map.expr then
|
if map.expr then
|
||||||
keymap.set_map(bufnr, mode, fallback, function()
|
local fallback_expr = string.format('<Plug>(cmp.u.k.fallback_expr:%s)', map.lhs)
|
||||||
local lhs = keymap.t(map.lhs)
|
keymap.set_map(bufnr, mode, fallback_expr, function()
|
||||||
local rhs = (function()
|
return keymap.solve(bufnr, mode, map).keys
|
||||||
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, {
|
end, {
|
||||||
expr = true,
|
expr = true,
|
||||||
noremap = map.noremap,
|
noremap = map.noremap,
|
||||||
script = map.script,
|
script = map.script,
|
||||||
silent = mode ~= 'c',
|
|
||||||
nowait = map.nowait,
|
|
||||||
})
|
|
||||||
elseif mode ~= 'c' or map.callback 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,
|
|
||||||
callback = map.callback,
|
|
||||||
noremap = map.noremap,
|
|
||||||
script = map.script,
|
|
||||||
silent = mode ~= 'c' and map.silent,
|
|
||||||
nowait = map.nowait,
|
nowait = map.nowait,
|
||||||
|
silent = map.silent and mode ~= 'c',
|
||||||
})
|
})
|
||||||
|
vim.api.nvim_feedkeys(keymap.t(fallback_expr), 'itm', true)
|
||||||
else
|
else
|
||||||
local lhs = keymap.t(map.lhs)
|
if map.callback then
|
||||||
local rhs = keymap.t(map.rhs)
|
map.callback()
|
||||||
if not map.noremap then
|
else
|
||||||
rhs = keymap.recursive(lhs, rhs)
|
local solved = keymap.solve(bufnr, mode, map)
|
||||||
|
vim.api.nvim_feedkeys(solved.keys, solved.mode, true)
|
||||||
end
|
end
|
||||||
return { keys = rhs, noremap = map.noremap }
|
|
||||||
end
|
end
|
||||||
return { keys = keymap.t(fallback), noremap = false }
|
end
|
||||||
end,
|
end
|
||||||
})
|
|
||||||
|
|
||||||
---Solve recursive mapping.
|
---Solve
|
||||||
---@param lhs string
|
keymap.solve = function(bufnr, mode, map)
|
||||||
---@param rhs string
|
local lhs = keymap.t(map.lhs)
|
||||||
---@return string
|
local rhs
|
||||||
keymap.recursive = function(lhs, rhs)
|
if map.expr then
|
||||||
if string.find(rhs, lhs, 1, true) == 1 then
|
rhs = map.callback and map.callback() or vim.api.nvim_eval(keymap.t(map.rhs))
|
||||||
local expr = string.format(keymap.t('<C-r>=v:lua.vim.json.decode(%s)<CR>'), vim.fn.string(vim.json.encode(keymap.t(lhs))))
|
else
|
||||||
return string.gsub(rhs, '^' .. vim.pesc(lhs), expr)
|
rhs = keymap.t(map.rhs)
|
||||||
end
|
end
|
||||||
return rhs
|
|
||||||
|
if map.noremap then
|
||||||
|
return { keys = rhs, mode = 'itn' }
|
||||||
|
end
|
||||||
|
|
||||||
|
if string.find(rhs, lhs, 1, true) == 1 then
|
||||||
|
local recursive = string.format('<Plug>(cmp.u.k.recursive:%s)', lhs)
|
||||||
|
keymap.set_map(bufnr, mode, recursive, lhs, {
|
||||||
|
noremap = true,
|
||||||
|
script = map.script,
|
||||||
|
nowait = map.nowait,
|
||||||
|
silent = map.silent and mode ~= 'c',
|
||||||
|
})
|
||||||
|
return { keys = keymap.t(recursive) .. string.gsub(rhs, '^' .. vim.pesc(lhs), ''), mode = 'itm' }
|
||||||
|
end
|
||||||
|
return { keys = rhs, mode = 'itm' }
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get map
|
---Get map
|
||||||
|
|||||||
@@ -37,46 +37,102 @@ describe('keymap', function()
|
|||||||
describe('fallback', function()
|
describe('fallback', function()
|
||||||
before_each(spec.before)
|
before_each(spec.before)
|
||||||
|
|
||||||
local keys = function(keys, mode)
|
local run_fallback = function(keys, fallback)
|
||||||
local state = {}
|
local state = {}
|
||||||
feedkeys.call(keys, mode, function()
|
feedkeys.call(keys, '', function()
|
||||||
|
fallback()
|
||||||
|
end)
|
||||||
|
feedkeys.call('', '', function()
|
||||||
if api.is_cmdline_mode() then
|
if api.is_cmdline_mode() then
|
||||||
state.buffer = { api.get_current_line() }
|
state.buffer = { api.get_current_line() }
|
||||||
else
|
else
|
||||||
state.buffer = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
state.buffer = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
||||||
end
|
end
|
||||||
state.cursor = api.get_cursor()
|
state.cursor = api.get_cursor()
|
||||||
state.wildmenumode = vim.fn.wildmenumode() == 1
|
|
||||||
end)
|
end)
|
||||||
feedkeys.call('', 'x')
|
feedkeys.call('', 'x')
|
||||||
return state
|
return state
|
||||||
end
|
end
|
||||||
|
|
||||||
it('recursive', function()
|
describe('basic', function()
|
||||||
|
it('<Plug>', function()
|
||||||
|
vim.api.nvim_buf_set_keymap(0, 'i', '<Plug>(pairs)', '()<Left>', { noremap = true })
|
||||||
|
vim.api.nvim_buf_set_keymap(0, 'i', '(', '<Plug>(pairs)', { noremap = false })
|
||||||
|
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
||||||
|
local state = run_fallback('i', fallback)
|
||||||
|
assert.are.same({ '()' }, state.buffer)
|
||||||
|
assert.are.same({ 1, 1 }, state.cursor)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('<C-r>=', function()
|
||||||
|
vim.api.nvim_buf_set_keymap(0, 'i', '(', '<C-r>="()"<CR><Left>', {})
|
||||||
|
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
||||||
|
local state = run_fallback('i', fallback)
|
||||||
|
assert.are.same({ '()' }, state.buffer)
|
||||||
|
assert.are.same({ 1, 1 }, state.cursor)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('callback', function()
|
||||||
|
vim.api.nvim_buf_set_keymap(0, 'i', '(', '', {
|
||||||
|
callback = function()
|
||||||
|
vim.api.nvim_feedkeys('()' .. keymap.t('<Left>'), 'int', true)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
||||||
|
local state = run_fallback('i', fallback)
|
||||||
|
assert.are.same({ '()' }, state.buffer)
|
||||||
|
assert.are.same({ 1, 1 }, state.cursor)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('expr-callback', function()
|
||||||
|
vim.api.nvim_buf_set_keymap(0, 'i', '(', '', {
|
||||||
|
expr = true,
|
||||||
|
noremap = false,
|
||||||
|
silent = true,
|
||||||
|
callback = function()
|
||||||
|
return '()' .. keymap.t('<Left>')
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
||||||
|
local state = run_fallback('i', fallback)
|
||||||
|
assert.are.same({ '()' }, state.buffer)
|
||||||
|
assert.are.same({ 1, 1 }, state.cursor)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- it('cmdline default <Tab>', function()
|
||||||
|
-- local fallback = keymap.fallback(0, 'c', keymap.get_map('c', '<Tab>'))
|
||||||
|
-- local state = run_fallback(':', fallback)
|
||||||
|
-- assert.are.same({ '' }, state.buffer)
|
||||||
|
-- assert.are.same({ 1, 0 }, state.cursor)
|
||||||
|
-- end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('recursive', function()
|
||||||
|
it('non-expr', function()
|
||||||
vim.api.nvim_buf_set_keymap(0, 'i', '(', '()<Left>', {
|
vim.api.nvim_buf_set_keymap(0, 'i', '(', '()<Left>', {
|
||||||
expr = false,
|
expr = false,
|
||||||
noremap = false,
|
noremap = false,
|
||||||
silent = true,
|
silent = true,
|
||||||
})
|
})
|
||||||
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
||||||
local state = keys('i' .. fallback.keys, fallback.noremap and 'n' or 'm')
|
local state = run_fallback('i', fallback)
|
||||||
assert.are.same({ '()' }, state.buffer)
|
assert.are.same({ '()' }, state.buffer)
|
||||||
assert.are.same({ 1, 1 }, state.cursor)
|
assert.are.same({ 1, 1 }, state.cursor)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('recursive expr', function()
|
it('expr', function()
|
||||||
vim.api.nvim_buf_set_keymap(0, 'i', '(', '"()<Left>"', {
|
vim.api.nvim_buf_set_keymap(0, 'i', '(', '"()<Left>"', {
|
||||||
expr = true,
|
expr = true,
|
||||||
noremap = false,
|
noremap = false,
|
||||||
silent = true,
|
silent = true,
|
||||||
})
|
})
|
||||||
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
||||||
local state = keys('i' .. fallback.keys, fallback.noremap and 'n' or 'm')
|
local state = run_fallback('i', fallback)
|
||||||
assert.are.same({ '()' }, state.buffer)
|
assert.are.same({ '()' }, state.buffer)
|
||||||
assert.are.same({ 1, 1 }, state.cursor)
|
assert.are.same({ 1, 1 }, state.cursor)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('recursive callback', function()
|
it('expr-callback', function()
|
||||||
pcall(function()
|
pcall(function()
|
||||||
vim.api.nvim_buf_set_keymap(0, 'i', '(', '', {
|
vim.api.nvim_buf_set_keymap(0, 'i', '(', '', {
|
||||||
expr = true,
|
expr = true,
|
||||||
@@ -87,12 +143,13 @@ describe('keymap', function()
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))
|
||||||
local state = keys('i' .. fallback.keys, fallback.noremap and 'n' or 'm')
|
local state = run_fallback('i', fallback)
|
||||||
assert.are.same({ '()' }, state.buffer)
|
assert.are.same({ '()' }, state.buffer)
|
||||||
assert.are.same({ 1, 1 }, state.cursor)
|
assert.are.same({ 1, 1 }, state.cursor)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
describe('realworld', function()
|
describe('realworld', function()
|
||||||
before_each(spec.before)
|
before_each(spec.before)
|
||||||
@@ -113,16 +170,18 @@ describe('keymap', function()
|
|||||||
assert.are.same({ 'aiueo', 'aiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
assert.are.same({ 'aiueo', 'aiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- it('#744', function()
|
it('#744', function()
|
||||||
-- vim.api.nvim_buf_set_keymap(0, 'i', '<C-r>', 'recursive', {
|
vim.api.nvim_buf_set_keymap(0, 'i', '<C-r>', 'recursive', {
|
||||||
-- noremap = true
|
noremap = true,
|
||||||
-- })
|
})
|
||||||
-- vim.api.nvim_buf_set_keymap(0, 'i', '<CR>', '<CR>recursive', {
|
vim.api.nvim_buf_set_keymap(0, 'i', '<CR>', '<CR>recursive', {
|
||||||
-- noremap = false
|
noremap = false,
|
||||||
-- })
|
})
|
||||||
-- keymap.listen('i', '<CR>', function(_, fallback) fallback() end)
|
keymap.listen('i', '<CR>', function(_, fallback)
|
||||||
-- feedkeys.call(keymap.t('i<CR>'), 'tx')
|
fallback()
|
||||||
-- assert.are.same({ '', 'recursive' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
end)
|
||||||
-- end)
|
feedkeys.call(keymap.t('i<CR>'), 'tx')
|
||||||
|
assert.are.same({ '', 'recursive' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user