Search code examples
emacselispkeymapping

How to rebind TAB and RET in an Emacs minor mode?


I am trying to define my minor mode, mimicking it after isearch-mode (since it is kind of an interactive search-and-replace tool, I thought it might be a good starting point). My commands work well (tested on global keybindings), but I have serious problems with binding them locally (in the minor mode map) to some keys, namely TAB and RET. I'm doing something like this:

(defvar my-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\s" 'my-command)
    (define-key map "\t" 'another-one)
    (define-key map "\r" 'yet-another)
    map))

(Of course, I did put my keymap into minor-mode-map-alist.)

While the space-bound command works fine, TAB and RET somehow do not. If I change, eg, "\t" to "[f11]", it works fine. I tried using the "vector notation" ([?\t]) with identical results (after C-h C-v-ing my keymap it was not surprising). What might be happening?

Edit: to clarify the problem, I tried to isolate it and I came up with the following code. Assume that I want to have an artificial, rather minimal minor mode tabbang in which the TAB key inserts an exclamation mark. I'm doing this:

(defvar tabbang-mode)
(add-to-list 'minor-mode-alist '(tabbang-mode tabbang-mode) t)

(defvar tabbang-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map [?\t] 'tabbang-insert-bang)
    (define-key map [?\C-\t] 'tabbang-insert-bang)
    (define-key map [f11] 'tabbang-insert-bang)
    (define-key map [?\r] 'tabbang-done)
    (define-key map [t] 'tabbang-other-char)
    map))
(add-to-list 'minor-mode-map-alist `(tabbang-mode . ,tabbang-mode-map) t)

(defun tabbang-insert-bang ()
  (interactive)
  (insert "!"))

(defun tabbang-mode ()
  (interactive)
  (setq tabbang-mode " tabbang"))

(defun tabbang-other-char ()
  (interactive)
  (tabbang-done)
  (setq unread-command-events
    (append (listify-key-sequence (this-command-keys))
        unread-command-events)))

(defun tabbang-done ()
  (interactive)
  (setq tabbang-mode nil))

While in my tabbang-mode, "other" keys correctly exit the mode and insert themselves, f11 inserts a bang (correct), TAB does not exit the mode (correct), but inserts nothing (wrong), C-TAB yields "undefined key" error (definitely wrong), and RET exits the mode (correct), but inserts a newline (wrong). And I tried on a "fresh" emacs (without loading site-file and my .emacs), so that no other code should intervene (I was afraid of yasnippet somehow capturing TAB etc.) What is going on?


Solution

  • I believe you can change them with (kbd "<tab>") and (kbd "<return>") in place of "\t" and "\r" respectively.


    In response to your edit, yes, the following works perfectly for me:

    ...
    (defvar tabbang-mode-map
      (let ((map (make-sparse-keymap)))
        (define-key map (kbd "<tab>") 'tabbang-insert-bang)
        (define-key map (kbd "<C-tab>") 'tabbang-insert-bang)
        (define-key map (kbd "<f11>") 'tabbang-insert-bang)
        (define-key map (kbd "<return>") 'tabbang-done)
        (define-key map (kbd "t") 'tabbang-other-char)
        map))
    (add-to-list 'minor-mode-map-alist `(tabbang-mode . ,tabbang-mode-map) t)
    ...