[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Performing an operation on any other key press

From: Aaron Jensen
Subject: Performing an operation on any other key press
Date: Sun, 24 Mar 2024 13:32:55 -0700


This topic may be more appropriate for the users list, but it involves a technique used in a few Emacs core packages, so I thought this would be the best place to discuss it.

First, some context. I use both Evil and a package called Corfu. Corfu is similar to company-mode in that it is an implementation of auto-complete. Evil has a function called evil-repeat that enables recording sequences of commands and then replaying them, usually with the "." key. Doing this requires a fair bit of machinery that may very well be hacks, as you can imagine. One of the things that it does is it requires commands to be associated with a strategy for recording the outcome of that command. There are a few strategies, but the primary ones that concern this question are the strategy that records the key sequence and the strategy that records buffer changes during the evaluation of the command.

For example, self-insert may record just the key sequence, as replaying it will be deterministic. Another, more sophisticated command (perhaps one that requires user input from the minibuffer) may record buffer changes instead so that replaying can be deterministic.

If that's not clear, please let me know. I know that not everyone here is familiar with how evil and evil-repeat work.

Next, we have Corfu, which has a capability where-in it displays the highlighted completion in the buffer before that completion is truly selected. One of the strategies it implements is, in company, called company-tng (tab and go), where in once that completion is displayed, pressing any key other than a key in corfu's map will cause the completion to be inserted into the buffer (rather than just being displayed as an overlay) and then the key that was pressed will do whatever it typically does. Imagine that key was a ";". In that case, the completion would complete and there would be a ; inserted. 

Corfu does this by adding a pre-command hook. If the corfu menu is displayed, and there is a selection, then that selection is inserted. That's the gist of what the pre-command hook does as far as I know.

The problem comes in when both evil-repeat and Corfu are combined. In that case, because Corfu modifies the buffer during a command in a way that is not repeatable, that specific command would need to be recorded by evil-repeat as "change", but if it is the default keypress recording, it won't be. 

This means that the repeat will only play the keypresses, and not include the text that Corfu inserted.

As far as I know, it wouldn't be a good idea to change evil-repeat to ensure that everything is recorded with the "change" strategy just to support Corfu.

The only thing so far that I have found that works is, within Corfu's pre-command hook, to override this-command to a command that is configured to the "change" strategy, and then copy this-command-keys to the head of unread-command-events (or, effectively, unread the command keys). As far as I can tell, this technique is used in a few places within Emacs (isearch and electric-buffer-menu at least).

My question is, is there any other way to ensure that Corfu can insert its text under the guise of a corfu command and not impact the next command such that evil-repeat (or something like it) can work. Unreading commands seems like a bit of a hack, and the author of Corfu is understandably not interested in implementing that except as a last resort.

It's been mentioned that using menu-items could help, but I fail to see how a conditional menu item could do the thing I'm describing, and it seems like adding side effects in a menu item filter is an even more egregious hack than unreading a command and allowing the command loop to process it anew.

I'd love to understand if there's a better way that evil-repeat could do its job as well. Any and all ideas are welcome.

Here's some additional context if interested:


Thank you,


reply via email to

[Prev in Thread] Current Thread [Next in Thread]