Subject: bug#13793: 24.3.50; M-x broken in viper and X

From: Stefan Monnier <[email-masked]>
Date: Tue, 26 Feb 2013 13:12:01 -0500

>> Maybe "enable evil-esc-mode in post-command-hook and disable it in
>> pre-command-hook" might work?
> I'm a little bit afraid of situations where a new binding is defined
> but pre-command-hook has not been called (to restore the original
> definition of `input-decode-map`). For example if a new binding is
> established in a hook or when Emacs starts. If evil is loaded before
> that binding is defined, i.e. input-decode-map is already 'patched',
> then it may fail. Of course one could start with an unpatched
> input-decode-map and wait for the first post-command-hook.

Agreed, using post/pre-command-hook is messy.  Other problems come up
with recursive edits (and minibuffers), where the pre-command-hook can
end up run twice without intervening post-command-hook and
post-command-hook can similarly be run twice without an intervening
pre-command-hook.

> So the question is: is it guaranteed that a post-command-hook will be
> called when Emacs starts and before any user input, and that a call to
> `define-key` will always be preceded by a pre-command-hook and
> followed by a post-command-hook, no matter how it is called?

No, basically with pre/post-command-hook, nothing is guaranteed.

> This includes any possibility to call `define-key` from a hook or
> so.  I just do not have the overview to give a reliable judgement on
> this.  IMO using an advice is more direct and simpler in this
> particular situation, although I really don't like it.

I think what we really care about is to detect "called from
read-key-sequence".  How 'bout:

(defvar evil-normal-esc-map (lookup-key input-decode-map [?\e]))
(define-key input-decode-map
  [?\e] `(menu-item "" ,evil-normal-esc-map
          :filter ,(λ (map)
                     (if (and (not evil-inhibit-escape)
                              (equal (this-single-command-keys) [?\e])
                              (sit-for 0.1))
                         [escape] map))))

So the special ESC=>escape mapping only takes place if the whole
last key-sequence so far is just [?\e], i.e. either we're still in
read-key-sequence, or the last read-key-sequence only read [?\e], which
should ideally never happen because it should have been mapped to [escape].

        Stefan