Cara mengganti binding mode utama

38

Terkadang binding kunci global saya ditimpa oleh mode utama. Contoh mudah adalah pengaturan berikut di file init saya

(global-set-key (kbd "C-j") 'newline-and-indent)

Tapi yang menjengkelkan keybinding ini disembunyikan oleh mode utama "Interaksi Lisp" yang merupakan mode default dari buffer awal.

Ketika saya menemukan diri saya dalam situasi di mana mode utama (atau mode minor) menyembunyikan kunci global saya, bagaimana saya bisa mendapatkannya kembali?

Catatan: Pertanyaan saya adalah tidak "Bagaimana saya bisa mengikat C-jke newline-and-indent'? Mode' di" Lisp Interaksi Saya tertarik pada jawaban yang jauh lebih umum tentang bagaimana menangani keymaps yang berbenturan atau pengguna keybindings yang disembunyikan oleh beberapa mode utama / minor.

nispio
sumber

Jawaban:

39

Ada juga pendekatan "jalan pintas" untuk solusi yang sama jika Anda tidak ingin mendefinisikan mode minor Anda sendiri (yang saya bicarakan dalam jawaban pertama saya).

Anda dapat menginstal use-packagepaket yang tersedia dari Melpa dan memanfaatkan bind-key*atau bind-keys*makro yang merupakan bagian dari bind-keypaket yang dikirimkan bersama use-package.

Dari dokumentasi bind-key.el:

;; If you want the keybinding to override all minor modes that may also bind
;; the same key, use the `bind-key*' form:
;;
;;   (bind-key* "<C-return>" 'other-window)

;; To bind multiple keys in a `bind-key*' way (to be sure that your bindings
;; will not be overridden by other modes), you may use `bind-keys*' macro:
;;
;;    (bind-keys*
;;     ("C-o" . other-window)
;;     ("C-M-n" . forward-page)
;;     ("C-M-p" . backward-page))
Kaushal Modi
sumber
Jika ada yang penasaran, perhatikan bahwa ini hanya menggunakan mode minor di belakang layar. Dibutuhkan pendekatan yang berbeda untuk konflik dengan mode kecil lainnya, dengan menggunakan emulation-mode-map-alistsuntuk menegakkan prioritas).
phils
Saya telah menandai ini sebagai jawaban karena kesederhanaannya. Ini pada dasarnya melakukan hal yang sama dengan jawaban ini , tetapi dengan nyaman melakukan sebagian besar pekerjaan untuk Anda di belakang layar.
nispio
@nispio Anda benar. Dari pengalaman saya satu-satunya perbedaan adalah metode yang digunakan untuk membuat binding mode minor menimpa mode lain secara global. Tetapi keuntungan dari pendekatan lain (memiliki mode minor Anda sendiri) adalah Anda dapat mematikannya sebentar untuk mencoba binding emacs asli jika Anda perlu (saya perlu melakukan itu beberapa kali).
Kaushal Modi
1
Tambahkan (bind-key* "C-M-&" 'override-global-mode), ke init Anda, dan Anda biasanya bisa mendapatkan binding dengan cepat jika perlu. Karena override-global-modeini bukan mode minor "global", Anda masih harus menonaktifkannya berdasarkan per-buffer. Jadi, jika Anda sering menonaktifkan kunci timpa global maka solusi ini tidak nyaman.
nispio
25

Anda dapat menentukan mode minor Anda sendiri dan peta kuncinya dan memilikinya yang menimpa semua mode lainnya (minor + utama). Itulah mengapa saya memilih untuk menulis mode minor saya sendiri.

Langkah-langkah untuk membuat binding kunci Anda menimpa semua binding:

  • Menentukan mode minor dan peta kunci Anda sendiri seperti yang ditunjukkan di bawah ini.
  • Aktifkan mode minor Anda secara global
  • (define-key my-mode-map (kbd "C-j") #'newline-and-indent)

Demikian pula binding kunci Anda lainnya yang diatur dalam mode minor Anda akan menimpa yang di mode lain.

Saya sangat merekomendasikan membaca posting blog oleh Christopher Wellons tentang cara menulis mode minor. Blog itu ditambah gangguan karena harus mengatur beberapa ikatan kunci nildalam beberapa mode mayor dan minor mengilhami saya untuk menulis mode minor saya sendiri.

Bagian terbaik dari menggunakan pendekatan ini adalah ketika Anda ingin memeriksa apa yang dilakukan ikatan kunci dalam konfigurasi default emacs, Anda cukup mematikan mode minor Anda; Anda kemudian menyalakannya kembali dan Anda mendapatkan kembali binding kunci kustom Anda.

;; Main use is to have my key bindings have the highest priority
;; https://github.com/kaushalmodi/.emacs.d/blob/master/elisp/modi-mode.el

(defvar my-mode-map (make-sparse-keymap)
  "Keymap for `my-mode'.")

