Viper lets you define hot keys, i.e., you can associate keyboard keys such as F1, Help, PgDn, etc., with Emacs Lisp functions (that may already exist or that you will write). Each key has a “preferred form” in Emacs. For instance, the Up key’s preferred form is [up], the Help key’s preferred form is [help], and the Undo key has the preferred form [f14]. You can find out the preferred form of a key by typing M-x describe-key-briefly and then typing the key you want to know about.
Under the X Window System, every keyboard key emits its preferred form, so you can just type
(global-set-key [f11] 'calendar) ; L1, Stop (global-set-key [f14] 'undo) ; L4, Undo
to bind L1 (a key that exists on some SUN workstations) so it will invoke
the Emacs Calendar and to bind L4 so it will undo changes.
However, on a dumb terminal or in an Xterm window, even the standard arrow
keys may
not emit the right signals for Emacs to understand. To let Emacs know about
those keys, you will have to find out which key sequences they emit
by typing C-q and then the key (you should switch to Emacs state
first). Then you can bind those sequences to their preferred forms using
input-decode-map
as follows:
(cond ((string= (getenv "TERM") "xterm") (define-key input-decode-map "\e[192z" [f11]) ; L1 (define-key input-decode-map "\e[195z" [f14]) ; L4, Undo
The above illustrates how to do this for Xterm. On VT100, you would have to replace "xterm" with "vt100" and also change the key sequences (the same key may emit different sequences on different types of terminals).
The above keys are global, so they are overwritten by the local maps defined by the major modes and by Viper itself. Therefore, if you wish to change a binding set by a major mode or by Viper, read this.
Viper users who wish to specify their own key bindings should be concerned
only with the following three keymaps:
viper-vi-global-user-map
for Vi state commands,
viper-insert-global-user-map
for Insert state commands,
and viper-emacs-global-user-map
for Emacs state commands (note:
customized bindings for Emacs state made to viper-emacs-global-user-map
are not inherited by Insert state).
For more information on Viper keymaps, see the header of the file
viper.el.
If you wish to change a Viper binding, you can use the
define-key
command, to modify viper-vi-global-user-map
,
viper-insert-global-user-map
, and viper-emacs-global-user-map
, as
explained below. Each of these key maps affects the corresponding Viper state.
The keymap viper-insert-global-user-map
also affects Viper’s Replace
state.
If you want to bind a key, say C-v, to the function that scrolls page down and to make 0 display information on the current buffer, putting this in your Viper customization file will do the trick in Vi state:
(define-key viper-vi-global-user-map "\C-v" 'scroll-down)
To set a key globally,
(define-key viper-emacs-global-user-map "\C-c m" 'smail) (define-key viper-vi-global-user-map "0" 'viper-info-on-file)
Note, however, that this binding may be overwritten by other keymaps, since the global keymap has the lowest priority. To make sure that nothing will override a binding in Emacs state, you can write this:
(define-key viper-emacs-global-user-map "\C-c m" 'smail)
To customize the binding for C-h in Insert state:
(define-key viper-insert-global-user-map "\C-h" 'my-del-backwards-function)
Each Emacs command key calls some Lisp function. If you have enabled the Help, (see Rudimentary Changes) C-h k will show you the function for each specific key; C-h b will show all bindings, and C-h m will provide information on the major mode in effect. If Help is not enabled, you can still get help in Vi state by prefixing the above commands with \, e.g., \ C-h k (or you can use the Help menu in the menu bar, if Emacs runs under X).
Viper users can also change bindings on a per major mode basis. As with
global bindings, this can be done separately for each of the three main Viper
states. To this end, Viper provides the function
viper-modify-major-mode
.
To modify keys in Emacs state for my-favorite-major-mode
, the user
needs to create a sparse keymap, say, my-fancy-map
, bind whatever
keys necessary in that keymap, and put
(viper-modify-major-mode 'dired-mode 'emacs-state my-fancy-map)
in your Viper customization file. To do the same in Vi and Insert states, you
should use vi-state
and insert-state
. Changes in Insert state
are also in effect in Replace state. For instance, suppose that the user wants
to use dd in Vi state under Dired mode to delete files, u to unmark
files, etc. The following code in the Viper customization file will then do
the job:
(setq my-dired-modifier-map (make-sparse-keymap)) (define-key my-dired-modifier-map "dd" 'dired-flag-file-deletion) (define-key my-dired-modifier-map "u" 'dired-unmark) (viper-modify-major-mode 'dired-mode 'vi-state my-dired-modifier-map)
A Vi purist may want to modify Emacs state under Dired mode so that k, l, etc., will move around in directory buffers, as in Vi. Although this is not recommended, as these keys are bound to useful Dired functions, the trick can be accomplished via the following code:
(setq my-dired-vi-purist-map (make-sparse-keymap)) (define-key my-dired-vi-purist-map "k" 'viper-previous-line) (define-key my-dired-vi-purist-map "l" 'viper-forward-char) (viper-modify-major-mode 'dired-mode 'emacs-state my-dired-vi-purist-map)
Yet another way to customize key bindings in a major mode is to edit the
list viper-major-mode-modifier-list
using the customization widget.
(This variable is in the Viper-misc customization group.)
The elements of this list are triples of the form: (major-mode viper-state
keymap), where the keymap contains bindings that are supposed to be active
in the given major mode and the given viper-state.
Effects similar to key binding changes can be achieved by defining Vi keyboard macros using the Ex commands :map and :map!. The difference is that multi-key Vi macros do not override the keys they are bound to, unless these keys are typed in quick succession. So, with macros, one can use the normal keys alongside with the macros. If per-mode modifications are needed, the user can try both ways and see which one is more convenient. See Vi Macros, for details.
Note: in major modes that come up in Emacs state by default, the
aforesaid modifications may not take place immediately (but only after the
buffer switches to some other Viper state and then back to Emacs state). To
avoid this, one should add viper-change-state-to-emacs
to an
appropriate hook of that major mode. (Check the function
viper-set-hooks
in viper.el for examples.) However, if you
did not set viper-always
to nil
, chances are that you won’t
need to perform the above procedure, because Viper will take care of most
useful defaults.
Finally, Viper has a facility that lets the user define per-buffer
bindings, i.e., bindings that are in effect in some specific buffers
only. Unlike per-mode bindings described above, per-buffer bindings can be
defined based on considerations other than the major mode. This is done
via the function viper-add-local-keys
, which lets one specify bindings
that should be in effect in the current buffer only and for a specific Viper
state. For instance,
(viper-add-local-keys 'vi-state '(("ZZ" . TeX-command-master) ("ZQ" . viper-save-kill-buffer)))
redefines ZZ to invoke TeX-command-master
in vi-state
and ZQ to save-then-kill the current buffer. These bindings take
effect only in the buffer where this command is executed. The typical use
of this function is to execute the above expression from within a function
that is included in a hook to some major mode. For instance, the above
expression
could be called from a function, my-tex-init
, which may be added to
tex-mode-hook
as follows:
(add-hook 'tex-mode-hook 'my-tex-init)
When TeX mode starts, the hook is executed and the above Lisp expression is evaluated. Then, the bindings for ZZ and ZQ are changed in Vi command mode for all buffers in TeX mode.
Another useful application is to bind ZZ to send-mail
in the Mail mode buffers (the specifics of this depend on which mail
package you are using, rmail
, mh-e
, vm
, etc.
For instance, here is how to do this for mh-e
, the Emacs interface
to MH:
(defun mh-add-vi-keys () "Set up ZZ for MH-e and XMH." (viper-add-local-keys 'vi-state '(("ZZ" . mh-send-letter)))) (add-hook 'mh-letter-mode-hook 'mh-add-vi-keys)
You can also use viper-add-local-keys
to set per buffer
bindings in Insert state and Emacs state by passing as a parameter the
symbols insert-state
and emacs-state
, respectively.
As with global bindings, customized local bindings done to Emacs state
are not inherited by Insert state.
On rare occasions, local keys may be added by mistake. Usually this is done
indirectly, by invoking a major mode that adds local keys (e.g.,
shell-mode
redefines RET). In such a case, exiting the wrong
major mode won’t rid you from unwanted local keys, since these keys are
local to Viper state and the current buffer, not to the major mode.
In such situations, the remedy is to type M-x viper-zap-local-keys.
So much about Viper-specific bindings. See Customization in The GNU Emacs Manual, and the Emacs quick reference card for the general info on key bindings in Emacs.