Skip to content

Conversation

@soifou
Copy link
Collaborator

@soifou soifou commented Nov 21, 2025

Revamp keymap handling by moving away from expr mappings to a normalized feedkeys approach, fixing several long-standing edge cases around multi-key mappings, fallbacks, and interoperability with other plugins.

{
    ['jk'] = { 'hide', 'fallback' },
    ['<C-x><C-o>'] = { 'show', 'fallback' },
    ['<Leader>cc'] = { 'show', 'fallback' },
}
vim.keymap.set('i', '<C-i>', function() vim.print('fallback to <C-i>') end, { unique = false })
vim.keymap.set('i', '<Tab>', function() vim.print('fallback to <Tab>') end, { unique = false })
vim.keymap.set('i', '<C-m>', function() vim.print('fallback to <C-m>') end, { unique = false })

{       
    ['<C-i>'] = { 'accept', 'snippet_forward', 'fallback' }, -- when fall back prints "fallback to <C-i>"
    ['<C-m>'] = { 'accept', 'fallback' }, -- when fall back prints "fallback to <C-m>"
}
  • Key composition
{
    ['<C-n>'] = { 'select_next' },
    ['<Tab>'] = {
       function() return '' end, -- would fallback
       function() return '<C-n>' end, -- would call `select_next`
    },
},
  • Recursive keymap
vim.keymap.set('i', '<F2>', '<Esc>:echo "Hello from F2"<CR>a', { noremap = true, silent = true })
{
   ['<Space><Space>'] = { function() return '<F2>' end }, -- would print "Hello from F2"
}
vim.cmd([[
  function! s:MyFunction()
    echo "script local function invoked"
  endfunction

  nnoremap <silent> <SNR>42_mykey :call <SID>MyFunction()<CR>
  nnoremap <silent> <Plug>(MyPlug) :echo "Plug mapping triggered"<CR>
]])

{
    ['<Leader>m'] = { function() return '<Esc><Plug>(MyPlug)a' end }, -- prints "Plug mapping triggered"
}
-  vim.schedule(function() completion_list.accept(opts) end)
-  return true
+  return completion_list.accept(opts)

@soifou

This comment was marked as outdated.

@saghen
Copy link
Owner

saghen commented Nov 21, 2025

Does calling feedkeys have any caveats that we should be aware of? If not, I'd like to adopt it for all the keymaps! Avoiding expr = true would avoid having to vim.schedule anything that edits the buffer, which would be great for scriptability. I don't mind the way you're detecting multi-key mappings but it would be great if there was a less hacky way (maybe replace_keycodes could help?)

@soifou
Copy link
Collaborator Author

soifou commented Nov 22, 2025

Thanks for the feedback, you give me some additional thinking! I'd like to explore this approach a bit more. The feedkeys solution does seem cleaner and we could get rid of multi-key detection if we turn off expr.

Edit: Updated PR description

@soifou soifou changed the title fix(keymap): handle multi-key mappings correctly refactor(keymap): improve fallback and key handling Nov 24, 2025
@soifou soifou marked this pull request as draft November 24, 2025 18:27
@saghen
Copy link
Owner

saghen commented Nov 25, 2025

Wow this is quite a bit of work, you'd end up fixing all the major issues with keymaps! These changes would be breaking, since theyre no longer vim.schedule so it might make sense to artificially vim.schedule or merge this into a new v2 branch.

Fyi, I got a bit ambitious with v2 so I'm going to reduce its scope. Just planning some light refactoring, these keymap changes, source-per-LSP, adopt blink.lib, and the improved API (for keymaps, enable, vim.g, dynamic config, etc)

@soifou
Copy link
Collaborator Author

soifou commented Nov 25, 2025

Yeah, as long as I'm here, I might as well fix these issues once and for all. I've had promising results locally. I don't mind working on this for v2, but last time I switched to this branch, everything was so broken that it was hard to make any convincing commits. Feel free to keep in touch on Matrix to coordinate!

This change introduces:
- multi-key sequence support (e.g. `<C-x><C-o>`)
- normalized key comparison and equivalence (`<C-i>` == `<Tab>`)
- key composition from user callbacks
- fallback handling, including `<Plug>`, `<SID>`, and script mappings
- automatic reapplication of missing (stolen) buffer keymaps

Keymaps are now buffer-local for all modes, rely on normalized
key notation instead of termcodes, and execute commands eagerly
until one succeeds.

BREAKING CHANGE: Keymap execution semantics, fallback behavior, and
mapping scope have changed. expr mappings are no longer used.
@soifou soifou force-pushed the fix/keymap-multikey branch from fec6db0 to c855892 Compare December 22, 2025 13:48
@soifou soifou changed the title refactor(keymap): improve fallback and key handling feat(keymap)!: replace expr keymaps with normalized feedkeys execution Dec 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants