Come posso sapere in quale keymap è associata una chiave?


63

Ho rimbalzato la chiave 'd' gnus-article-mode, ma il suo vecchio comportamento è ancora attivo quando il punto è su un allegato. Posso vedere che il rebinding non ha avuto effetto lì facendo C-h k d, ma non mi sta dicendo quale keymap è in vigore a quel punto, in modo da poterlo ricollegare.

C'è un modo per scoprirlo?

Ecco un esempio preciso: sto usando il male e voglio che gli articoli siano in modalità movimento. Per il layout della mia tastiera ho configurato "d" come tasto per salire.

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

Per assicurarmi che le chiavi malvagie siano prese in considerazione, ho disattivato la chiave gnus nella mappa locale:

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

Sfortunatamente, quando il punto è su un allegato, il tasto 'd' non sale più ma mi offre di cancellare l'allegato. Immagino che a quel punto sia attivo un altro legame, quindi la domanda.

Soluzione Ho usato quanto keymaps-at-pointsegue per trovare la keymap usata da una proprietà text. Ho quindi esaminato il codice della funzione associata per trovare il nome della mappa di tasti gnus-mime-button-map. Il seguente codice fa quello che voglio:

(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
Usa questo pseudo-codice Lisp e la descrizione come guida di alto livello su come Emacs cerca una chiave : manuale Elisp, nodoSearching Keymaps . Vedi anche nodi Functions for Key Lookupe Active Keymaps.
Tracciò il

Si è verificato un errore piuttosto grande con la mia risposta iniziale nella seconda funzione. L'ho appena risolto ora, quindi dovrebbe essere in grado di individuare il tuo keybind. Potresti riprovare?
Malabarba,

Risposte:


61

Emacs 25

Come menzionato da @YoungFrog nei commenti, a partire da Emacs 25.1 , il buon vecchio C-h kmetodo per descrivere i key-binding ti dirà anche in quale keymap è stata trovata la chiave.

Prima di Emacs 25

C'è del codice qui su questo, ma è incompleto in quanto non copre tutto. Di seguito è una versione migliorata di esso.

Le chiavi possono essere associate in 9 (!) Modi. Grazie a @Drew per questo link (integrato anche da questo ) con l'elenco completo. Per ordine di precedenza, sono:

  1. Un insieme specifico terminale di chiavi, overriding-terminal-local-map. Questo è definito dalla set-transient-mapfunzione.
  2. Una mappa di override del buffer locale, overriding-local-map. Se questo è impostato, le voci da 3 a 8 vengono ignorate (probabilmente perché non ne vedi molte).
  3. Al punto tramite il keymaptesto-propety (che potrebbe andare sul testo reale o sugli overlay).
  4. Una variabile che simula essenzialmente differenti insiemi possibili di attivare minori-modi, emulation-mode-map-alists.
  5. Una variabile dove grandi-modi possono ignorare le keybinds di minori-modi, minor-mode-overriding-map-alist.
  6. Le modalità secondarie effettive , in cui sono archiviate le combinazioni di tasti minor-mode-map-alist.
  7. Al punto (di nuovo), tramite la local-mapproprietà text. Se esiste, l'articolo 8 viene ignorato.
  8. La keymap buffer-locale standard (dove vanno le combinazioni di tasti in modalità principale o buffer-locale), restituita dalla funzione current-local-map.
  9. La keymap globale , restituita da current-global-map.

C'è anche un semi-oggetto 10. Qualsiasi comando sia stato trovato attraverso la procedura sopra potrebbe essere stato rimappato.

La seguente funzione richiede alcune di queste possibilità (le più probabili) e restituisce o stampa il risultato.

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

Esistono funzioni integrate per ognuna di queste ad eccezione della prima, quindi dobbiamo crearne una (anche una versione migliorata del codice linkato sopra).

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

Dato che stai dicendo che il comportamento è attivo quando il punto è su un allegato, ci sono buone probabilità che questo keybind abbia luogo su una sovrapposizione o su una proprietà text.

Se non funziona , prova anche il seguente comando. Posiziona il cursore sull'allegato e fai 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)))

Questo mi dirà se la chiave ha un'associazione locale e a quale comando è associata, ma non mi dice ancora il nome della keymap da cui proviene.
nispio,

@nispio se ha un'associazione locale, deve provenire dalla keymap della modalità principale o da una chiamata a local-set-key. Sfortunatamente, non esiste un modo infallibile per distinguere i due casi.
Malabarba,

1
@Malabarba Intendi "Sfortunatamente, non esiste un modo semplice per distinguere i due casi?" Sicuramente le informazioni sono tutte presenti. Inoltre, ogni modalità principale ha esattamente una mappa delle modalità? In caso contrario, c'è un modo per dire quale mappa delle modalità principali è attiva?
nispio,

1
A partire da emacs 25 ancora da rilasciare, "Ch k" ti dirà, in alcuni (bene, "nella maggior parte dei casi" si spera) in quale keymap (più precisamente: un simbolo che trattiene quella keymap come valore) un dato keybinding è definito. esempio di output:k runs the command gnus-summary-kill-same-subject-and-select (found in gnus-summary-mode-map), which is (...)
YoungFrog,

2
Per chiunque sia interessato, ecco il commit pertinente che mostra la keymap dell'associazione chiave in emacs 25+.
Kaushal Modi,

2

Quello su:

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

Ad esempio per (my-lookup-key (kbd "C-c v v"))ottenere l'elenco nel *Message*buffer:

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

Questo approccio è utile per la ricerca di sotto-keymap che includeva in keymap di livello superiore, ad esempio output da:

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

è vc-prefix-mapincluso in global-map:

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

Se modifichi la condizione di filtro da includere keymapp, sarai in grado di cercare sempre prefissi:

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

Quindi rst-mode-mapsarà trovato su entrambi:

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

1

Sono consapevole che quanto segue non risponde alla domanda su Come posso scoprire quale keymap , tuttavia tratta il problema sottostante in questo caso su come garantire che la dchiave si comporti in base a ciò che l'utente desidera. Se preferisco, posso rimuovere questa risposta e convertirla in un'altra domanda + risposta relativa a quel problema.

Come metodo per sovrascrivere la text-property/overlaymappa, dovresti essere in grado di utilizzare:

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

Come da Controllo delle mappe attive , overriding-local-mapha la precedenza su ogni altra mappa attiva diversa da overriding-terminal-local-map.


Questo rompe tutto: non riesco più ad aprire un messaggio in modalità di riepilogo e male e ghiaccioli smettono di funzionare.
brab

Questo rompe tutto: non riesco più ad aprire un messaggio in modalità di riepilogo e male e ghiaccioli smettono di funzionare.
Emily Hoover,

0

emacs-buttons fornisce buttons-display, che con un argomento prefisso mostra una visualizzazione ricorsiva di tutti i binding correnti.

(Dichiarazione di non responsabilità: sono l'autore del pacchetto)

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.