Is there a way to switch Bash or zsh from Emacs mode to vi mode with a keystroke?
Asked Answered
M

4

12

I'd like to be able to switch temporarily from emacs mode to vi mode, since vi mode is sometimes better, but I'm usually half-way through typing something before I realize I want to use vi mode.

I don't want to switch permanently to vi mode, because I normally prefer emacs mode on the command line, mostly because it's what I'm used to, and over the years many of the keystrokes have become second nature. (As an editor I generally use emacs in viper mode, so that I can use both vi and emacs keystrokes, since I found myself accidentally using them in vi all the time, and screwing things up, and because in some cases I find vi keystrokes more memorable and handy, and in other cases emacs.)

Moulin answered 14/4, 2010 at 18:49 Comment(0)
M
14

You can create a toggle since the key bindings are separate between vi mode and emacs mode.

$ set -o emacs
$ bind '"\ee": vi-editing-mode'
$ set -o vi
$ bind '"\ee": emacs-editing-mode'

Now Alt-e (or Esc e) will toggle between modes.

Add this somewhere in your definition for PS1 so you have an indicator in your prompt of which mode you're in. It won't show the change immediately when you toggle modes, but it will update when a new prompt is issued.

$(set -o | grep emacs.*on >/dev/null 2>&1 && echo E || echo V)
Meyerhof answered 14/4, 2010 at 22:36 Comment(10)
You sir, are brilliant. However, it's still not quite working in zsh. With a few small adjustments, I can get the mode indicator to work from the commandline: <tt>set -o | grep 'vi.*on' >|/dev/null 2>&1 && echo '[vi]' || echo ''</tt> But my prompt is only changed when I source my .zshrc file. (I haven't tested the mode indicator in bash yet.)Moulin
The Zsh line editor doesn't seem to support 'vi-editing-mode', only 'vi-cmd-mode'. (You can, however, still use 'set -o vi' or 'bindkey -v' to effectively get a full vi mode.) ^xv is bound to vi-cmd-mode in zsh by default. It only lasts for the line it is executed on, and you just hit 'i' to get out of it, which puts you back in emacs mode. In contrast, the bash solution lasts until you switch modes explicitly, and hitting 'i' will put you in vi insert mode.Moulin
If you put the $(command) in the definition of your PS1 variable, the command will be immediately evaluated - thus your prompt is statically defined, and you have to source your ~/.zshrc to regenerate the prompt. If you want your prompt to dynamically display a variable/command, you have to modify the precmd function which Zsh runs before the prompt is displayed. Put this in your zshrc to fix the mode indicator:Collide
function precmd { psvar[1]="$(set -o | grep 'emacs.*on' >/dev/null 2>&1 && echo E || echo V)" }Collide
By the way, I think you're looking for the ZLE mode vi-insert and not vi-editing-mode ;)Collide
@Jabir Ali Ouassou: You can escape the dollar sign (or enclose the command substitution in single quotes) and the command substitution will be evaluated when the prompt is issued rather than when it's defined. Bash requires shopt -s promptvars (which is the default). Zsh requires setopt promptsubst.Meyerhof
@Dennis Williamson: Thanks a lot, I didn't know that was possible!Collide
To make it permanent in which file do I have to put the "set" and "bind" commands?Largent
@dash17291: In ~/.bashrcMeyerhof
When you switch from emacs to vi, you most likely want to end up in vi-command mode. vi-editing-mode takes you into vi-insert mode, which is basically a cut-down version of the mode you were just in. See my answer to move direct to vi-command mode.Truditrudie
T
5

Aha! I looked at the readline source, and found out that you can do this:

 "\M-v": vi-editing-mode
 "\M-e": emacs-editing-mode

There doesn't appear to be a toggle, but that's probably good enough!

For posterity's sake, here's my original answer, which could be useful for people trying to do things for which there is no readline function.

Here's a way you could set it up, clearing the current command line in the process. Not what you want, I know, but maybe it'll help someone else who finds this question. In ~/.inputrc:

"\M-v": "\C-k\C-uset -o vi\C-j" # alt (meta)-v: set vi mode
"\M-e": "\C-k\C-uset -o vi\C-j" # alt (meta)-e: set emacs mode

or to toggle...this should work:

"\M-t": "\C-k\C-u[[ \"$SHELLOPTS\" =~ '\\bemacs\\b' ]] && set -o vi || set -o emacs\C-j"

These are essentially aliases, taken one step farther to map to keys in readline so that you don't have to type an alias name and hit enter.

Trinetta answered 14/4, 2010 at 19:12 Comment(0)
T
4

The following .inputrc lines allow Meta / Alt+E to switch between emacs and vi-insert modes.

Mooshing both j and k simultaneously will take you to vi-command mode.

Note: The only English word with "kj" is "blackjack", no words contain "jk")

set keymap emacs
"\ee": vi-editing-mode
"jk": "\eejk"
"kj": "\eejk"

set keymap vi-insert
"\ee": emacs-editing-mode
"jk": vi-movement-mode
"kj": vi-movement-mode

set keymap vi-command
"\ee": emacs-editing-mode

Note: If you add a binding under keymap emacs to vi-movement-mode to try to switch straight to vi-command mode, the prompt doesn't update if you have show-mode-in-prompt on, hence the above work-around is needed.

Truditrudie answered 16/8, 2016 at 11:18 Comment(1)
This works great when going from emacs -> vi, but once on vi-mode it only works if I am in vi-command-mode, this means that to effectively go from vi -> emacs I need to press Esc Esc eMenke
K
1

I finally found out how to toggle vi and emacs mode with a single key e.g. [alt]+[i] in zsh:

# in the .zshrc
# toggle vi and emacs mode
vi-mode() { set -o vi; }
emacs-mode() { set -o emacs; }
zle -N vi-mode
zle -N emacs-mode
bindkey '\ei' vi-mode              # switch to vi "insert" mode
bindkey -M viins 'jk' vi-cmd-mode  # (optionally) exit to vi "cmd" mode
bindkey -M viins '\ei' emacs-mode  # switch to emacs mode
Karlotta answered 4/12, 2022 at 21:2 Comment(1)
Brilliant! Worked very nicelyMoulin

© 2022 - 2024 — McMap. All rights reserved.