Modify repeat keys?

Frank Fischer frank.fischer at
Thu Aug 11 10:59:40 CEST 2011

On Thu, Aug 11, 2011 at 01:56:05AM +0200, Michael Markert wrote:
> Hi,
> I'm using this function to exit insert state with jk

You found a point where the evil already has an answer ;)

The following code should work (I removed the lambda-trick because you need 'evil-define-command' so this is easier).

#+begin_src emacs-lisp
(define-key evil-insert-state-map "j" #'cofi/maybe-exit)

(evil-define-command cofi/maybe-exit ()
  :repeat change
  (let ((modified (buffer-modified-p)))
    (insert "j")
    (let ((evt (read-event (format "Insert %c to exit insert state" ?k)
			   nil 0.5)))
       ((null evt) (message ""))
       ((and (integerp evt) (char-equal evt ?k))
	(delete-char -1)
	(set-buffer-modified-p modified)
	(push 'escape unread-command-events))
       (t (setq unread-command-events (append unread-command-events
					      (list evt))))))))
#+end_src emacs-lisp

The only difference is the keyword ':repeat change' and instead of
switching back to normal mode the 'escape event resend (given that
this event still exits insert-mode, otherwise you need more work).

Let me explain how this works. Evil knows two different ways of
recording changes. The first simpler way is to record the key-strokes,
which is the default for (almost) all commands. This is usually what
you want and this approach handles some cases like automatic
identation very well. But your function is ... evil. Its behaviour
depends on time and therefore the function is not recordable as
key-strokes. Even if the switch back to normal-state would work, it
would be impossible to repeat the two characters "jk" in some word,
because when repeated those two commands would always follow very
quickly and therefore exit insert-state, no matter how big the pause
between the two original key-strokes has been. The same problem
arises, e.g., for some auto-completion packages which popup possible
completions after some time. For these situations Evil has a second
way to record repeation, namely by buffer changes. Instead of
recording the key-sequences the buffer changes itself are recorded
(and the key-strokes are dropped). The keyword ':repeat change' makes
Evil repeat your function by change instead of by key-strokes. This
means when "jk" is repeated the function cofi/maybe-exit is never
called again. Instead the buffer change itself is replayed, which is
either the insertion of "jk" or the insertion and deletion of "j".
Furthermore, if "jk" is used to exit insert-state, the extra event
'escape is appended to the repeat-information (i.e., the whole
repeat-information for the insertion consists of the command to start
insert-state, some keys that are inserted literally, some buffer
changes caused by "jk" and finally a key-stroke sequence [escape])
which is the same as pressing escape.

I hope this helps.


More information about the implementations-list mailing list