Search code examples
emacselisp

Elisp: Conditionally change keybinding


I'm trying to write a custom tab completion implementation which tries a bunch of different completions depending on where the point is. However, if none of the conditions for completions are met I would like tab to do what ever the current mode originally intended it to do.

Something like this:

(defun my-custom-tab-completion ()
  (interactive)
  (cond
   (some-condition
    (do-something))
   (some-other-condition
    (do-something-else))
   (t
    (do-whatever-tab-is-supposed-to-do-in-the-current-mode))) ;; How do I do this?

Currently I'm checking for specific modes and doing the right thing for that mode, but I really would like a solution that just does the right thing without me having to explicitly add a condition for that specific mode.

Any ideas of how to do this?

Thanks! /Erik


Solution

  • You could use functions such as key-binding (or its more specific variants global-key-binding, minor-mode-key-binding and local-key-binding) to probe active keymaps for bindings.

    For example:

    (call-interactively (key-binding (kbd "TAB")))
    ;; in an emacs-lisp-mode buffer:
    ;;    --> indent-for-tab-command
    ;; 
    ;; in a c++-mode buffer with yas/minor-mode:
    ;;    --> yas/expand
    

    One way to avoid infinite loops if your command is bound to TAB could be to put your binding in a minor mode, and temporarily disable its keymap while looking for the TAB binding:

    (define-minor-mode my-complete-mode
      "Smart completion"
      :keymap (let ((map (make-sparse-keymap)))
                (define-key map (kbd "TAB") 'my-complete)
                map))
    
    (defun my-complete ()
      (interactive)
      (if (my-condition)
          (message "my-complete")
        (let ((my-complete-mode nil))
          (call-interactively (key-binding (kbd "TAB"))))))