手动退出临时叠加地图



在 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. 如果它不在映射中,则执行以下代码:

(为什么这种格式不正确?好的,现在它做到了)

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

因此,我真正想要的是使用适当的变量执行上面的代码。但是这段代码是闭包的一部分,我在确定闭包内部的overlaysymclearfunsymalist等值时遇到了问题。我尝试通过eval pre-command-hook来寻找clearfunsym,但奇怪的是那里什么都没有(除了另一个不相关的钩子)。

我尝试重新评估函数定义并对其进行edebug,并在(add-hook 'pre-command-hook clearfunsym)后注意到,pre-command-hook仍然nil,这让我感到困惑。我将继续深入研究源代码,也许我会重写我自己的函数版本,以另外生成一个我以后可以调用的force-clear函数,但也许有人可以看到一个更干净的解决方案。

你写道:"我在确定像覆盖符号这样的值时遇到了问题"但是,会评估覆盖符号。它具有值(生成符号"t")。它是一个名称为 t 的符号。这使得访问它变得困难,但并非不可能。对以下行的评估给出了注释的结果:

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

这同样适用于 clearfunsym,它计算为 clear-temporary-overlay-map。

还有一条评论:当您调试设置临时叠加图时,您正在点击键。可能是这些击键调用了清除临时覆盖映射和清除预命令钩子?

试试:

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

然后进入文本缩放模式 (C-+) 并查看测试前命令挂钩。为了评估我的计算机上test-pre-command-hook,给了我以下列表:

清除临时叠加地图工具提示隐藏)。

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

(((t keymap (67108912 . #[0 "301302300!!207" [1 text-scale-adjust abs] 3 "
...
(fn)" nil]) (45 . #[0 "301302300!!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只是一种保护。(也许,以前有别的东西杀死了地图?临时地图应始终位于最前面(因为set-temporary-overlay-map中的push)。有什么东西有机会在它前面放另一个键盘吗?也许,时间控制的东西?然后,您需要emulation-mode-map-alists搜索具有(make-symbol "t")键盘映射的 alist。

原始set-temporary-overlay-map非常混乱,不可读,并且依赖于许多不必要的肮脏技巧和技巧。使用词法绑定,我以一种更清晰和模块化的方式重写了函数。

  1. 修订后的函数使用词法绑定来替换急于懒惰的求值技巧(在这种情况下,它们是完全不必要的)。
  2. 原始函数不必要地为每个函数创建两个符号,(clearfunsymclear-fun基本上是一回事,后者仅用作前者的函数单元)
  3. 修改后的函数提供了一个force-overlay-clear,当满足条件时(即最后一个键不在覆盖映射中),由clear-temporary-overlay-map pre-command-hook 调用。如果用户想要手动清除此地图,也可以调用它。该函数force-overlay-clear自己的函数单元无效,因此如果调用两次,它将出错。
  4. 简化用于测试清除是否适用的代码。

我无法消除这个极其奇怪的(overlaysym (make-symbol "t")),担心其他代码可能会依赖于这个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))
  )

您可以执行以下操作:

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

最新更新