如何找出一个键绑定在哪个键映射中?


63

我已在中弹回'd'键gnus-article-mode,但是当该点位于附件上时,它的旧行为仍然有效。我可以看到rebinding并没有在那里生效C-h k d,但是它并没有告诉我那时什么键盘映射是有效的,因此我可以重新绑定它。

有办法找到答案吗?

这是一个精确的示例:我正在使用邪恶,并且希望文章处于运动模式。对于我的键盘布局,我将“ d”配置为向上键。

(evil-mode 1)
(add-to-list 'evil-motion-state-modes 'gnus-article-mode)
(setq evil-emacs-state-modes (remove 'gnus-article-mode evil-emacs-state-modes))
(define-key evil-motion-state-map "d" 'evil-previous-line)

为了确保考虑到邪恶键,我在本地地图中未设置gnus键:

(defun as/alter-article-evil-map ()
  (local-unset-key "d"))
(add-hook 'gnus-article-mode-hook 'as/alter-article-evil-map)

不幸的是,当该点位于附件上时,“ d”键不再向上,但是它可以让我删除附件。我猜想那时还有其他绑定处于活动状态,因此是个问题。

解决方案我使用keymaps-at-point下面的方法从文本属性中找到使用的键盘映射。然后,我查看了绑定函数的代码以查找键映射的名称gnus-mime-button-map。以下代码实现了我想要的功能:

(defun as/alter-article-evil-map ()
  (define-key gnus-mime-button-map "d" nil))
(add-hook 'gnus-article-mode-hook 'as/alter-article-evil-map)

3
使用此伪代码Lisp和描述作为Emacs如何查找密钥的高级指南Elisp手册,nodeSearching Keymaps。另请参阅节点Functions for Key LookupActive Keymaps
提请2014年

我在第二个功能中的最初回答有一个很大的错误。我现在已经对其进行了修复,因此它应该能够找到您的按键绑定。你能再试一次吗?
马拉巴巴2014年

Answers:


61

埃马克斯25

正如@YoungFrog在评论中提到的那样,从Emacs 25.1开始C-h k,描述键绑定的古老方法也将告诉您找到该键的键映射。

在Emacs 25之前

还有一些代码 在这里 就这一点,但因为它没有面面俱到它是不完整的。下面是它的改进版本。

键可以以9(!)方式绑定。感谢@Drew 提供了完整列表的此链接(也由this进行了补充)。按照优先顺序,它们是:

  1. 特定于终端的一组键overriding-terminal-local-map。这由set-transient-map功能定义。
  2. 缓冲区局部覆盖图overriding-local-map。如果设置了此项,则将跳过第3–8项(可能是为什么您看不到其中的许多项)。
  3. 在点通过keymap文本欢迎使用属性(可以去实际文本或覆盖)。
  4. 一个基本上模拟启用的次要模式的不同可能集的变量emulation-mode-map-alists
  5. 主模式可以覆盖次模式的键绑定的变量minor-mode-overriding-map-alist
  6. 实际的次要模式,其键绑定存储在中minor-mode-map-alist
  7. 在点(再次),通过local-maptext属性。如果存在,则跳过第8项。
  8. 该函数返回的标准缓冲区本地键映射(主模式或缓冲区本地键绑定所在的位置)current-local-map
  9. 全球键盘映射,返回的current-global-map

还有一个半项目10。通过上述过程找到的任何命令也可能已被重新映射。

以下函数查询其中一些可能性(最有可能的可能性),并返回或打印结果。

(defun locate-key-binding (key)
  "Determine in which keymap KEY is defined."
  (interactive "kPress key: ")
  (let ((ret
         (list
          (key-binding-at-point key)
          (minor-mode-key-binding key)
          (local-key-binding key)
          (global-key-binding key))))
    (when (called-interactively-p 'any)
      (message "At Point: %s\nMinor-mode: %s\nLocal: %s\nGlobal: %s"
               (or (nth 0 ret) "") 
               (or (mapconcat (lambda (x) (format "%s: %s" (car x) (cdr x)))
                              (nth 1 ret) "\n             ")
                   "")
               (or (nth 2 ret) "")
               (or (nth 3 ret) "")))
    ret))

除第一个功能外,每个功能都有内置功能,因此我们必须创建一个功能(也是上面链接的代码的改进版本)。

(defun key-binding-at-point (key)
  (mapcar (lambda (keymap) (when (keymapp keymap)
                             (lookup-key keymap key)))
          (list
           ;; More likely
           (get-text-property (point) 'keymap)
           (mapcar (lambda (overlay)
                     (overlay-get overlay 'keymap))
                   (overlays-at (point)))
           ;; Less likely
           (get-text-property (point) 'local-map)
           (mapcar (lambda (overlay)
                     (overlay-get overlay 'local-map))
                   (overlays-at (point))))))

既然您说的是当附件上的指针处于活动状态,则此键绑定很有可能发生在覆盖或文本属性上。

如果那不起作用,请尝试以下命令。只需将光标放在附件上,然后执行即可M-x keymaps-at-point

(defun keymaps-at-point ()
  "List entire keymaps present at point."
  (interactive)
  (let ((map-list
         (list
          (mapcar (lambda (overlay)
                    (overlay-get overlay 'keymap))
                  (overlays-at (point)))
          (mapcar (lambda (overlay)
                    (overlay-get overlay 'local-map))
                  (overlays-at (point)))
          (get-text-property (point) 'keymap)
          (get-text-property (point) 'local-map))))
    (apply #'message
           (concat 
            "Overlay keymap: %s\n"
            "Overlay local-map: %s\n"
            "Text-property keymap: %s\n"
            "Text-property local-map: %s")
           map-list)))

这将告诉我该键是否具有本地绑定,以及该键绑定到什么命令,但是仍然不能告诉我它来自哪个键映射名称
nispio 2014年

@nispio如果具有本地绑定,则必须来自主模式的键盘映射或来自对local-set-key的调用。不幸的是,没有万无一失的方法来区分这两种情况。
马拉巴巴2014年

1
@Malabarba您是说“不幸的是,没有简单的方法可以将这两种情况区分开吗?” 当然,信息全部存在。另外,每个主要模式都只有一个模式图吗?如果不是,是否有办法判断哪个主模式映射处于活动状态?
nispio 2014年

1
从尚未发布的emacs 25开始,在某些情况下(希望是“在大多数情况下”),“ Ch k”将告诉您给定的键绑定在哪个键映射(更确切地说:将该键映射作为其值的符号)中定义。示例输出:k runs the command gnus-summary-kill-same-subject-and-select (found in gnus-summary-mode-map), which is (...)
YoungFrog

2
对于任何感兴趣的人,这里是相关的提交,它在emacs 25+中显示键绑定的键映射。
2015年

2

关于:

(defun my-lookup-key (key)
  "Search for KEY in all known keymaps."
  (mapatoms (lambda (ob) (when (and (boundp ob) (keymapp (symbol-value ob)))
                      (when (functionp (lookup-key (symbol-value ob) key))
                        (message "%S" ob))))
            obarray))

例如,(my-lookup-key (kbd "C-c v v"))我在*Message*缓冲区中获取列表:

global-map
term-pager-break-map
widget-global-map

此方法对于搜索包含在更高级别的按键图中的子按键图很有用,例如,从以下输出:

(my-lookup-key (kbd "d"))

vc-prefix-map其被纳入global-map

(eq (lookup-key global-map (kbd "C-x v")) 'vc-prefix-map)

如果您将过滤条件修改为包括keymapp-您将可以搜索前缀:

(defun my-lookup-key-prefix (key)
  "Search for KEY as prefix in all known keymaps."
  (mapatoms (lambda (ob) (when (and (boundp ob) (keymapp (symbol-value ob)))
                      (when (let ((m (lookup-key (symbol-value ob) key)))
                              (and m (or (symbolp m) (keymapp m))))
                        (message "%S" ob))))
            obarray))

因此,rst-mode-map将两个发现:

(my-lookup-key-prefix (kbd "C-c C-t"))
(my-lookup-key-prefix (kbd "C-c C-t C-t"))

1

我知道以下内容无法回答“ 如何找出哪个按键图”的问题,但是在这种情况下,它解决了潜在的问题,即如何确保d按键根据用户的要求进行操作。如果愿意,我可以删除此答案,然后转换为关于该问题的另一个问题+答案。

作为覆盖text-property/overlay地图的方法,您应该可以使用:

(let ((map (make-sparse-keymap)))
  (define-key map "d" 'evil-previous-line)
  (setq overriding-local-map map))

根据“ 控制活动地图”overriding-local-map优先于除之外的所有其他活动地图overriding-terminal-local-map


这破坏了一切:我不再能够以摘要模式打开消息,并且邪恶和冰柱停止工作。
brab 2014年

这破坏了一切:我不再能够以摘要模式打开消息,并且邪恶和冰柱停止工作。
艾米莉·胡佛

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.