3

I'm trying to transfer over from vim to Emacs. I'm using evil to emulate vim modal editing. In vim, I've remapped jk to get me out of insert mode and back into normal mode using inoremap jk <esc>. At the moment, I'm using

(key-chord-define evil-insert-state-map "jk" 'evil-normal-state)
(key-chord-define evil-replace-state-map "jk" 'evil-normal-state)

to do this in Emacs.

However, when I'm in insert mode and want to type, say, jump, I end up with just mp, and j u is undefined appears under the mode bar.

Is there any way to fix this behaviour? Preferably, the solution would even allow me to type jk by typing j, waiting, and then typing k (like in vim).


UPDATE

To clarify, I was only using key-chord as a temporary solution to get me into normal mode; I am very willing to accept any solution that fixes the problem, regardless of whether it involves key-chord.

2
  • This shouldn't happen in the first place with key-chord.
    – wasamasa
    Commented Jan 31, 2016 at 10:19
  • (key-chord-define evil-insert-state-map "jk" 'evil-normal-state) let's you escape from insert mode.
    – ReneFroger
    Commented Feb 3, 2016 at 9:38

4 Answers 4

4

FWIW, I cannot reproduce the condition as reported above. It would be helpful if you could share a minimal configuration that can reproduce this error of yours.

An alternative way of solving this is writing a command bound to the initial key of the escape sequence that waits with a timeout for another event to occur. If this event happens in a timely manner and matches the final key of the escape sequence, execute the command to exit insert state. If the timeout is met, but the final key is wrong, insert the initial key and the different one. If the timeout is missed, insert the first key.

(defun my-jk ()
  (interactive)
  (let* ((initial-key ?j)
         (final-key ?k)
         (timeout 0.5)
         (event (read-event nil nil timeout)))
    (if event
        ;; timeout met
        (if (and (characterp event) (= event final-key))
            (evil-normal-state)
          (insert initial-key)
          (push event unread-command-events))
      ;; timeout exceeded
      (insert initial-key))))

(define-key evil-insert-state-map (kbd "j") 'my-jk)

Another implementation of the above with slightly different semantics can be found in cofi's config.

1
  • The file you like to is far from trivial. Can you add a line reference so we can quickly jump to the section you mention?
    – PythonNut
    Commented Feb 3, 2016 at 5:29
4

You have, in effect, defined j as a prefix key, which means that any key you press after it is looked up in the keymap associated with j. Since u is undefined after j you get the behavior you see.

A key cannot be bound to two different commands. In particular, it cannot be bound to both self-insert-command (or equivalent), which inserts a character, and bound to a keymap (acts as a prefix key).

What you can do instead is to bind j to a command that reads a character and does something different depending on what that character is: If it is k then do evil-normal-state. If it is any other character than k then it inserts that character.

Something like this (but you would bind the key in some Evil mode, no doubt, not globally):

(defun my-j ()
  "Read a char.  If `k' then invoke `evil-normal-state'.  Else insert it."
  (interactive)
  (let ((ch  (read-char-exclusive)))
    (if (eq ?k ch)
        (message "Invoke EVIL-NORMAL-STATE HERE") ;; (evil-normal-state)
      (insert "j" (string ch)))))

(global-set-key "j" 'my-j)
5
  • This can be done significantly better by taking a timeout into account, see this question for a code example.
    – wasamasa
    Commented Jan 31, 2016 at 10:20
  • Also, the first part of the explanation does not apply as this is not regular define-key, but key-chord-define...
    – wasamasa
    Commented Jan 31, 2016 at 10:21
  • @wasamasa; could you put this into an answer?
    – digitalis_
    Commented Feb 1, 2016 at 18:13
  • Neither of these answer the question as is, so I won't until it changes to reflect that you're no longer interested in using key-chord to escape from Evil's insert state.
    – wasamasa
    Commented Feb 1, 2016 at 23:32
  • @wasamasa; I've updated the question accordingly.
    – digitalis_
    Commented Feb 2, 2016 at 16:47
0

(key-chord-define evil-insert-state-map "jk" 'evil-normal-state) let's you escape from insert mode.

0

I am using General.el package for this, with following config.

(use-package general
  :config
  (general-evil-setup)
  ;; Exit insert state / mode using jk keybind using general.el package
   (general-imap "j"
    (general-key-dispatch 'self-insert-command
   :timeout 0.25
   "k" 'evil-normal-state))

GL

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.