Как я могу узнать, к какой карте ключей привязан ключ?

63

Я получил отскок от клавиши 'd' gnus-article-mode, но его старое поведение все еще активно, когда точка находится во вложении. Я вижу, что повторное связывание не вступило в силу 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ниже, чтобы найти используемую раскладку ключей из свойства text. Затем я посмотрел на код связанной функции, чтобы найти имя карты ключей 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)
brab
источник
3
Используйте этот псевдокод Lisp и описание в качестве высокоуровневого руководства о том, как Emacs ищет ключ : руководство Elisp, узелSearching Keymaps . Смотрите также узлы Functions for Key Lookupи Active Keymaps.
Дрю
Была довольно большая ошибка с моим первоначальным ответом во второй функции. Я только что исправил это сейчас, поэтому он должен быть в состоянии найти вашу привязку клавиш. Не могли бы вы попробовать еще раз?
Малабарба

Ответы:

61

Emacs 25

Как упомянуто @YoungFrog в комментариях, начиная с Emacs 25.1 , старый добрый C-h kметод описания привязок клавиш также скажет вам, в какой карте ключей был найден ключ.

До Emacs 25

Там какой - код здесь на этом, но это неполное , поскольку он не охватывает все. Ниже улучшенная версия этого.

Ключи можно связать 9 (!) Способами. Спасибо @Drew за эту ссылку (также дополненную этим ) с полным списком. В порядке очередности они являются:

  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-mapсвойство text. Если это существует, пункт 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)))
Malabarba
источник
Это скажет мне, имеет ли ключ локальную привязку, и к какой команде он привязан, но он все равно не скажет мне имя таблицы ключей, из которой он получен .
Nispio
@nispio, если он имеет локальную привязку, он должен быть из таблицы ключей основного режима или из вызова local-set-key. К сожалению, нет надежного способа разграничить два случая.
Малабарба
1
@ Malabarba Вы имеете в виду "К сожалению, нет простого способа разграничить два случая?" Конечно, информация все присутствует. Кроме того, у каждого основного режима есть только одна карта режима? Если нет, есть ли способ узнать, какая карта основного режима активна?
Ниспио
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+.
Каушал Моди
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"))
gavenkoa
источник
1

Мне известно, что следующее не дает ответа на вопрос « Как узнать, какая карта ключей» , однако в данном случае она касается основной проблемы, заключающейся в том, как обеспечить dповедение ключа в соответствии с желаниями пользователя. При желании я могу удалить этот ответ и преобразовать его в другой вопрос + ответ по этой проблеме.

В качестве метода переопределения text-property/overlayкарты вы должны использовать:

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

В соответствии с Controlling Active Maps , overriding-local-mapимеет приоритет над любой другой активной картой, кроме overriding-terminal-local-map.

Джонатан Лич-Пепин
источник
Это ломает все: я больше не могу открыть сообщение в режиме резюме, и зло и сосульки перестают работать.
Брэб
Это ломает все: я больше не могу открыть сообщение в режиме резюме, и зло и сосульки перестают работать.
Эмили Гувер
0

Обеспечивает emacs-buttonbuttons-display , который с префиксным аргументом отображает рекурсивную визуализацию всех текущих привязок.

(Отказ от ответственности: я автор пакета)

https://github.com/erjoalgo/emacs-buttons/blob/master/doc/img/sample-visualization.png

erjoalgo
источник