Bagaimana saya bisa mengetahui keymap mana kunci terikat?

63

Saya telah mengembalikan kunci 'd' gnus-article-mode, tetapi perilaku lamanya masih aktif saat titik ada pada lampiran. Saya bisa melihat bahwa pemberontakan itu tidak berpengaruh di sana dengan melakukan C-h k d, tetapi tidak memberi tahu saya apa keymap yang berlaku pada saat itu, sehingga saya dapat membaliknya.

Apakah ada cara untuk mengetahuinya?

Ini adalah contoh yang tepat: Saya menggunakan kejahatan, dan saya ingin artikel berada dalam mode gerak. Untuk tata letak keyboard saya, saya telah mengkonfigurasi sebagai tombol untuk naik.

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

Untuk memastikan kunci kejahatan diperhitungkan, saya membatalkan kunci kunci gnus di peta lokal:

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

Sayangnya, saat titik berada pada lampiran, tombol 'd' tidak lagi naik tetapi menawarkan saya untuk menghapus lampiran. Saya kira pengikatan lain aktif pada saat itu, maka pertanyaannya.

Solusi Saya menggunakan di keymaps-at-pointbawah ini untuk menemukan keymap yang digunakan adalah dari properti teks. Saya kemudian melihat kode fungsi terikat untuk menemukan nama keymap gnus-mime-button-map. Kode berikut melakukan apa yang saya inginkan:

