Reputation: 4223
In emacs, I want to bind a command to C-i. So I put (global-set-key "\C-i" 'forward-word)
in my .emacs file. This works, except now the TAB key is bound to 'forward-word as well.
How do I bind a command to C-i without changing TAB?
Upvotes: 33
Views: 6243
Reputation: 74470
In short, this should solve the problem for you:
(setq local-function-key-map (delq '(kp-tab . [9]) local-function-key-map))
(global-set-key (kbd "C-i") 'forward-word)
Longer version:
From the emacs lisp documentation on function keys:
In ASCII, C-i and <TAB> are the same character. If the terminal can distinguish between them, Emacs conveys the distinction to Lisp programs by representing the former as the integer 9, and the latter as the symbol tab.
Most of the time, it's not useful to distinguish the two. So normally local-function-key-map (see Translation Keymaps) is set up to map tab into 9. Thus, a key binding for character code 9 (the character C-i) also applies to tab. Likewise for the other symbols in this group. The function read-char likewise converts these events into characters.
So, once you do the following, you can see the difference in the key bindings:
(setq local-function-key-map (delq '(kp-tab . [9]) local-function-key-map))
;; this is C-i
(global-set-key (kbd "C-i") (lambda () (interactive) (message "C-i")))
;; this is <tab> key
(global-set-key (kbd "<tab>") (lambda () (interactive) (message "<tab>")))
Note, each mode sets up the various TAB bindings differently, so you may need to do customization per mode that you care about.
Version Dependency:
The above works for Emacs 23.1. From the NEWS file:
Function key sequences are now mapped using `local-function-key-map', a new variable. This inherits from the global variable function-key-map, which is not used directly any more.
Which means, in versions 22 and earlier, you can get the same effect by using the variable function-key-map
. I tested this and found it to work with Emacs 21.
(setq local-function-key-map (delq '(kp-tab . [9]) function-key-map))
(global-set-key (kbd "C-i") 'forward-word)
Upvotes: 34
Reputation: 39050
I came up with my own solution before seeing this question:
(define-key input-decode-map [#x2000009] [#x6000069]) ; C-S-i
(define-key input-decode-map [#x200000d] [#x600006d]) ; C-S-m
(define-key input-decode-map "\C-i" [#x4000069])
(define-key input-decode-map "\C-m" [#x400006d])
(define-key input-decode-map "\C-[" [#x400005b])
The reason this works is, the keycode that the letter key generates is the "real" keycode [on the left], whereas the keycode that tab, enter, escape generate are function key symbols. The function key symbols are already mapped in input-decode-map to the same symbols that the letters naturally generate, and will continue to work.
The keycodes that I mapped them to use the same mechanism as keycodes for C-0, etc [i.e. keys that don't have control codes in ASCII]. So, they're described as e.g. C-i (since they're excluded from the special casing that describes them as TAB/etc), and still have event-modifiers/event-basic-type (control)/?i.
The downside is that they're a bit harder to work with - you have to use the numbers, since they won't be generated by (kbd) and you can't call a function or variable within a vector literal. If you bind C-i or C-m to a movement function, it will work with shift selection (since even though they're "fake" keys the mode still knows that C-S-m is shifted and that it's normal equivalent is C-m)
NOTE: This will impact text terminals, so if you use text terminals you will need to detect whether the gui is running with (when window-system ...)
- if you use the Emacs Daemon with graphical windows you may want to put this in a window-setup-hook.
Upvotes: 1
Reputation: 1203
Perhaps this may help, as it did help me:
Instead of:
; differentiate tab from C-i
(setq local-function-key-map (delq '(kp-tab . [9]) function-key-map))
Try:
(define-key local-function-key-map [tab] nil)
Upvotes: 1
Reputation: 181
I recommend the following:
(define-key input-decode-map (kbd "C-i") (kbd "H-i"))
(global-set-key (kbd "H-i") 'whatever-you-want)
It should work from at least Emacs 23.
This is similar to the keyboard-translate technique in Caio's answer, but operates at a slightly higher level.
The disadvantage of keyboard-translate is that it will take effect even
when Emacs isn't running read-key-sequence, and in particular C-q C-i
will no longer work as a way to insert a literal tab character.
Modifying local-function-key-map doesn't work well, because typically
you want the <tab>
key to continue doing whatever the current mode has
defined for TAB
.
Upvotes: 18
Reputation: 1816
This solution, which is a sort of combination of the previous two, worked for me. In this particular case, I wanted to reassign C-i to previous-line. This solution preserves the functionality of TAB in the minibuffer. Note that TAB needs to be refined locally for the modes you use with a hook:
; As mentioned in the other solution, C-i and TAB are the same character in ASCII.
; We have to differentiate between the two and reassign each in a roundabout way.
; differentiate tab from C-i
(setq local-function-key-map (delq '(kp-tab . [9]) function-key-map))
;; Translate the problematic key to the function key Hyper:
(keyboard-translate ?\C-i ?\H-i)
;; Rebind accordingly
(global-set-key [?\H-i] 'previous-line)
; Finish by redefining tab for c-mode.
(defun my-c-mode-common-hook ()
(local-set-key (kbd "<tab>") 'indent-for-tab-command)
)
(add-hook 'c-mode-common-hook 'my-c-mode-common-hook)
Upvotes: 1
Reputation: 291
I found this solution, after much pain, lost in the messages archives. It's simple, avoids conflicts with other modes, and is the only which worked for me:
;; Translate the problematic keys to the function key Hyper:
(keyboard-translate ?\C-i ?\H-i)
(keyboard-translate ?\C-m ?\H-m)
;; Rebind then accordantly:
(global-set-key [?\H-m] 'delete-backward-char)
(global-set-key [?\H-i] 'iswitchb-buffer)
Upvotes: 29