driscoll@boulder.UUCP (09/18/87)
;;; ;;; ;;; This is code that will (I hope) end all traces of the ;;; backspace/delete and related flames. ;;; ;;; Written by: ;;; ;;; James Driscoll Sept 16, 1987 ;;; University of Colorado, Boulder ;;; Department of Computer Science ;;; Campus Box 430 ;;; Boulder CO 80302 ;;; ;;; ;;; EXPLANATION ;;; ;;; This code allows you to change the *meaning* of keys ;;; without changing their *names*. (see detailed list of ;;; exceptions below) This code is intended to serve the ;;; needs of those who have complaints with the standard ;;; key bindings of gnu-emacs that are of the form: ;;; ;;; I think of backspace in terms of ;;; backward deletion. ;;; ;;; Gnu-emacs does indeed provide mechanisms to change ;;; the binding of keys to functions. Thus, one solution ;;; to the above complaint is to switch the meanings of ;;; the delete keys and backspace keys by globally setting ;;; backspace to run the function backward-delete-character ;;; and delete to the help prefix character. Unfortunately ;;; different modes have different ideas of backward deletion ;;; and a particular mode might like to replace the binding of ;;; of (what it thought was) delete to backward-delete-char with ;;; a binding of delete to backward-delete-char-untabify. The ;;; essential problem is that the implementors of the various ;;; modes associate the abstract concept of backward deletion ;;; with the particular key delete. A (functionally) acceptable ;;; solution would be to add hooks to every mode that would ;;; exchange the bindings of backspace and delete. Most people ;;; claim this is too heavy a burden. This burden could be ;;; automated by writing code that scans through your load path ;;; looking at .el and .elc files for modes affecting the keys ;;; you are concerned about and then generates the appropriate ;;; hook for the mode, and even force it to run a hook if it ;;; doesn't do so already. This would be an acceptable solution ;;; (modulo the hooks getting stale) but was more work than I ;;; was willing to undertake. ;;; ;;; The other means of altering the meaning of keys is to use ;;; the keyboard-translation-table. This table is best used to ;;; to make a non-standard terminal compatible with Gnu-Emacs. If ;;; your terminal generates a code when you press that key labeled ;;; X that Gnu-Emacs thinks is Z, then you can use the translation ;;; table to tranlate that Z back to X. Thus you press X, Gnu-Emacs ;;; gets a Z, immediately translates it to X, and inserts it. Both ;;; you and Gnu-Emacs are happy with this. ;;; ;;; This appears to be the ideal solution to the backspace/delete ;;; problem: simply use the translation table to make Gnu-Emacs see ;;; delete when you type backspace, and the reverse. The majority ;;; of the problem is solved. When you type backspace, it deletes ;;; backward one character. When you type delete, the help prompt ;;; comes up. Unfortunately, if you ask what key delete-backward-char ;;; is on, you will be told it is on delete. If you type C-q delete ;;; it inserts a ^H. The problem is that when you switched the codes ;;; of the keys, you really should have pryed the keys off your ;;; keyboard and switched them as well. In summary, the keyboard ;;; translation table allows you to change the *location* of keys, ;;; not their *meaning*. ;;; ;;; For some, this might not be a problem. They could use the ;;; keyboard-translation-table to switch backspace and delete and ;;; then stick a piece of tape on their delete key with backspace ;;; written on it, and another on the backspace key with delete ;;; written on it. At least the key they associate with backward ;;; deletion is in the right place, but its name has just changed. ;;; The problems they have are that: 1) When they are told to type ;;; backspace, it is in an unexpected place, and 2) when they ;;; need to insert ^H that key is in the wrong place. This ;;; way of thinking about things changes the *meaning* of the key, ;;; as well as its *name*. ;;; ;;; This code takes the keyboard-translation-table as its base, ;;; but then avoids the name change. You can exchange the meaning ;;; of ^N and ^P. When you type ^N, you move to the previous line. ;;; When you ask what key runs previous-line, you will be told ^N. ;;; When you want to insert ^N type C-q ^N. Finally everyone ;;; should be happy and we can move on to more interesting issues. ;;; (Well, almost. There are a small number of problems involving ;;; key-sequences being displayed incorrectly, all of which are ;;; described in detail below. These can be fixed [in a general ;;; and natural way as described later], but this requires ;;; altering the c code.) ;;; ;;; ;;; USING THIS CODE ;;; ;;; To use this code, load this file in your .emacs. For example: ;;; ;;; (setq load-path (cons "<your-dir>" load-path)) ;;; (load "<this-file>") ;;; ;;; will do the trick if you name this file <this-file> and put it ;;; in the directory <your-dir>. After this you should go about ;;; swapping the keys you are particularly annoyed at. For instance, ;;; if you use HP terminals and would be happy to have the meaning ;;; of DEL and C-H swapped, you should follow the command to load ;;; with the command: ;;; ;;; (swap-meanings-of-keys ?^H ?^?) ;;; ;;; where you got ^H by typing C-q C-h, and similarly ^? by ;;; typing C-q DEL. If this is what you had in mind, you can ;;; save yourself a few keystrokes by instead typing: ;;; ;;; (swap-del-and-bs nil) ;;; ;;; which will accomplish the same thing. If you think of ;;; the backspace key as meaning go back one character, C-b as ;;; meaning delete back a character, and DEL as meaning help there ;;; is still help. You should put the following lines in your ;;; .emacs after the load: ;;; ;;; (swap-meanings-of-keys ?^H ?^B) ;;; ; now C-h means what C-b did; C-b what C-h did ;;; (swap-meanings-of-keys ?^B ?^?) ;;; ; this swaps the meaning of C-b and DEL. Since ;;; ; DEL is unaltered, C-b now does what DEL used to do. ;;; ; DEL now does what C-b previously did, which is what C-h ;;; ; originally did. ;;; ;;; If this seems confusing and you have a complicated ;;; rearrangement in mind, you should get out an introductory ;;; group theory book and figure out how decompose an arbitrary ;;; permutation into transpostions. Or write a more general ;;; routine. ;;; ;;; If you prefer interactive swapping, load this file via ;;; ;;; ESC-x load-libraray RET <this-file> RET ;;; ;;; Then you can swap keys by typing: ;;; ;;; ESC-x swap-two-keys RET <key1> <key2> ;;; ;;; If you get hopelessly confused, type (if you still can!) ;;; ;;; ESC-x reset-swappings RET ;;; ;;; to get back to the original meanings of the keys. ;;; ;;; ;;; ELABORATION ;;; ;;; There are three minor(?) exceptions to the description of ;;; how this works, and one major exception. ;;; ;;; 1. This is the major one. C-h M (describe-mode) is wrong ;;; in its identification of key sequences. If you need to ;;; know what key a command is on, C-h W (where-is) works ;;; properly. If you need to know what command a key runs ;;; C-h C (describe-key-briefly) and C-h K (describe-key) ;;; work properly. The close cousin of describe-mode, ;;; describe bindings (C-h B), is also wrong. ;;; ;;; 2. If you swap the self inserting keys m and n and then ;;; type C-h c m (describe-key-briefly) you will be told that ;;; it is bound to the self-insert-command, even though typing ;;; m inserts the character n. Be advised C-q m still (i.e. ;;; correctly [or at least consistently]) inserts m. ;;; ;;; 3. Some very low-level echoing can not be affected without ;;; changing c code. If you swap k with a prefix character ;;; (i.e. C-c, C-x, ESC, C-u, etc.) and press k it will echo ;;; as the prefix character rather than k. ;;; ;;; 4. Lisp code that uses (interactive "k") will not echo ;;; the key you type, but rather what it is swapped with. ;;; The only standard lisp code that uses this is ;;; describe-key and describe-key-briefly. If you swap ;;; keys a and b, then invoke either describe-key or ;;; describe-key-briefly and press a, it will initially ;;; echo as b, but you will be correctly told that "a runs ;;; the command ... " ;;; ;;; To help compensate for this problem, some characters now ;;; echo in a somewhat different format. In unaltered gnu-emacs ;;; the delete key echoes as DEL. It should now echo as Del, ;;; the space bar as Spc rather than SPC, etc. Thus, if some piece ;;; of code I couldn't get around tells you that SPC is the key to ;;; press, and you have swapped the space bar, it is wrong. If ;;; it on the other hand it tells you Spc is the key of choice, you ;;; can procede with greater confidence. Control characters echo ;;; as C-X rather than the original C-x. If C-x is typed at you, ;;; be wary; C-X is likely to be right. ;;; ;;; All the above could be fixed by changing the c implementation ;;; of key_description to use a lisp vector that maps keys to ;;; print names. This vector could then be manipulated by this ;;; or other code. There remains a problem: code that prompts ;;; the user for input must be carefully written. For instance ;;; ;;; (message "Type ? for more help") ;;; ;;; must be written something like ;;; ;;; (message (concat "Type " (key-with-print-name "?") ;;; " for more help")) ;;; ;;; or it will not work as anticpated by the user if the meaning ;;; of the ? key has been swapped. ;;; ;;; ;;; ;;; BUGS: ;;; ;;; (In addition to the above clarifications) ;;; ;;; This code will only work for you if you are not already ;;; using the keyboard translation table. This is not an ;;; insurmountable problem if the table is static and global, ;;; but the effort required to make it work properly seemed ;;; not worth it to me. If you have buffer local values ;;; for the keyboard translate table, that throws a wrench in ;;; the works. If the keyboard translation table is dynamically ;;; changing (as, for instance, this code allows) then the code ;;; that alters it will need to cooperate with a much enhanced ;;; version of this code. ;;; ;;; Although in the above description it always refers to ;;; swapping two "keys," only the two character codes are ;;; swapped. That is, if you swap a and b, then ^A and ^B ;;; remain unaltered. If what you really want to do is ;;; swap the meanings of the physical keys a and b, you must ;;; additionally swap A and B, C-a and C-b, M-a and M-b, and ;;; M-C-a and M-C-b. ;;; ; Function that creates a vector that maps from ; characters to print-names (defun init-descriptions () (let ((descriptions (make-vector 256 "???")) (i 32) ; 32=SPC (control-to-upcase (- ?A 1))) ; 1=?^A (while (< i 127) ; 127=DEL (aset descriptions i (char-to-string i)) (setq i (+ 1 i))) (setq i 0); 0=?^@ (while (< i 32) ; 32=SPC (aset descriptions i (concat "C-" (char-to-string (+ i control-to-upcase)))) (setq i (+ 1 i))) (aset descriptions 32 "Spc") (aset descriptions 127 "Del") (aset descriptions 27 "Esc") (aset descriptions 10 "Lfd") (aset descriptions 9 "Tab") (aset descriptions 13 "Ret") (setq i 128) ; 128=M-C-@ (while (< i 256) (aset descriptions i (concat "M-" (aref descriptions (- i 128)))) (setq i (+ 1 i))) descriptions)) (defvar key-descriptions-vector (init-descriptions) "Vector of pretty descriptions of keys") ; function to compute print names of a key sequence ; represented as a string. This replaces the c definition ; in keymap.c for emacs-lisp calls, but it cannot replace ; the c calls to key-description in: ; ; callint.c echoing for (interactive "k") calls ; keyboard.c echoing for prefix, etc. characters ; keymap.c keymap manipulation, C-h M consequently in error (defun key-description (s) "Return a pretty description of key-sequence KEYS. Control characters turn into C-foo, spaces are put between elements, etc." (let ((description "")) (while (> (length s) 0) (setq description (concat description (aref key-descriptions-vector (aref s 0)) (if (> (length s) 1) " " ""))) (setq s (substring s 1))) description)) ; this creates the ``identity'' translation table (defun init-translate-table () (let ((table (make-string 256 0)) (i 0)) (while (< i 255) (aset table i i) (setq i (+ 1 i))) table)) ; init the keyboard-translate-table to the identity. ; create the variable inverse-translate-table that is ; used by quoted-insert. If only this code manipulates ; the keyboard-translation-table (if other code does, it ; won't in general work unless it keeps this code in mind) ; keyboard-translation-table will be a permutation (i.e. a ; bijective function) and inverse-translation-table will be ; its inverse (setq keyboard-translate-table (init-translate-table)) (defvar inverse-translate-table (init-translate-table) "Should be inverse of keyboard-translate-table.") ; in case you get totally confused, you can try to bail out ; by cleaning the slate. (defun reset-swappings (arg) "Undoes all meaning swaps of keys to restore initial meanings." (interactive "p") (progn (message "Unswapping all keys...") (setq keyboard-translate-table (init-translate-table)) (setq inverse-translate-table (init-translate-table)) (setq key-descriptions-vector (init-descriptions)) (message "Unswapping all keys...Done"))) ; Code that actually does a swap ; It swaps the entries in the translation table, ; swaps the names of the keys so their names remains unchanged, ; and fixes inverse-translate-table so it remains the inverse. (defun swap-meanings-of-keys (key1 key2) (let ((des1 (aref key-descriptions-vector key1)) (des2 (aref key-descriptions-vector key2)) (trans1 (aref keyboard-translate-table key1)) (trans2 (aref keyboard-translate-table key2))) (aset key-descriptions-vector key2 des1) (aset key-descriptions-vector key1 des2) (aset keyboard-translate-table key1 trans2) (aset keyboard-translate-table key2 trans1) (aset inverse-translate-table trans1 key2) (aset inverse-translate-table trans2 key1))) ; Interactively callable routine to change meaning of DEL an BS (defun swap-del-and-bs (arg) "Swap the meanings of the delete key and the backspace key. Please don't be upset that pressing delete causes C-h to echo, as you must change c code to fix that. Don't trust C-h M!" (interactive "p") (swap-meanings-of-keys 8 127)) ; General interactively callable routine to swap the meaning ; of two keys pressed by the user. (defun swap-two-keys (arg) "Swap the meanings of two keys globally. Be advised that keys swapped with ESC, C-X, or C-h will not always echo exactly right; fixing that requires rewriting c code. Don't trust C-h M!" (interactive "p") (let ((k1 0) (k2 0)) (message "Swap the key: ") (setq k1 (read-quoted-char)) (message "Swap the key %s with the key:" (key-description (char-to-string k1))) (setq k2 (read-quoted-char)) (swap-meanings-of-keys (aref inverse-translate-table k1) (aref inverse-translate-table k2)) (message "Swapped meaning of keys %s and %s" (key-description (char-to-string k2)) (key-description (char-to-string k1))))) ; replaces original version of quoted-insert from simple.el ; Instead of inserting the translation of the key the user typed, ; it inserts the character code generated by the keyboard. (defun quoted-insert (arg) "Read next input character and insert its inverse translation. Useful for inserting control characters. You may also type up to 3 octal digits, to insert a character with that code" (interactive "*p") (let ((char (aref inverse-translate-table (read-quoted-char)))) (while (> arg 0) (insert char) (setq arg (1- arg))))) ;;; ;;; end of code ;;;