neovim / neovim

Vim-fork focused on extensibility and usability
https://neovim.io
Other
82.73k stars 5.66k forks source link

vim.on_key does not expose a way to only trigger on completion of a valid action #30803

Open xiaoshihou514 opened 1 week ago

xiaoshihou514 commented 1 week ago

Problem

I am trying to collect keystroke statistics like somebody did in emacs, however, there is no way to make actions like ci[ to be sent to vim.on_key together. Instead, the function will be triggered 3 times, with c, i and [.

Expected behavior

The ability to write

vim.on_key(f, {
  ns = nil,
  on_action_complete = true
})

... and get ci[ sent to f!

bew commented 1 week ago

You might have a chance using the SafeState autocommand, that is triggered when neovim finished something and is now in a safe state to do something else. You'd still have to collect each keys manually though 🤔

Note that this is how this multicursor plugin from @jake-stewart works AFAIK (collect, wait for safe state, propagate last inputs to other cursors if it makes sense)

justinmk commented 1 week ago

as mentioned in https://github.com/neovim/neovim/issues/30741#issuecomment-2404867582 , this is not implemented yet.

Part of the work to implement "multicursor" support will be to "atomize" input so that its structure is exposed. That seems like a requirement for this, otherwise you end up re-implementing a mapping parser in on_key (which doesn't know if a key will resolve to a mapping / builtin normal command)?

jake-stewart commented 1 week ago

You might have a chance using the SafeState autocommand, that is triggered when neovim finished something and is now in a safe state to do something else. You'd still have to collect each keys manually though 🤔

Note that this is how this multicursor plugin from @jake-stewart works AFAIK (collect, wait for safe state, propagate last inputs to other cursors if it makes sense)

It's far from perfect.

  1. I have to manually filter out keys fed via vim.fn.feedkeys and vim.api.nvim_feedkeys.
  2. Repeating an overlapped mapping will stop vim from entering a safe state. For example, map lj to something and press l three times. Despite the l mapping executing twice, vim never had a chance to enter a safe state.
xiaoshihou514 commented 1 week ago

Hmm, vim.fn.state("o") might help me here, but it's still very vulnerable to internal changes.