How do I switch to vi editing mode in readline?

I want to switch to vi editing mode in a readline environment. But I don’t want to use ‘set -o vi’. I want to temporarily switch using a keyboard shortcut. The man page says I can do this with M-C-j. But that doesn’t work for me.

I’m using Ubuntu and an xterm. Doesn’t work under gnome-terminal either.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

I’d confirm that the keyboard mapping Meta+Control+j is in fact correct on your system. You can use this command to list all the keybinds for the various modes of Bash. On my system there wasn’t a keybinding either.

$ bind -P| grep edit
edit-and-execute-command can be found on "C-xC-e".
emacs-editing-mode is not bound to any keys
vi-editing-mode is not bound to any keys

You can do the following so that when you type Esc+e it will toggle between the 2 modes.

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

The bind command now shows this:

in vi mode

$ bind -P |grep edit
edit-and-execute-command is not bound to any keys
emacs-editing-mode can be found on "ee".
vi-editing-mode is not bound to any keys

in emacs mode

$ bind -P |grep edit
edit-and-execute-command can be found on "C-xC-e".
emacs-editing-mode is not bound to any keys
vi-editing-mode can be found on "ee".

Now you can use Esc+e to toggle between the 2 different modes.

Method 2

Bash explicitly disables this and a few other Readline shortcuts. See the initialize_readline() function in the bash source code (http://www.catonmat.net/download/bashline.c):

   /* In Bash, the user can switch editing modes with "set -o [vi emacs]",
      so it is not necessary to allow C-M-j for context switching.  Turn
      off this occasionally confusing behaviour. */
   rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
   rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
#if defined (VI_MODE)
  rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
#endif

I don’t seem to be able to override this behavior using the Readline configuration file (.inputrc).

Method 3

Here’s what I ended up using for my ~/.inputrc, based on slm’s answer.

set show-mode-in-prompt on

set keymap emacs
"ea": vi-editing-mode

set keymap vi-command
"k": history-search-backward
"j": history-search-forward
"z": emacs-editing-mode
"ea": emacs-editing-mode

set keymap vi-insert
"ea": emacs-editing-mode
"C-l": clear-screen
"C-e": end-of-line
"C-k": kill-line

set editing-mode vi

I tried the $if mode= syntax, but I think that is resolved statically (one time, when reading the file), so it doesn’t work as I expected. So we need to switch to each keymap and modify its key bindings, even if previously set on another keymaps. At the end I say which mode I want to start with.

Method 4

I tried to have emacs-styled mappings being used in vi mode.
I ended up with:

set keymap vi-command
"k": history-search-backward
"j": history-search-forward

set keymap vi-insert
"C-A": beginning-of-line
"C-B": backward-char
"C-D": delete-char
"C-E": end-of-line
"C-F": forward-char
"C-K": kill-line
"C-L": clear-screen
"C-N": next-history
"C-P": previous-history
"C-O": operate-and-get-next

# Enable Readline not waiting for additional input when a key is pressed.
# Needed for the mappings below.
set keyseq-timeout 0

# `yank-last-arg` does not work exactly as in emacs mode
"e.": yank-last-arg
"e177": backward-kill-word
"e0": digit-argument
"e1": digit-argument
"e2": digit-argument
"e3": digit-argument
"e4": digit-argument
"e5": digit-argument
"e6": digit-argument
"e7": digit-argument
"e8": digit-argument
"e9": digit-argument
"eb": backward-word
"ec": capitalize-word
"ed": kill-word
"ef": forward-word
"el": downcase-word
"en": non-incremental-forward-search-history
"ep": non-incremental-reverse-search-history
"et": transpose-words
"eu": upcase-word
"ey": yank-pop

# some other useful mappings

"e/": complete-filename
"ek": kill-whole-line
"eo": "C-vC-j"
# quickly switch to "normal" mode
"C-[": vi-movement-mode
# perserve the currently editing line so that we can 
# do something else before restoring it.
"eg": insert-comment
"er": "C-R#C-AC-DC-E"

set editing-mode vi

It is helpful to read the man page for readline and
the READLINE section on the bash man page.

Method 5

This should work in any bash to switch to vi-mode:

Ctrl-a, Ctrl-k, set -o vi, Ctrl-y

Jump to start. Cut to end-of-line. Switch to vi-mode. Yank the line back.

Not the perfect simple hotkey but it will work at any bash without setting anything up first. For me this is also easy to memorize because I use ctrl-a/e and ctrl-k/u all the time. You can also hold down ctrl and type ak.

Related: In particular, I like the W/B vi hotkeys to move between words. This is different from w/b (or emacs m-f/m-b) which treat the following as 4 words not 1: “a/b/c/d” or “a,b:c-d”. It becomes super-fast to move through a huge command line. f' is also useful to jump to the next quote (or any char) and ;/, move between matches.

Interestingly, the Ctrl-y emacs hotkey still works in vi-input-mode.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x