4

In emacs 24, set-temporary-overlay-map makes active a keymap which becomes inactivated as soon as the user presses a key which is not defined in that keymap.

I need to inactivate the overlay keymap manually, but no function is provided to do this in particular. I've peeked into the source code:

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

I gather that the mechanism to inactivate the current overlay keymap is as follows:

  1. A function clearfun is defined to run before every command, checking if the previous command invoked was in the map.
  2. If it was not in the map, the following code is executed:

(Why doesn't this format correctly? Ok, now it does)

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

Thus, what I really want is to execute the code above with the appropriate variables. But this code is part of a closure, and I'm having trouble determining values like overlaysym, clearfunsym, alist inside the closure. I tried looking for clearfunsym by eval-ing pre-command-hook, but strangely nothing is there (except for another unrelated hook).

I tried re-evaluating the function defintion and edebugging it, and I notcied after the (add-hook 'pre-command-hook clearfunsym), pre-command-hook is still nil, which puzzles me. I will continue digging deeper into the source code, and maybe I will just rewrite my own version of this function to additionally produce a force-clear function that I can call later, but maybe someone can see a cleaner solution.

4

3 回答 3

2

您写道:“我无法确定像 overlaysym 这样的值” 但是,overlaysym 已被评估。它具有值(生成符号“t”)。它是一个名为 t 的符号。这使得访问它变得困难,但并非不可能。对以下行的评估给出了注释掉的结果:

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

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

还有一条评论:当您调试 set-temporary-overlay-map 时,您正在按下键。可能是这些击键调用 clear-temporary-overlay-map 和 clear 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给了我以下列表:

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

让我们对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只是一种保护。(也许,以前有其他东西杀死了地图?)临时地图应该总是在前面(因为pushin set-temporary-overlay-map)。有什么机会在它前面放另一个键盘映射吗?也许,时间控制的东西?然后您需要emulation-mode-map-alists使用键盘映射搜索 alist (make-symbol "t")

于 2013-10-11T17:32:08.263 回答
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)))))
....
于 2013-10-14T16:35:14.817 回答
0

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

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

我无法消除极其奇怪的(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))
  )
于 2013-10-13T15:00:10.987 回答