(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
sumber
3
Gunakan pseudo-code Lisp ini dan deskripsikan sebagai panduan tingkat tinggi Anda tentang bagaimana Emacs mencari kunci : Elisp manual, nodeSearching Keymaps . Lihat juga node Functions for Key Lookupdan Active Keymaps.
Drew
Ada bug yang cukup besar dengan jawaban awal saya di fungsi kedua. Saya baru saja memperbaikinya sekarang, jadi itu harus dapat menemukan keybind Anda. Bisakah kamu mencoba lagi?
Malabarba

Jawaban:

61

Emacs 25

Seperti yang disebutkan oleh @YoungFrog dalam komentar, dimulai dengan Emacs 25.1 , C-h kmetode lama yang bagus untuk menggambarkan key-bind juga akan memberi tahu Anda keymap mana kunci tersebut ditemukan.

Sebelum Emacs 25

Ada beberapa kode di sini tentang ini, tetapi tidak lengkap karena tidak mencakup semuanya. Di bawah ini adalah versi yang disempurnakan.

Kunci dapat diikat dengan 9 (!) Cara. Terima kasih kepada @Drew untuk tautan ini (juga dilengkapi dengan ini ) dengan daftar lengkap. Atas perintah diutamakan, mereka adalah:

  1. Satu set kunci khusus terminal overriding-terminal-local-map,. Ini ditentukan oleh set-transient-mapfungsi.
  2. Peta override buffer-lokal overriding-local-map,. Jika ini disetel, item 3–8 dilewati (mungkin mengapa Anda tidak melihat banyak dari ini).
  3. Pada titik melalui keymappropety teks (yang bisa pergi pada teks aktual atau overlay).
  4. Variabel yang pada dasarnya mensimulasikan berbagai set kemungkinan mode minor yang diaktifkan emulation-mode-map-alists,.
  5. Variabel di mana mode-utama dapat menimpa ikatan tombol mode-kecil minor-mode-overriding-map-alist,.
  6. Mode minor aktual , yang keybind-nya disimpan minor-mode-map-alist.
  7. Pada titik (lagi), melalui local-mapproperti teks. Jika ini ada, item 8 dilewati.
  8. Keymap buffer-local standar (di mana mode utama atau buffer-local keybinds pergi), dikembalikan oleh fungsi current-local-map.
  9. The keymap global yang , dikembalikan oleh current-global-map.

Ada juga semi-item 10. Perintah apa pun yang ditemukan melalui prosedur di atas mungkin juga telah dipetakan kembali.

Fungsi berikut menanyakan beberapa kemungkinan ini (yang paling mungkin), dan mengembalikan atau mencetak hasilnya.

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

Ada fungsi bawaan untuk masing-masing fungsi ini kecuali yang pertama, jadi kita harus membuatnya (juga versi yang ditingkatkan dari kode yang ditautkan di atas).

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

Karena Anda mengatakan perilaku aktif saat titik ada pada lampiran, ada kemungkinan keybind ini terjadi pada overlay atau properti teks.

Jika itu tidak berhasil , coba perintah berikut juga. Cukup tempatkan kursor pada lampiran, dan lakukan 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
sumber
Ini akan memberi tahu saya apakah kunci tersebut memiliki ikatan lokal, dan perintah apa yang terikat padanya, tetapi masih tidak memberi tahu saya nama keymap tempat asalnya.
nispio
@nispio jika memiliki pengikatan lokal, ia harus dari keymap mode-utama atau dari panggilan ke kunci-set-lokal. Sayangnya, tidak ada cara yang mudah untuk membedakan kedua kasus tersebut.
Malabarba
1
@Malabarba Apakah maksud Anda "Sayangnya, tidak ada cara mudah untuk membedakan kedua kasus ini?" Tentunya informasinya hadir. Juga, apakah setiap mode utama memiliki tepat satu peta mode? Jika tidak, apakah ada cara untuk mengetahui peta mode utama mana yang aktif?
nispio
1
Mulai dari yang belum dirilis emacs 25, "Ch k" akan, dalam beberapa (well, "sebagian besar" mudah-mudahan) kasus memberi tahu Anda keymap mana (lebih tepatnya: simbol yang memegang keymap itu sebagai nilainya) pengikat kunci yang diberikan adalah didefinisikan. contoh keluaran:k runs the command gnus-summary-kill-same-subject-and-select (found in gnus-summary-mode-map), which is (...)
YoungFrog
2
Bagi siapa pun yang tertarik, berikut adalah komit yang relevan yang menampilkan keymap pengikat kunci di emacs 25+.
Kaushal Modi
2

Itu tentang:

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

Misalnya untuk (my-lookup-key (kbd "C-c v v"))saya dapatkan daftar di *Message*buffer:

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

Pendekatan ini berguna untuk mencari sub-keymap yang termasuk dalam keymap tingkat yang lebih tinggi, misalnya keluaran dari:

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

adalah vc-prefix-mapyang termasuk ke dalam global-map:

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

Jika Anda memodifikasi kondisi pemfilteran untuk menyertakan keymapp- Anda akan dapat mencari awalan:

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

Jadi rst-mode-mapakan ditemukan pada keduanya:

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

Saya menyadari bahwa yang berikut ini tidak menjawab pertanyaan tentang Bagaimana saya bisa mengetahui keymap yang mana , namun ini berkaitan dengan masalah mendasar dalam hal ini tentang bagaimana memastikan bahwa dkunci tersebut berperilaku sesuai dengan yang diinginkan pengguna. Jika lebih disukai, saya dapat menghapus jawaban ini dan mengubahnya menjadi pertanyaan + jawaban lain mengenai masalah itu.

Sebagai metode untuk mengganti text-property/overlaypeta, Anda harus dapat menggunakan:

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

Sesuai Mengontrol Peta Aktif , overriding-local-mapdidahulukan dari setiap peta aktif lainnya selain overriding-terminal-local-map.

Jonathan Leech-Pepin
sumber
Ini menghancurkan segalanya: Saya tidak lagi bisa membuka pesan dalam mode ringkasan, dan kejahatan dan es berhenti bekerja.
brab
Ini menghancurkan segalanya: Saya tidak lagi bisa membuka pesan dalam mode ringkasan, dan kejahatan dan es berhenti bekerja.
Emily Hoover
0

emacs-buttons menyediakan buttons-display, yang dengan argumen awalan menampilkan visualisasi rekursif dari semua binding saat ini.

(Penafian: Saya penulis paket)

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

erjoalgo
sumber