Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emacs key binding fallback

Tags:

emacs

elisp

I have a minor mode. If that mode is active and the user hits DEL, I want to do some action, but only if some condition holds. If the condition holds and the action is executed I want to do nothing more after that. But if the condition fails, I don't want to do anything and let the default DEL action execute.

Not sure how I could solve this. But I guess I could do it in two ways:

1) I could rebind the DEL key to a function in the minor mode and then check if the conditions holds ot not. But then how do I know what the default command to DEL is?

2) I could add a pre command hook like this. Execute the command and then break the chain. But how do I break the chain?

(add-hook 'pre-command-hook
          (lambda()
            (when (equal last-input-event 'backspace)
              ;; Do something and then stop (do not execute the
              ;; command that backspace is bound to)
              )))

In what way would you solve it? Thanks!

like image 919
rejeep Avatar asked Mar 22 '10 16:03

rejeep


2 Answers

The way to do this is to temporarily disable your minor mode, then look up the key binding.

Pretend that you've bound 'do-thingy to DEL. Then this would do the trick (assuming the condition you want to trigger off is (equal last-input-event 'backspace):

(defun do-thingy ()
  "Do something, unless last event was backspace."
  (interactive)
  (if (equal last-input-event 'backspace)
      (let* ((my-minor-mode nil)
             (original-func (key-binding (kbd "DEL"))))
        ;; original-func is whatever DEL would be if
        ;; my-minor-mode were disabled
        (call-interactively original-func))
    (message "Here's my minor mode behavior!")))

Note: This behavior assumes you have set up your key bindings the standard way a minor-mode would. Specifically, you should add your keymap to the variable minor-mode-map-alist by adding an element (my-minor-mode . my-minor-mode-keymap). That's how the above let statement works, it looks up the binding you want with your mode temporarily disabled.

If you use define-minor-mode to define your minor mode, the keymap gets set up the "right way" automatically.

like image 159
Trey Jackson Avatar answered Sep 23 '22 02:09

Trey Jackson


This is what I use for my smart-tab package which does exactly that.

(defun smart-tab-default ()
  "Indents region if mark is active, or current line otherwise."
  (interactive)
  (if mark-active
      (indent-region (region-beginning)
                     (region-end))

    (call-interactively
     (or
      ;; Minor mode maps for tab (without smart-tab-mode)
      (cdar (assq-delete-all 'smart-tab-mode (minor-mode-key-binding "\t")))
      (cdar (assq-delete-all 'smart-tab-mode (minor-mode-key-binding [(tab)])))
      (local-key-binding "\t")
      (local-key-binding [(tab)])
      (global-key-binding "\t")
      (global-key-binding [(tab)])))))

And in the command smart-tab (which is the one bound to tab in the minor mode), it has the following:

(if (smart-tab-must-expand prefix)
    ;; use smart tab
  (smart-tab-default))

It first checks if there are any minor mode bindings for tab (not including smart-tab-mode), then local, and finally global keybindings.

like image 21
haxney Avatar answered Sep 23 '22 02:09

haxney