手动退出临时叠加层地图

时间:2013-10-11 03:22:08

标签: emacs closures elisp key-bindings

在emacs 24中,set-temporary-overlay-map激活一个键盘图,一旦用户按下该键盘图中未定义的键,该键盘图就会失效。

我需要手动停用覆盖键映射,但没有提供任何功能来执行此操作。我偷看了源代码:

(defun set-temporary-overlay-map (map &optional keep-pred)
  "Set MAP as a temporary keymap taking precedence over most other keymaps.
Note that this does NOT take precedence over the \"overriding\" maps
`overriding-terminal-local-map' and `overriding-local-map' (or the
`keymap' text property).  Unlike those maps, if no match for a key is
found in MAP, the normal key lookup sequence then continues.

Normally, MAP is used only once.  If the optional argument
KEEP-PRED is t, MAP stays active if a key from MAP is used.
KEEP-PRED can also be a function of no arguments: if it returns
non-nil then MAP stays active."
  (let* ((clearfunsym (make-symbol "clear-temporary-overlay-map"))
         (overlaysym (make-symbol "t"))
         (alist (list (cons overlaysym map)))
         (clearfun
          ;; FIXME: Use lexical-binding.
          `(lambda ()
             (unless ,(cond ((null keep-pred) nil)
                            ((eq t keep-pred)
                             `(eq this-command
                                  (lookup-key ',map
                                              (this-command-keys-vector))))
                            (t `(funcall ',keep-pred)))
               (set ',overlaysym nil)   ;Just in case.
               (remove-hook 'pre-command-hook ',clearfunsym)
               (setq emulation-mode-map-alists
                     (delq ',alist emulation-mode-map-alists))))))
    (set overlaysym overlaysym)
    (fset clearfunsym clearfun)
    (add-hook 'pre-command-hook clearfunsym)
    ;; FIXME: That's the keymaps with highest precedence, except for
    ;; the `keymap' text-property ;-(
    (push alist emulation-mode-map-alists)))

我认为停用当前叠加键映射的机制如下:

  1. 函数clearfun定义为在每个命令之前运行,检查调用的上一个命令是否在地图中。
  2. 如果它不在地图中,则执行以下代码:
  3. (为什么这种格式不正确?好的,现在确实如此)

    (set ',overlaysym nil)   ;Just in case.
                   (remove-hook 'pre-command-hook ',clearfunsym)
                   (setq emulation-mode-map-alists
                         (delq ',alist emulation-mode-map-alists))
    

    因此,我真正想要的是用适当的变量执行上面的代码。但是这段代码是闭包的一部分,而我在闭包内确定overlaysymclearfunsymalist等值时遇到了问题。我试图通过clearfunsym - eval寻找pre-command-hook,但奇怪的是没有任何东西(除了另一个无关的钩子)。

    我尝试重新评估功能定义并对其进行操作,我在(add-hook 'pre-command-hook clearfunsym)pre-command-hook仍为nil之后发现,这让我很困惑。我将继续深入研究源代码,也许我只是重写我自己的这个函数版本,以便另外生成一个force-clear函数,我可以稍后调用,但也许有人可以看到更清晰的解决方案。

3 个答案:

答案 0 :(得分:1)

您写道:"我无法确定像overlaysym" 但是,评估了overlaysym。它具有值(make-symbol" t")。 它是名称为t的符号。这使得很难访问它,但并非不可能。 对以下行的评估给出了评论结果:

(setq mysym (make-symbol "t"))
;; t
(set mysym 'test)
;; test
(symbol-value mysym)
;; test

同样适用于clearfunsym,其评估为clear-temporary-overlay-map。

还有一条评论:当您调试set-temporary-overlay-map时,您正在按键。可能是这些击键调用clear-temporary-overlay-map并清除pre-command-hook?

试试:

(defadvice set-temporary-overlay-map (after test activate)
  (setq test-pre-command-hook pre-command-hook))

然后输入text-scale-mode(C- +)并查看test-pre-command-hook。对于在我的计算机上评估test-pre-command-hook的评估,我给出了以下列表:

clear-temporary-overlay-map tooltip-hide)。

让我们对emulation-mode-map-alists做同样的事情。然后我们得到:

(((t keymap (67108912 . #[0 "\301\302\300!!\207" [1 text-scale-adjust abs] 3 "
...

(fn)" nil]) (45 . #[0 "\301\302\300!!\207" [1 text-scale-adjust abs] 3 "
(fn)" nil]))))

特别注意开头的 t 。这意味着您可以通过搜索开头带有符号t的列表来找到叠加层地图。

类似下面的代码片段应该足以删除叠加图:

(when (assoc-string "t" (car emulation-mode-map-alists))
  (setq emulation-mode-map-alists (cdr emulation-mode-map-alists)))

when只是一种保护。 (也许,其他东西之前杀死了地图?)临时地图应该始终位于前面(因为push中的set-temporary-overlay-map)。有什么事情有机会在它面前放置另一个键盘图吗?也许,有时间控制的东西?然后,您需要使用emulation-mode-map-alists键盘映射(make-symbol "t")搜索alist。

答案 1 :(得分:0)

原始的set-temporary-overlay-map非常令人困惑,难以理解,依赖于许多不必要的肮脏黑客和伎俩。使用词法绑定,我已经以更清晰和模块化的方式重写了该函数。

  1. 修订后的函数使用词法绑定来替换急切的评估黑客(在这种情况下,它们完全没必要)。
  2. 原始函数不必要地为每个函数创建两个符号,(clearfunsymclear-fun基本相同,后者仅用作前者的函数单元格。
  3. 修订后的函数提供force-overlay-clear,当满足条件时,clear-temporary-overlay-map预命令挂钩调用force-overlay-clear(即,最后一个键不在叠加图中)。如果用户想要手动清除此地图,也可以调用它。该函数使(overlaysym (make-symbol "t"))自己的函数单元无效,因此如果调用两次则会出错。
  4. 测试clear是否适用的代码简化。
  5. 我无法消除极其奇怪的t,担心其他一些代码可能会依赖此(defun set-temporary-overlay-map (map &optional keep-pred) (lexical-let* ( (map map) (keep-pred keep-pred) (clear-temporary-overlay-map nil) (force-overlay-clear nil) (overlaysym (make-symbol "t")) (alist (list (cons overlaysym map)))) (fset 'force-overlay-clear (lambda () (message "clearing overlay") ;this is a copy of the original code to clear (set overlaysym nil) ;Just in case. (remove-hook 'pre-command-hook 'clear-temporary-overlay-map) (setq emulation-mode-map-alists (delq alist emulation-mode-map-alists)) ;void the function cell of 'force-overlay-clear', an attempt to clear overlay twice will err (fset 'force-overlay-clear nil) )) (fset 'clear-temporary-overlay-map (lambda () (unless (cond ((null keep-pred) nil) (keep-pred (lookup-key map (this-command-keys-vector))) (t (funcall keep-pred))) (force-overlay-clear) ))) (set overlaysym overlaysym) (add-hook 'pre-command-hook 'clear-temporary-overlay-map) ;; FIXME: That's the keymaps with highest precedence, except for ;; the `keymap' text-property ;- (push alist emulation-mode-map-alists)) ) 符号。因此,修订版几乎肯定等同于原始版本。我测试了这个,它运行得很好。

    {{1}}

答案 2 :(得分:0)

您可以执行以下操作:

(defun my-exit-command ()
  (do-what-the-q-key-should-do))

....(set-temporary-overlay-map
     my-overlay-map
     (lambda ()
       (and (eq this-command (lookup-key my-overlay-map
                                         (this-single-command-keys)))
            (not (eq this-command 'my-exit-command)))))
....