¿Cómo puedo saber en qué mapa de teclas está vinculada una tecla?


63

He recuperado la tecla 'd' gnus-article-mode, pero su comportamiento anterior todavía está activo cuando el punto está en un archivo adjunto. Puedo ver que la nueva vinculación no surtió efecto al hacerlo C-h k d, pero no me dice qué mapa de teclas está vigente en ese momento, por lo que puedo volver a vincularlo.

¿Hay alguna manera de descubrirlo?

Aquí hay un ejemplo preciso: estoy usando el mal, y quiero que los artículos estén en modo de movimiento. Para la distribución de mi teclado, he configurado 'd' como la tecla para subir.

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

Para asegurarme de que se tengan en cuenta las claves malvadas, configuro la clave gnus en el mapa local:

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

Desafortunadamente, cuando el punto está en un archivo adjunto, la tecla 'd' ya no sube, pero me ofrece eliminar el archivo adjunto. Supongo que otro enlace está activo en ese punto, de ahí la pregunta.

Solución Usé lo keymaps-at-pointsiguiente para encontrar que el mapa de teclas utilizado era de una propiedad de texto. Luego miré el código de la función enlazada para encontrar el nombre del mapa de teclas gnus-mime-button-map. El siguiente código hace lo que quiero:

(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
Utilice esta lista y descripción de pseudocódigo como guía de alto nivel sobre cómo Emacs busca una clave : manual de Elisp, nodoSearching Keymaps . Ver también nodos Functions for Key Lookupy Active Keymaps.
Dibujó el

Hubo un error bastante grande con mi respuesta inicial en la segunda función. Lo acabo de arreglar ahora, por lo que debería poder localizar su combinación de teclas. ¿Podrías intentarlo de nuevo?
Malabarba

Respuestas:


61

Emacs 25

Como mencionó @YoungFrog en los comentarios, comenzando con Emacs 25.1 , el C-h kmétodo antiguo de describir las asociaciones de teclas también le dirá en qué mapa de teclas se encontró la clave.

Antes de Emacs 25

Hay algo de código aquí sobre esto, pero está incompleto ya que no cubre todo. A continuación se muestra una versión mejorada de la misma.

Las teclas se pueden enlazar de 9 (!) Formas. Gracias a @Drew por este enlace (también complementado por esto ) con la lista completa. Por orden de precedencia, son:

  1. Un conjunto terminal específica de teclas, overriding-terminal-local-map. Esto está definido por la set-transient-mapfunción.
  2. Un mapa de anulación local al búfer, overriding-local-map. Si este está configurado, se omiten los elementos 3–8 (probablemente por qué no ve muchos de estos).
  3. En el punto a través de la keymappropiedad de texto (que podría ir en texto real o en superposiciones).
  4. Una variable que simula esencialmente diferentes conjuntos posibles de modos menores habilitados, emulation-mode-map-alists.
  5. Una variable donde las principales modos pueden anular los vínculos de teclado de menores modos, minor-mode-overriding-map-alist.
  6. Los modos menores reales , cuyas teclas están almacenadas minor-mode-map-alist.
  7. En el punto (nuevamente), a través de la local-mappropiedad de texto. Si esto existe, se omite el elemento 8.
  8. El mapa de teclas local de búfer estándar (donde se encuentran las combinaciones de teclas locales de modo principal o búfer), devuelto por la función current-local-map.
  9. El mapa de teclas global , devuelto por current-global-map.

También hay un semi-elemento 10. Cualquier comando encontrado a través del procedimiento anterior también podría haberse reasignado.

La siguiente función consulta algunas de estas posibilidades (las más probables) y devuelve o imprime el resultado.

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

Hay funciones integradas para cada una de ellas, excepto la primera, por lo que debemos crear una (también una versión mejorada del código vinculado anteriormente).

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

Como está diciendo que el comportamiento está activo cuando el punto está en un archivo adjunto, hay una buena posibilidad de que esta combinación de teclas tenga lugar en una superposición o propiedad de texto.

Si eso no funciona , pruebe el siguiente comando también. Simplemente coloque el cursor en el archivo adjunto y listo 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)))

Esto me dirá si la clave tiene un enlace local y a qué comando está vinculada, pero aún no me dice el nombre del mapa de teclas del que proviene.
nispio

@nispio si tiene un enlace local, debe ser del mapa de teclas del modo principal o de una llamada a la clave de conjunto local. Desafortunadamente, no hay una manera infalible de distinguir los dos casos.
Malabarba

1
@Malabarba ¿Quiere decir "Desafortunadamente, no hay una manera fácil de distinguir los dos casos?" Seguramente toda la información está presente. Además, ¿cada modo principal tiene exactamente un mapa de modo? Si no, ¿hay alguna manera de saber qué mapa de modo principal está activo?
nispio

1
A partir de emacs 25 que aún no se ha lanzado, "Ch k", en algunos casos (bueno, "en la mayoría" con suerte) le dirá en qué mapa de teclas (más precisamente: un símbolo que mantiene ese mapa de teclas como su valor) una combinación de teclas dada es definido. salida de ejemplo:k runs the command gnus-summary-kill-same-subject-and-select (found in gnus-summary-mode-map), which is (...)
YoungFrog

2
Para cualquier persona interesada, aquí está la confirmación relevante que muestra el mapa de teclas del enlace de clave en emacs 25+.
Kaushal Modi

2

Eso sobre:

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

Por ejemplo, para (my-lookup-key (kbd "C-c v v"))obtener la lista en el *Message*búfer:

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

Este enfoque es útil para buscar mapas de subclaves que se incluyen en mapas de teclas de nivel superior, por ejemplo, resultados de:

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

es el vc-prefix-mapque está incluido en global-map:

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

Si modifica la condición de filtrado para incluir keymapp, podrá buscar siempre prefijos:

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

Entonces rst-mode-mapse encontrará en ambos:

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

1

Soy consciente de que lo siguiente no responde a la pregunta de ¿Cómo puedo averiguar qué mapa de teclas? Sin embargo, trata el problema subyacente en este caso de cómo garantizar que la dclave se comporte de acuerdo con lo que el usuario desea. Si lo prefiero, puedo eliminar esta respuesta y convertirla en otra pregunta + respuesta con respecto a ese problema.

Como método para anular el text-property/overlaymapa, debe poder usar:

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

Según Controlling Active Maps , overriding-local-maptiene prioridad sobre cualquier otro mapa activo que no sea overriding-terminal-local-map.


Esto rompe todo: ya no puedo abrir un mensaje en modo resumen, y el mal y los carámbanos dejan de funcionar.
brab

Esto rompe todo: ya no puedo abrir un mensaje en modo resumen, y el mal y los carámbanos dejan de funcionar.
Emily Hoover

0

emacs-buttons proporciona buttons-display, que con un argumento prefijo muestra una visualización recursiva de todos los enlaces actuales.

(Descargo de responsabilidad: soy el autor del paquete)

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