Sams Teach Yourself Emacs in 24 Hours |
|||||||||||||||||
Hour 23: Binding Keys and Creating Menus |
|||||||||||||||||
|
|
A key sequence is the input packet that Emacs interprets as a whole. The key sequence is associated to some action through keymap. A keymap in Emacs is stored in a Lisp variable, which is traditionally named so that it ends in the suffix -keymap. The most basic predefined keymaps in Emacs are the following: global-map, esc-map, mode-specific-map, ctl-x-map, ctl-x-4-map, ctl-x-5-map.
The first one, global-map, contains key definitions that are available everywhere in Emacs. Normal keyboard characters a and b, and other character keys, such as Enter (which generates ASCII code 10) and the Tab key (which generates ASCII code 9) are defined in this map.
The esc-map is part of the global-map and handles all key sequences that are started with Esc key.
The mode-specific-map is part of the global-map and handles all the key sequences started with the C-c key. This is the keymap that Emacs has specifically reserved for extension packages and for user keybindings.
The ctl-x-map is the keymap under which C-x-derived key sequences are interpreted. This keymap is the heart of the Emacs, because most of the Emacs functions have been buried under the C-x key.
The ctl-x-map includes two more submaps: ctl-x-4-map, where you find "other window" commands, and ctl-x-5-map, which contains frame-handling commands.
A key sequence that leads to the submap--such as C-x, C-c, C-x 4, C-x 5--is called prefix key. A prefix can be a single key or combination of characters, modifiers, and named events (complex key sequence).
Let's take a look what prefix is in your Emacs: Press C-h b (describe-bindings) and look for lines that read Prefix Command . To make skimming the long listing easier, adjust it to show only the lines matching the word prefix like this:
1. Switch to the Help window. If modeline reads %%, call C-x C-q to toggle the buffer's read-only flag to the off position. Proceed when modeline reads --.
2. Position the cursor at the top of the window.
3. Call the command M-x delete-non-matching-lines RET prefix RET.
Note - Traditionally the Help key has been C-h, but in other programs the f1 key established the position to access the help system. Emacs followed this tradition and added f1 key to mean the same as C-h. |
The exact listing depends on your configured Emacs, but Figure 23.2 is an example of what you can expect.
There are several defined prefix keys that lead to further submaps, which is indicated by the wording Prefix command . From here you can query the defined keys in a particular keymap with the key sequence <PREFIX-KEY> followed by C-h. For example, to peek at what keys are defined in ctl-x-4-map, press C-x 4 C-h. The listing shows us several commands in that submap (see Figure 23.3).
A prefix argument carries extra information to the command behind key sequence. It is an additional parameter that makes the command work differently than without a parameter. Take a closer look at key a, with C-h a:
(self-insert-command N)
This command can accept the additional argument N (count), so how do you send it? The keys C-u (universal-argument) and Esc followed by a numeric value have been assigned to be used for a prefix argument. The C-u can be distinguished from the Esc prefix key, but that depends on the command if it wants to do that. C-u can be taken as C-u (raw prefix argument '(4) in Lisp), but can also be interpreted to map to value 4 or a multiple of it. If you press C-u C-u, it can be counted as 4x4 = 16 (raw value would be '(16) in Lisp). The self-insert-command interprets C-u as 4, so if you press C-u a; you would get a repeated four times. Not very useful? Well, let's draw some straight lines. Try C-u C-u C-u = and you get 64 equal characters on the screen. You could have done that with the Esc prefix key too by pressing Esc followed by number keys 6 and 4 plus the equals sign (=). Want to delete all lines to the end of buffer? Use C-u C-u C-u C-u C-k, which sends 4x4x4x4 (256) kill-line commands.
It is also possible to pass negative prefix arguments to commands. The plain minus sign is sent with key sequence C-u - followed by <command>. That did not request four dashes, if you thought that it would draw a small separator on the screen. Emacs reserves the minus sign for the negative prefix, because that's the only way to pass signed values. If you want to pass numeric negative values, you can add a number: C-u - 2 would pass -2 to a <command>.
Note - C-u does not multiply when it is used for negative prefixes. |
You can find a use for negative arguments, for example, when you adjust indentation. First, draw a region, then call C-u C-x Tab (indent-rigidly) to push selected lines four spaces forward. To unindent the text, first select a region or recall the previous region with C-x C-x (exchange-point-and-mark), then call C-u - 4 C-x Tab. This is same command except that you used -4 instead of the positive value 4.
As you learned earlier, the global-map rules everywhere and inside it there can be more keymaps and submap definitions. There are many modes in Emacs, such as text-mode, mail-mode, message-mode, c++-mode, sh-mode, dired-mode, tar-mode, that need special keybindings to serve the mode's needs. How does a mode take over a global-map that is always present? It defines its own keymap and overrides the global-map with it. The dired-mode can now take control of all your regular keys such as a, b, and c and make it easier to use the mode.
Only one major mode can be active at the time. C++ editing mode (c++-mode), for example, is one major mode that remaps your global-map keys with its own keymap. In C++ the key mappings are mostly designed to help you position your code, to indent braces { and }, or to indent your current line when you hit the Tab key.
Each major mode keymap is named by using the mode's name and adding a suffix -keymap to it. The major mode name is the command that turns on the major mode--for example, M-x text-mode, M-x c++-, and so on. The keymap for each mode is named respectively text-mode-map and c++-mode-map.
You can't use the major mode keymaps right away. The only keymaps that are immediately available in Emacs are those mentioned in the "Key Sequences and Keymaps" section earlier this hour, which were part of the global-map. Remember that global-map was always present and defined.
The major mode's keymap does not exist until the major mode has been loaded and it has defined and initialized the keymap. If you have vanilla Emacs and you try to access c++-mode-map, Emacs rings a bell and tells you that "Symbol's value as variable is void: c++-mode-map."
;; See if C-c C-c binding is defined in c++-mode-map (lookup-key c++-mode-map "\C-c\C-c") ;; --> Signals error
The right moment to access a mode's keymap is when the mode gets turned on. At that point, mode initializes itself and runs a MODE-hook for user customizations. C++ mode runs c++-mode-hook, text mode runs text-mode-hook, and so on. The following is a solution to handle mode's keybinding settings in separate functions:
(add-hook 'c++-mode-hook 'my-c++-mode-hook) (defun my-c++-mode-hook () (define-key c++-mode-map [(f4)] 'c-indent-function))
This Lisp code says that call function my-c++-mode-hook is used when C++ mode is turned on, and key f4 defines the run command c-indent-function.
There can be only one major mode active in a buffer, but it is possible to have any number of accompanying minor modes running parallel to major mode. For example, minor modes font-lock-mode and lazy-lock-mode can run at the same time as the major mode c++-mode. These two minor modes highlight your current program so that function names, language keywords, type definitions, and so on get a distinct coloring.
Minor modes can have keymaps that override the major mode's keymap. If you have modes (A-major) and minor modes X Y Z shown in the modeline, and if they all define key C-c C-c, the leftmost minor mode sweeps the board.
While all major modes have a MAJOR-hook, it is not always the case with minor modes. For example font-lock-mode is a minor mode and it runs font-lock-mode-hook. lazy-lock-mode is also a minor mode, but it does not have and thus doesn't run lazy-lock-mode-hook.
You have to examine each minor mode to see if it supports a MODE-hook, where you can add minor mode keybinding customizations or other minor mode setup code. Use C-h v <MODE-NAME + Tab> to find your information on variables defined by minor mode. If you can complete a variable name with the Tab key to end with -hook suffix, the mode (whether it's major or minor) does have its own hook.
To define and modify key settings in Emacs, you have to write some Lisp code. Suppose you want to disable an Emacs keybinding that you find disturbing. Do you find yourself mistakenly hitting C-z and iconifying Emacs accidentally from time to time? Me too, so here is the Lisp command that makes the C-z binding go away:
;; C-x runs Emacs command iconify-or-deiconify-frame (define-key global-map [(control z)] nil)
Let's return to that C-z binding. Suppose you still have the default binding which you can restore with the following:
(global-set-key [(control z)] 'iconify-or-deiconify-frame)
You can't assign C-z-derived keys, such as if you try to add these new bindings:
;; Signals error: ;; "Key sequence control a uses invalid prefix characters" (global-set-key [(control z) (p)] 'print-buffer) (global-set-key [(control z) (P)] 'print-region)
Why not? Because the C-z key has already been taken by iconify-or-deiconify-frame. To be able to press keys after C-z, the key must be made a prefix key first. You make one by clearing the key as you did earlier:
(define-key global-map [(control z)] nil)
Now C-z has no definition, so you can use it as a prefix key and bind more keys after it:
(global-set-key [(control z) (p)] 'print-buffer) (global-set-key [(control z) (P)] 'print-region)
If you look at the listing C-z C-h, you see that Emacs has made a submap that is accessed via C-z (see Figure 23.4).
Before you leap into defining custom keybindings, you have to find out what kind of key sequence Emacs thinks you pressed.
Running Emacs in an X Window system is different from running Emacs in console mode. The console mode can be started with the -nw (no window) command-line switch or you might be running it already inside a vt100 tty or equivalent terminal. Be prepared that console Emacs can't use the same bindings as the X Window version. Here is a comparison minitable for Ctrl-<key> combinations in both environments. Notice that this listing might be different in the terminal where you're running the Emacs:
Keypress |
-nw |
Windowed |
Ctrl-right |
Esc O C |
C-right |
Ctrl-left |
Esc O D |
C-left |
Ctrl-up |
Esc O A |
C-up |
Ctrl-down |
Esc O B |
C-down |
Ctrl-RET |
RET |
C-return <<bold |
Shift-RET |
RET |
S-return <<bold |
Look closely at the last rows: In a nonwindowed environment the Shift and Ctrl modifiers are not recognized for these keys, whereas Ctrl-a and Shift-a are. In the nonwindowed Emacs, the cursor keys are recognized as a combination of separate characters. The cursor keys appear in this case under the Esc O prefix key.
The most convenient way to find out the key sequence syntax is to press the keys and recall the input with command C-h l (view-lossage). Follow these steps:
1. Press the x key three times.
2. Press a key sequence, say Ctrl-Alt-a.
3. Press the x key three times.
4. Recall the view-lossage with C-h l.
The output of the buffer contains the key sequence syntax that you can use. Look for text in the buffer that looks like the following:
x x x M-C-a x x x C-h l
Between the x's you see the key sequence. The Emacs keybinding syntax for this sequence is
(global-set-key [(meta control a)] 'beginning-of-defun) ;; Go to function
The syntax is written inside []. The parentheses () close a single key sequence and each M and C have been converted to lowercase meta and control modifier names. This universal keybinding syntax works between a variety of Emacs and XEmacs platforms.
How about mouse keybindings? They can be found as easily as the keyboard bindings. Try pressing Ctrl-Alt-mouse-1 and Ctrl-Alt-mouse-2 and check the results from C-h l buffer:
C-M-down-mouse-1 C-M-down-mouse-2
These key sequences can be bound to conveniently scroll your current buffer:
(global-set-key [(control meta mouse-1)] 'scroll-up) ;; Like Page down key (global-set-key [(control meta mouse-2)] 'scroll-down) ;; Like Page up key
Look at more examples. You want to bind Ctrl-F1:
;; C-h l reads: C-f1, so here is the binding (global-set-key [(control f1)] 'shell)
Or maybe bring up a diary with Ctrl-Alt-d. Pay attention to the meta modifier; which was produced by this keyboard:
;; C-h l reads: M-C-d; which was useless down-list command (global-set-key [(meta control ?d)] 'diary)
In some other keyboards the previous example could read as the following:
;; C-h l output reads: A-C-d (global-set-key [(alt control ?d)] 'diary)
The compile command doesn't seem to have a default keybinding, so let's map it to Alt-f12:
;; C-h l output reads: M-f12 (global-set-key [(meta f12)] 'compile)
What if you map Esc-mouse-1 to run command imenu, which brings up a function summary in programming modes? Pay attention that mouse buttons generate multiple events and you need only the button press event, which is mouse-1. Also note that Esc is part of the ASCII code table and can be represented with ?\e escape code. You use that and not the "escape" event name:
;; C-h l output reads: escape down-mouse-1 mouse-1 (global-set-key [(?\e mouse-1)] 'compile)
One of the handiest features in Emacs is the capability to record keyboard activities and play them back later. The macros are suitable for ad hoc tasks where you have a repeated editing task in the buffer. The Emacs default macro keybindings C-x (. and C-x ) have been assigned to uncomfortable key sequences, so let's remap them to faster keys:
(global-set-key [(control ?\()] 'start-kbd-macro) (global-set-key [(control ?\))] 'end-kbd-macro) (global-set-key [(control ?\=)] [ja1]'call-last-kbd-macro)
You picked three close keys from the top row and assigned them to macro commands: Hold down Ctrl-Shift-8 (choose the 8 that's in the top row of the keyboard) and you can start recording a macro. Hold down Ctrl-Shift-9 (choose the 9 the top row) to stop recording the macro. Finally press Ctrl-Shift-0 to play the recorded macro.
You see something new in this global-set-key Lisp call. When you map keys, Emacs can interpret normal ASCII characters as usual with (Ctrl-a), but if you map something outside of regular ASCII range, you have to use Emacs character syntax, where a character is denoted with a question mark. [(Ctrl-a)] can be represented with [(Ctrl-?a)] and with [(Ctrl-?\a)] syntaxes. That extra backslash means "take the next character literally."
You have only seen how single functions can be bound to keys; but what if you want to always pass a certain prefix argument to the function? Take, for example, C++ mode and commenting. Go to an empty buffer and call M-x c++-mode. The mode line indicator shows that your major mode is "(C++)". To peek at the mode's bindings, press key sequence C-h m (describe-mode) and find comment-region, which is bound to key C-c C-c. Follow these steps:
1. Type two lines of text into the buffer.
2. Draw a region over those lines.
3. Press C-c C-c to comment the region.
The next question is how you uncomment the region. Examine what the comment-region function definition says with C-h k C-c C-c. I've included only some relevant lines from that description below:
(comment-region BEG END &optional ARG) ... With just C-u prefix arg, uncomment each line in region.
The need to pass C-u argument to the C-c C-c key sequence might not look like much trouble, but it soon starts to be when you work with the code longer. And it would be nice if you wouldn't have to stress your pinky to access C-c C-c in a long editing session. Let's move the comment and uncomment commands to function keys f5 and f6, which are more easily accessible. Recall the previously mentioned C++ setting example and add couple of new Lisp lines to it:
(add-hook 'c++-mode-hook 'my-c++-mode-hook) (defun my-c++-mode-hook () (define-key c++-mode-map [(f4)] 'c-indent-function) (define-key c++-mode-map [(f5)] 'comment-region) (define-key c++-mode-map [(f6)] (definteractive (comment-region '(4)))) )
The macro definteractive is not part of the default Emacs command set, but you can find it from the CD by loading sams-lib.el. You attached your first customized function call into key. definteractive declares an anonymous function at point with the body containing a call to comment region with argument '(4), which is the Lisp representation for the C-u prefix argument. The function key F6 now always calls function comment-region with prefix C-u and you don't have to type it as in the original c++-mode-map binding C-u C-c C-c Commenting and uncommenting a region is only two keypresses away!
Sams Teach Yourself Emacs in 24 Hours |
|||||||||||||||||
Hour 23: Binding Keys and Creating Menus |
|||||||||||||||||
|
© Copyright Macmillan USA. All rights reserved.