elisp的:有条件地更改键绑定(Elisp: Conditionally change keybi

2019-08-31 12:00发布

我试图写它尝试了一堆不同的地方点是不同的补全自定义选项卡完成实施。 然而,如果没有的完井条件得到满足,我想标签做什么都当前模式原本打算做的事。

事情是这样的:

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

目前我正在检查特定的模式和做正确的事情该模式,但我真的想一个解决方案,只是做了正确的事情没有我明确地添加一个条件的特定模式。

的任何想法如何做到这一点?

谢谢! /埃里克

Answer 1:

您可以使用功能,如key-binding (或它的更具体的变种global-key-bindingminor-mode-key-bindinglocal-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)
  (if (my-condition)
      (message "my-complete")
    (let ((my-complete-mode nil))
      (call-interactively (key-binding (kbd "TAB"))))))


Answer 2:

顺便说一句,这里是另一种解决方案:

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


Answer 3:

下面是我基于写了一个宏观的Emacs键绑定后备有条件地定义键绑定。 它增加了键绑定到指定的次要模式,但如果条件不成立,则执行以前分配的作用:

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


Answer 4:

这是可能的,你可以不需要任何特殊的解决办法都实现这一目标。 在多数模式TAB默认少了点缩进,但是如果设置了全局变量tab-always-indent'complete ,它会尝试先执行完成,并缩进,如果没有完成是可能的。 这通常确实很好,但如果TAB是在你的主要模式之一绑定到另一个命令,你可能是出于运气。

如果在您需要的模式下运行,你只需要您的自定义完成功能添加到列表中前completion-at-point-functions在所有适用的缓冲器(可能使用的模式挂钩)。 在completion-at-point命令调用中列出的每个函数completion-at-point-functions ,直到其中一个返回非nil ,因此,所有你需要做的,有您的自定义功能完成“告吹”,以现有的行为是回报nil不是这样。

这不是一个100%的问题的答案,但如果主要模式你与按照正常的规则,这一点可能是最干净的方式被写入工作。



Answer 5:

define-key可以接受带引号的字符串或交互的lambda就像这个例子。

;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的可命名函数来代替像我的标签完成和更有效的利用。

从定义键的文档字符串(Emacs的25)

DEF is anything that can be a key's 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'.)


文章来源: Elisp: Conditionally change keybinding
标签: emacs elisp