50

Recently I tried Emacs and found Evil helpful to keep my vim custom. I'm used to typing "jj" to return normal mode from insert mode like many Vimers do but don't know how to make it in Evil mode.

I map it like this but seems not correct:

(define-key evil-insert-state-map (kbd "jj") 'evil-normal-state)
limon
  • 907
  • 1
  • 9
  • 19

8 Answers8

47

This works for me. It requires the KeyChord library:

;;Exit insert mode by pressing j and then j quickly
(setq key-chord-two-keys-delay 0.5)
(key-chord-define evil-insert-state-map "jj" 'evil-normal-state)
(key-chord-mode 1)

It is inspired by @phils answer above and based on Simon's Coding Blog: Emacs and Unity Every Day.

E. Sambo
  • 901
  • 9
  • 9
  • hi there sambo. thanks for you answer it works good. but i used to work with "ii" instead of "jj" in vim, so i checked it with above code. but it does not work. i am new to emacs and lisp and i am kind of confusing about this. i check some other function like add hook but they also does not work. could you please help me to find the reason? – AMIR REZA SADEQI Apr 05 '20 at 22:37
  • @AMIRREZASADEQI you have to install key-chord and then put the code in your Emacs init file (~/.emacs or ~/.emacs.d/init.el) – Pascal Jun 23 '20 at 07:54
  • The Key-chord GitHub repo is [here](https://github.com/emacsorphanage/key-chord). – jdhao Oct 12 '21 at 09:27
25

I don't know whether it works with Evil, but for Emacs in general the KeyChord library is designed for this sort of thing.

Try it and see?

(key-chord-define evil-insert-state-map "jj" 'evil-normal-state)
phils
  • 71,335
  • 11
  • 153
  • 198
  • 2
    Yep, I can confirm that works. It's what I use and I find it works more smoothly than the accepted answer. – Kris Jenkins Feb 16 '13 at 21:26
  • Nice! Worked like a charm! I say evil-mode should depend on this library and bring that functionality into evil since that's how vim behaves. – Nacht Nov 19 '14 at 16:51
19

If you're using Spacemacs then I just found that this setting (added to the beginning of user-init) works very well,

(setq-default evil-escape-key-sequence "jj")

Caoilte
  • 2,381
  • 1
  • 21
  • 27
  • 5
    This is actually part of the [evil-escape](https://github.com/syl20bnr/evil-escape) package, so you don't need all of spacemacs. – Long Jun 20 '17 at 21:16
  • 9
    If you're having trouble making this work try adding (setq-default evil-escape-delay 0.2) too. Also, I suggest putting both these lines inside the user-config section of your ~/.spacemacs file instead of the user-init section – David Zorychta Aug 14 '17 at 20:39
14

See this blog post: http://zuttobenkyou.wordpress.com/2011/02/15/some-thoughts-on-emacs-and-vim/ and search for "cofi". I use the "kj" version myself and it works just like Vim.

EDIT: Here is the actual code snippet from the blog post:

(define-key evil-insert-state-map "k" #'cofi/maybe-exit)

(evil-define-command cofi/maybe-exit ()
  :repeat change
  (interactive)
  (let ((modified (buffer-modified-p)))
    (insert "k")
    (let ((evt (read-event (format "Insert %c to exit insert state" ?j)
               nil 0.5)))
      (cond
       ((null evt) (message ""))
       ((and (integerp evt) (char-equal evt ?j))
    (delete-char -1)
    (set-buffer-modified-p modified)
    (push 'escape unread-command-events))
       (t (setq unread-command-events (append unread-command-events
                          (list evt))))))))
Linus Arver
  • 1,331
  • 1
  • 13
  • 18
6

For my windows install, adding as part of use-package evil configuration worked for me in init.el:

(use-package evil
 :ensure t
 :config
 (evil-mode 1)
 (define-key evil-insert-state-map "jj" 'evil-normal-state)
)

For Ubuntu, I followed E. Sambo's answer.

roger
  • 303
  • 2
  • 8
  • I think this is by far the best answer since is a config from the internals of evil package itself and no need to install another package. – redeemefy May 25 '21 at 14:01
  • but now you can insert `jj` literally into your buffer anymore, which is far from satisfactory. – jdhao Oct 12 '21 at 09:24
0

It's a bit more complicated - you have to watch for the previous character. This should do the trick. (the gist is for "jk", you can easily modify it for "jj" though you will note that "jk" is more efficient/faster).

user673592
  • 2,090
  • 1
  • 22
  • 37
0

This is my own solution i've been using for some time, although i use `jf' actually.

(defun xwl-jj-as-esc ()
  (interactive)
  (if (memq evil-state '(insert replace))
      (let ((changed? (buffer-modified-p)))
          (insert "j")
          (let* ((tm (current-time))
                 (ch (read-key)))
            (if (and (eq ch ?j)
                     (< (time-to-seconds (time-since tm)) 0.5))
                (save-excursion
                  (delete-char -1)
                  (evil-force-normal-state)
                  (set-buffer-modified-p changed?))
              (insert ch))))
    (call-interactively 'evil-next-line)))

(define-key evil-insert-state-map  "j" 'xwl-jj-as-esc)
(define-key evil-replace-state-map "j" 'xwl-jj-as-esc)
xwl
  • 840
  • 10
  • 12
0

Initially I naively shared the answer below which not only it doesn't work (since it "disables" j and k) but also many emacs folks would laugh!

I found that evil-mode provides evil-define-key according to https://evil.readthedocs.io/en/latest/keymaps.html#evil-define-key.

So I tried below and it works for me.

;; you should be able to do "jj" instead but I prefer "jk"
(evil-define-key 'insert 'global (kbd "jk") 'evil-normal-state)

Now I found a real answer that is satisfying to me and working (using https://github.com/noctuid/general.el)

;; Enable installation of packages from MELPA.
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(unless package-archive-contents
  (package-refresh-contents))

;; Download Evil
(unless (package-installed-p 'evil)
  (package-install 'evil))

;; Enable Evil
(require 'evil)
(evil-mode 1)

;; ------------------------------------------------------
;; you might have all the above already 
;; but I still included for the completeness of the code
;; ------------------------------------------------------

;; Download general.el
(unless (package-installed-p 'general)
  (package-install 'general))

;; Enable general.el
(require 'general)
(general-evil-setup)

;; now usable `jk` is possible!
(general-imap "j"
  (general-key-dispatch 'self-insert-command
    :timeout 0.25
    "k" 'evil-normal-state))

since I literally just discovered general.el, this code might not be optimal

Heechul Ryu
  • 381
  • 4
  • 6