Elisp: conditionally change key binding

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

Something like that:

(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?

I am currently testing specific modes and doing the right thing for this mode, but I really would like the solution to be correct, without the need to explicitly add a condition for this particular mode.

Any ideas on how to do this?

Thanks! / Eric

+5
source share
5 answers

, key-binding ( global-key-binding, minor-mode-key-binding local-key-binding) .

:

(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

, TAB, , TAB:

(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)
  (message "my-complete")
  (let ((my-complete-mode nil))
    (call-interactively (key-binding (kbd "TAB")))))
+2

, Emacs key binding fallback, . , , :

(defmacro define-key-with-fallback (keymap key def condition &optional mode)
  "Define key with fallback. Binds KEY to definition DEF in keymap KEYMAP, 
   the binding is active when the CONDITION is true. Otherwise turns MODE off 
   and re-enables previous definition for KEY. If MODE is nil, tries to recover 
   it by stripping off \"-map\" from KEYMAP name."
  `(define-key ,keymap ,key
     (lambda () (interactive)
        (if ,condition ,def
          (let* ((,(if mode mode
                     (let* ((keymap-str (symbol-name keymap))
                            (mode-name-end (- (string-width keymap-str) 4)))
                       (if (string= "-map" (substring keymap-str mode-name-end))
                           (intern (substring keymap-str 0 mode-name-end))
                         (error "Could not deduce mode name from keymap name (\"-map\" missing?)")))) 
                  nil)
                 (original-func (key-binding ,key)))
            (call-interactively original-func))))))

- , TAB , - . ( , yasnippets) :

(define-key-with-fallback outline-minor-mode-map (kbd "TAB") 
  (outline-cycle 1) (outline-on-heading-p))
+3

, :

(define-key <map> <key>
  `(menu-item "" <my-cmd> :filter ,(lambda (cmd) (if <my-predicate> cmd))))
+3

, - . TAB , tab-always-indent 'complete, , . , TAB , .

, completion-at-point-functions (, ). completion-at-point , completion-at-point-functions, nil, , , "" , - nil .

100% , , , , .

+2

define-key lambdas, .

;Static
(define-key evil-normal-state-mapr "m" 'evil-motion-state)
;Conditional
(define-key evil-normal-state-map "m" 
  (lambda () (interactive) (message "%s" major-mode)))

Lambda can be replaced with named functions such as my-tab-completion and used more efficiently.

From the docstring keyword (Emacs 25)

DEF is anything that can be a key definition:
 nil (means key is undefined in this keymap),
 a command (a Lisp function suitable for interactive calling),
 a string (treated as a keyboard macro),
 a keymap (to define a prefix key),
 a symbol (when the key is looked up, the symbol will stand for its
    function definition, which should at that time be one of the above,
    or another symbol whose function definition is used, etc.),
 a cons (STRING . DEFN), meaning that DEFN is the definition
    (DEFN should be a valid definition in its own right),
 or a cons (MAP . CHAR), meaning use definition of CHAR in keymap MAP,
 or an extended menu item definition.
 (See info node `(elisp)Extended Menu Items'.)
0
source

All Articles