1

In vim one can escape normal mode by pressing Alt or Meta then pretty much any key, e.g. Met-j to escape insert mode and move down a line. Holding alt before typing would send an <Esc> key before the command.

I found it was the fastest was of escaping normal mode and has become muscle memory. I have been researching how to do this with evil mode, but haven't figured it out as there is lots of documentation about how to make evil mode quit various emacs states.

4 Answers 4

2

This trick is a side effect of how terminal emulators work, key combinations with meta are implemented as sending an escape key followed by the letter (unless your terminal emulator is configured to do something else like inserting an accented letter), if you try this trick in gvim, you'll notice it isn't supported there.

Emacs chose to interpret the escape key followed by a letter as an alternative way of typing the meta modifier, in order to support a greater amount of keybindings. This behavior is pretty much hardcoded and cannot be easily worked around. Instead Evil provides \ in normal state to temporarily switch to Emacs state and C-o in insert state to temporarily switch to normal state.

1
  • I used to use it in normal Vim and NeoVim-Qt. The reason I am trying out Emacs is because it apparently so much more configurable. The first thing I try to config I get the answer of its not possible. Hmmm.
    – user27072
    Commented Mar 16, 2020 at 17:56
1

Ended up doing the following: ALT+arrows or ALT+: enters normal mode. ALT+: also asks for execute command:

(defun evil-normal-state-then-ex ()
"Run 'evil-normal-state' and 'evil-ex' in sequence."
  (interactive)
  (evil-normal-state)
  (evil-ex))

(define-key evil-insert-state-map (kbd "M-h") 'evil-normal-state)
(define-key evil-insert-state-map (kbd "M-j") 'evil-normal-state)
(define-key evil-insert-state-map (kbd "M-k") 'evil-normal-state)
(define-key evil-insert-state-map (kbd "M-l") 'evil-normal-state)
(define-key evil-insert-state-map (kbd "M-:") 'evil-normal-state-then-ex)
2
  • That doesn't move the cursor after switching to normal state though or does it?
    – wasamasa
    Commented Mar 9, 2020 at 19:28
  • Yes it does move the cursor, and M-: will enter into command mode. Often the next thing I wish to do is move or enter a command after leaving insert mode. Unfortunately things like M-w dont work unless I map out pages of every combination or movement commands.
    – user27072
    Commented Mar 10, 2020 at 13:09
1

I do not understand why you would prefer to have some binding that would switch to normal-state instead of just staying in insert-state, and in that case you can simply bind the normal-state-commands to the key-combo's in insert-state. However, if you insist on having the inconvenient behavior of also switching to normal state (and move your finger to M instead of ESC), then in response to the answer by @wasamama (and your comment there), I would say that creating a workaround isn't that hard. You could create a binding for every key-combo starting with M in insert mode, to a function which runs the command that is bound in the evil-normal-state-map, e.g.:

(defun my-evil-normal-from-insert ()
  (interactive)
  (let ((normal-key (substring (key-description (this-command-keys)) 2)))
    (evil-normal-state)
    (funcall (alist-get (string-to-char normal-key) (cdr evil-motion-state-map)))))

If you don't want to exit from insert-state, then you could comment out evil-normal-state and have the arguably more convenient behavior.

However, many normal-state bindings (like those of j,k,h,l) are actually defined in the evil-motion-state-map, so if you only require such commands, then you could simply replace the map in the function above.

If you would like to 'remap' all keys after M with commands that can be bound in either the evil-motion-state-map or the evil-normal-state-map then you could use the following function:

(defun my-evil-normal-from-insert ()
  (interactive)
  (let ((maps (list evil-motion-state-map evil-normal-state-map))
        (normal-key (substring (key-description (this-command-keys)) 2))
        (continue t))
    (while (and maps continue)
      (when-let (command (alist-get (string-to-char normal-key) (cdar maps)))
        ;; (evil-normal-state)
        (funcall command)
        (setq continue nil))
      (setq maps (cdr maps)))))

Then you can define the bindings for the insert-state keys that you need individually, or you could create a loop to bind most/all of them. Here follows an example for such a loop, where I have not checked if all created bindings make sense:

(let* ((letters "qwertyuiopasdfghjklzxcvbnm")
       (capitals (upcase letters))
       (others "1234567890-=[];',./!@#$%^&*()_+{}|:<>?")
       (c-list (split-string (concat letters capitals others) "")))
  (dolist (l c-list)
    (evil-global-set-key 'insert (kbd (concat "M-" l)) #'my-evil-normal-from-insert)))

You could extend the loop if you would like to bind even more options.

I am almost sure that there must be more elegant ways to achieve this. However, I think this example already shows how 'hackable' Emacs is, by showing how often Emacs can still do things, despite those things 'being hard-coded' not to work in a certain way.

-1

You can install the package evil-escape from MELPA. It does what you need.

2
  • From what I understand the package define more state to escape from. The trouble I am having is how to define the the key combo M-???? to be <esc> then ????? when in insert mode.
    – user27072
    Commented Mar 4, 2020 at 15:51
  • No, it doesn't. The package is designed to give you an alternative way of exiting insert mode, whereas this is about temporarily suspending insert state.
    – wasamasa
    Commented Mar 4, 2020 at 16:57

Your Answer

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