;;;###autoload
(define-minor-mode my-mode
  "A minor mode so that my key settings override annoying major modes."
  ;; If init-value is not set to t, this mode does not get enabled in
  ;; `fundamental-mode' buffers even after doing \"(global-my-mode 1)\".
  ;; More info: http://emacs.stackexchange.com/q/16693/115
  :init-value t
  :lighter " my-mode"
  :keymap my-mode-map)

;;;###autoload
(define-globalized-minor-mode global-my-mode my-mode my-mode)

;; https://github.com/jwiegley/use-package/blob/master/bind-key.el
;; The keymaps in `emulation-mode-map-alists' take precedence over
;; `minor-mode-map-alist'
(add-to-list 'emulation-mode-map-alists `((my-mode . ,my-mode-map)))

;; Turn off the minor mode in the minibuffer
(defun turn-off-my-mode ()
  "Turn off my-mode."
  (my-mode -1))
(add-hook 'minibuffer-setup-hook #'turn-off-my-mode)

(provide 'my-mode)

;; Minor mode tutorial: http://nullprogram.com/blog/2013/02/06/
Kaushal Modi
sumber
5

Untuk membuat pengikatan global menimpa pengikatan mode mayor, cukup atur pengikatan ke nildalam mode mayor:

(define-key my-major-mode-map (kbd "C-j") nil)

Pengikatan global tidak mungkin diutamakan daripada semua mode secara umum (jika tidak, tidak ada gunanya memiliki mode utama), tetapi Anda dapat meretasnya dengan membuat mode minor sendiri dengan binding paling penting. Maka Anda setidaknya akan didahulukan dari sebagian besar (walaupun tidak semua) mode.

shosti
sumber
Apakah ini akan berfungsi untuk mode utama sembarang? Dengan kata lain, jika saya menginstal mode utama yang disebut "foo-mode" saya dapat memasukkan (define-key foo-mode (kbd "C-j") nil)file .emacs saya dan berharap ini berfungsi?
nispio
Sebenarnya Anda akan menginginkannya foo-mode-map(contoh saya dalam jawaban itu buruk), tapi ya, itu akan menonaktifkan penjilidan kunci dalam mode utama sehingga penjilidan kunci global akan digunakan sebagai gantinya (kecuali ada mode minor berbeda yang menggunakannya).
shosti
Apakah itu cukup universal bahwa keymap untuk foo-modeakan dipanggil foo-mode-map?
nispio
@nispio ya, itu berlaku untuk sebagian besar mode (walaupun ada beberapa penyamun di luar sana).
shosti
0

Anda dapat menggunakan makro ini:

(defmacro expose-global-keybinding (binding map)
  `(define-key ,map ,binding (lookup-key (current-global-map) ,binding)))

(defmacro expose-bindings (map bindings)
  `(dolist (bnd ,bindings)
     (expose-global-keybinding (kbd bnd) ,map)))

EDIT :

Lihat contoh di bawah ini:

Jika keymap X mengungguli Y global binding Anda, Anda menulis:

(expose-bindings X '("Y"))

Dan kemudian override akan menjadi 'dibatalkan'.

Renan Ranelli
sumber
makro Anda, Anda mendapat manfaat dari menggunakan backquote: `(define-key, map, binding (lookup-key (current-global-map), binding))
Sigma
Bisakah Anda mengomentari apa yang dilakukan makro, dan bagaimana menggunakannya? Tidak jelas bagi saya dari kode.
nispio
Makro pertama hanya mencari kunci di peta global dan menetapkan hasilnya ke other map, dengan demikian, mengekspos peta global yang mengikat melalui other map. Yang kedua hanya memungkinkan Anda menerapkan yang pertama untuk daftar binding.
Renan Ranelli
Maaf jika saya padat, tetapi saya masih belum sepenuhnya memahami penggunaannya. Apakah saya perlu menentukan keymap global khusus saya sendiri? Apakah itu perlu milik mode minor? Apakah saya melakukan expose-bindingspertama, dan kemudian secara global mengikat kunci-kunci itu ke perintah yang saya inginkan? Mungkin Anda bisa menunjukkan contoh apa yang mungkin saya masukkan ke file init saya untuk membuat ini berfungsi.
nispio
2
Perhatikan bahwa nama-nama makro ini adalah misnomers. Mereka tidak "mengekspos" ikatan global, tetapi mereka menduplikasi ikatan global. Jika ikatan global yang sebenarnya berubah, salinan duplikat ini tidak akan.
phils