Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does "set keymap vi" do?

Tags:

bash

shell

I wanted vim-like navigation for my terminal, so I added:

set editing-mode vi
set keymap vi

To my .inputrc file based on this. editing-mode vi adds vi navigation. What does keymap vi do, and why do I need it?

like image 573
Vlad the Impala Avatar asked Apr 03 '12 22:04

Vlad the Impala


People also ask

What does set VI mean?

While Unix/Linux systems are case sensitive, you can set vi to ignore case by using the :set ignorecase setting. This can be extremely useful when you have no idea whether the text you are searching for will be capitalized or not. And, as for reverting this setting, the process is much the same as other settings.

What is set vi in Linux?

The Vi application is the default text editor on most Linux systems, so it's the primary interface you will use when you need to edit a configuration file. If you're used to a graphical text editor, such as Notepad++ or VS Code, Vi can be confusing at first.


2 Answers

TL;DR

If you don't want to change/add bindings in the default keymaps, you don't need the line keymap vi.

What keymap vi does is state that any bindings listed after that point apply to that keymap (which is exactly the same keymap as vi-command and vi-move).

If you want to change the insertion keymap (eg to add a Ctrl-A binding to go the beginning of the line while you're typing), you'll need to do this below a keymap vi-insert line.

If you want further info on the vi mode and maps, skip to the heading editing-mode vi (the last one).

But wait! There's a fair bit of background info that may be needed though: eg, the difference between an editing-mode and a keymap.

Particularly useful is the concept of a hybrid emacs keymap for inserting text and while still easily getting to vi-command for making changes.

What is the difference between an editing-mode and a keymap?

There are only two editing-modes: emacs (the default) and vi.

The GNU Readline Library documentation says:

editing-mode
    The editing-mode variable controls which default set of key bindings is
    used. By default, Readline starts up in Emacs editing mode, where the
    keystrokes are most similar to Emacs. This variable can be set to either
    `emacs' or `vi'.

Note the difference between editing-mode and keymap: In editing-mode vi the two (yes there's only two, read on) keymaps are swapped in and out to emulate the different modes of the vi editor. ALL the emacs ones operate at the same time in editing-mode emacs (explained later).

So what does editing-mode actually do? It just sets the active keymap upon shell startup to either emacs or vi-insert.

What are the unique keymaps?

Acceptable keymap names are emacs, emacs-standard, emacs-meta, emacs-ctlx,
vi, vi-move, vi-command, and vi-insert.

vi is equivalent to vi-command; emacs is equivalent to emacs-standard.

While not documented, vi/vi-command and vi-move keymaps are also equivalent:

+ravi@boxy:~$ diff <(bind -pm vi) <(bind -pm vi-move)
+ravi@boxy:~$ 

This leaves us with: emacs, emacs-meta, emacs-ctlx, vi, and vi-insert as unique keymaps to explain. Differentiating the keymaps is probably best done by inspecting them...

What are the keymaps default bindings?

To view the default keybindings for (example) emacs (the default), use:

INPUTRC=~/dev/null bash -c 'bind -pm emacs' | grep -v '^#

You can replace emacs with any other keymap name in the example above.

There are many lines saying self-insert or do-lowercase-version which aren't very useful, so to remove them:

INPUTRC=~/dev/null bash -c 'bind -pm emacs' | grep -vE '^#|: (do-lowercase-version|self-insert)$' | sort

What is the difference between the various emacs keymaps?

TL;DR: They are different views on a single set of mappings applied to editing-mode emacs.

If you the output of the second command into the files called emacs-standard, emacs-meta, emacs-ctlx, vi-command, and vi-insert for their corresponding keymaps, you can find out that:

There are NO commands mapped in emacs-meta and emacs-ctlx which don't also appear in emacs-standard:

$ comm -13 <(sed -r 's/.*: (\S+)/\1/' emacs-standard|sort) <(sed -r 's/.*: (\S+)/\1/' emacs-ctlx|sort)
$ comm -13 <(sed -r 's/.*: (\S+)/\1/' emacs-standard|sort) <(sed -r 's/.*: (\S+)/\1/' emacs-meta|sort)
$

So emacs/emacs-standard is a behaviourally functional superset of both emacs-ctlx and emacs-meta This means that:

keymap emacs
"\eg": glob-expand-word
"\C-x\C-r": re-read-init-file

Is functionally equivalent to:

keymap emacs-meta
"g": glob-expand-word

keymap emacs-ctlx
"\C-r": re-read-init-file

You might argue that the second form is easier to read.

Inserting text: emacs vs vi-insert

There are 28 commands in emacs-standard not in vi-insert

+ravi@boxy:~/lib/readline$ comm -12 vi-insert emacs-standard |wc -l
28
+ravi@boxy:~/lib/readline$

emacs/emacs-standard is basically a superset of vi-insert. So for typing text, it's best to use the emacs-standard keymap over vi-insert as long as you can easily switch between emacs and vi-command.

The only additional bindings in vi-insert not in emacs-standard are:

+ravi@boxy:~/lib/readline$ comm -23 vi-insert emacs-standard 
"\C-d": vi-eof-maybe
"\C-n": menu-complete
"\C-p": menu-complete-backward
"\e": vi-movement-mode

The first 3 of these four conflict with emacs bindings:

"\C-d": delete-char
"\C-n": next-history
"\C-p": previous-history

which I resolved as follows:

set keymap emacs
"\e": "kj" # see https://unix.stackexchange.com/questions/303631/how-can-i-setup-a-hybrid-readline-with-emacs-insert-mode-and-vi-command-mode
"\C-d": delete-char # eof-maybe: ^D does nothing if there is text on the line
"\C-n": menu-complete
"\C-p": menu-complete-backward
"\C-y": previous-history # historY
"\e\C-y": previous-history

editing-mode vi

As we saw above, vi, vi-command and vi-move are one and the same keymap:

+ravi@boxy:~$ diff <(bind -pm vi) <(bind -pm vi-move)
+ravi@boxy:~$ 

Note that's a total of just two distinct maps which are associated by default with editing-mode vi.

When in editing-mode vi, the keymaps in use are vi/vi-command/vi-move and vi-insert (the starting keymap). Only one of these two maps is active at a time.

editing-mode vi does nothing more than set a default keymap when the shell starts, labelled vi-insert. Again, tthere is only one keymap active at a time. This vi-insert keymap maps most keys to self-insert so when you press the plastic button on your keyboard, the symbol printed on it appears on your screen.

The vi-insert keymap allows itself to be swapped to the text-manipulating keymap called vi-command/vi/vi-move by using vi-movement-mode command, bound to the ESC key by default in the vi-insert keymap.

Actually, even the emacs keymap can set the vi-like text manipulation keymap active by using the vi-movement-mode command, as in the hybrid solution mentioned above.

Or in easier language...

By default, press ESC to change to the vi-command keymap when the vi-insert keymap is active.

The vi-command keymap uses standard, single keypresses like a, b and c to move and interact with text, just like the vi editor's default or command mode. There are generally no Ctrl+key combinations. You can't insert text in this mode; the letter keys are mapped to editing/moving commands. For typing text, you switch to the vi-insert keymap (example: press i for "Insert").

Entering text is done using the the vi-insert keymap, which is active when the shell starts if you have editing-mode vi in your .inputrc file. Swap to the vi-insert keymap by pressing i for "insert" while in vi-command (or in numerous other ways for those initiated into vi).

Unless you know the vi editor, you'll probably find vi-command keys very hard to use at first, but if you get good at it, you can edit text like a long-bearded wizard.

like image 158
Tom Hale Avatar answered Oct 07 '22 14:10

Tom Hale


From man readline (my emphasis):

The set of legal keymap names is emacs, emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move, vi-command, and vi-insert. vi is equivalent to vi-command

Thus, in your inputrc you can specify different keybinds for the different modes, so, for example, in command mode you could yank the last argument from the previous command with Altp, but this keybind would have no effect in insert mode (in this simple setup anyway):

set editing-mode vi

set keymap vi-command
# these are for vi-command mode
"\e[A": history-search-backward
"\e[B": history-search-forward
"\ep": yank-last-arg

set keymap vi-insert
# these are for vi-insert mode
"\e[A": history-search-backward
"\e[B": history-search-forward
Control-l: clear-screen
like image 39
jasonwryan Avatar answered Oct 07 '22 14:10

jasonwryan