6

我正在尝试编写一个自定义选项卡完成实现,它根据点的位置尝试一堆不同的完成。但是,如果不满足任何完成条件,我希望 tab 执行当前模式最初打算执行的操作。

像这样的东西:

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

目前我正在检查特定模式并为该模式做正确的事情,但我真的想要一个解决方案,它只做正确的事情,而无需我为该特定模式显式添加条件。

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

谢谢!/埃里克

4

5 回答 5

6

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

(define-key <map> <key>
  `(menu-item "" <my-cmd> :filter ,(lambda (cmd) (if <my-predicate> cmd))))
于 2014-04-04T13:05:38.223 回答
4

这是我基于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))
于 2013-05-01T18:01:22.657 回答
3

您可以使用诸如key-binding(或其更具体的变体global-key-bindingminor-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)
  (if (my-condition)
      (message "my-complete")
    (let ((my-complete-mode nil))
      (call-interactively (key-binding (kbd "TAB"))))))
于 2013-04-18T18:47:37.457 回答
2

您可能根本不需要任何特殊的解决方法就可以实现这一目标。在大多数模式TAB下,默认情况下只是缩进,但如果你将全局变量设置tab-always-indent'complete它会尝试先完成,如果无法完成则缩进。这通常效果很好,尽管如果TAB在您的主要模式之一中绑定到另一个命令,您可能会不走运。

如果这在您需要的模式下有效,您只需将自定义完成函数添加到completion-at-point-functions所有适用缓冲区中列表的前面(可能使用模式挂钩)。该completion-at-point命令调用列出的每个函数,completion-at-point-functions直到其中一个返回 non- nil,因此您需要做的就是让您的自定义完成函数“失败”到现有的行为是nil从它返回。

这不是问题的 100% 答案,但如果您使用的主要模式是根据正常指南编写的,那么它可能是最干净的方法。

于 2013-04-18T22:01:13.290 回答
1

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 可以用 my-tab-completion 之类的命名函数替换并更有效地使用。

来自定义键的文档字符串(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'.)
于 2015-07-20T10:06:53.903 回答