Jak zastąpić powiązania trybu głównego

38

Czasami moje globalne skróty klawiszowe są zastępowane przez tryb główny. Prostym przykładem jest następujące ustawienie w moim pliku init

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

Ale irytujące jest to ukrywanie klawiszy w głównym trybie „Lisp Interaction”, który jest domyślnym trybem bufora scratch.

Kiedy znajduję się w sytuacji, w której główny tryb (lub mniejszy tryb) ukrywa moje globalne przypisanie klawiszy, jak mogę je odzyskać?

Uwaga: Moje pytanie nie jest „Jak mogę związać C-jsię newline-and-indent«? Trybie»in” Lisp Interaction Interesuje mnie znacznie bardziej ogólna odpowiedź na temat tego, jak radzić sobie z mapami klawiszy, które kolidują lub z kluczami użytkownika, które są ukryte przez jakiś tryb główny / podrzędny.

nispio
źródło

Odpowiedzi:

39

Istnieje również podejście „skrótowe” do tego samego rozwiązania, jeśli nie chcesz definiować własnego trybu podrzędnego (o którym mówię w mojej pierwszej odpowiedzi).

Możesz zainstalować use-packagepakiet dostępny w Melpa i użyć bind-key*lub bind-keys*makra, które jest częścią bind-keypakietu, który jest dostarczany use-package.

Z dokumentacji 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
źródło
Na wypadek, gdyby ktoś był ciekawy, pamiętaj, że ten tryb wykorzystuje tylko niewielki tryb za sceną. Stosuje inne podejście do konfliktów z innymi mniejszymi trybami, wykorzystując emulation-mode-map-alistsdo wymuszenia pierwszeństwa).
phils
Oznacziłem to jako odpowiedź ze względu na jego prostotę. Zasadniczo robi to samo, co ta odpowiedź , ale wygodnie wykonuje większość zadań za kulisami.
nispio
@nispio Masz rację. Z mojego doświadczenia wynika, że ​​jedyną różnicą jest metoda używana do tego, aby powiązania między mniejszymi trybami zastępowały inne tryby globalnie. Ale zaletą drugiego podejścia (posiadania własnego trybu pomniejszego) jest to, że możesz go chwilowo wyłączyć, aby wypróbować oryginalne wiązania emacs, jeśli zajdzie taka potrzeba (musiałem to zrobić kilka razy).
Kaushal Modi
1
Dodaj (bind-key* "C-M-&" 'override-global-mode)do swojego init, a zwykle możesz szybko usunąć wiązania, jeśli zajdzie taka potrzeba. Ponieważ override-global-modenie jest to „globalny” tryb podrzędny, nadal musisz go dezaktywować dla poszczególnych buforów. Jeśli więc często zdarza się dezaktywować globalne klawisze zastępowania, to rozwiązanie nie jest wygodne.
nispio
25

Możesz zdefiniować własny tryb podrzędny i jego mapę kluczy i zastąpić wszystkie pozostałe tryby (podrzędne + główne). Właśnie dlatego zdecydowałem się napisać własny tryb pomniejszy.

Kroki, aby powiązania klawiszy zastąpiły wszystkie powiązania:

  • Definiowanie własnego trybu pomocniczego i mapy klawiszy, jak pokazano poniżej.
  • Aktywuj swój tryb dodatkowy na całym świecie
  • (define-key my-mode-map (kbd "C-j") #'newline-and-indent)

Podobnie inne przypisania klawiszy ustawione w trybie podrzędnym zastąpią te w innych trybach.

Gorąco polecam przeczytanie posta na blogu autorstwa Christophera Wellonsa na temat pisania pomniejszego trybu. Ten blog oraz irytacja związana z ustawieniem wielu powiązań klawiszy nilw wielu głównych i mniejszych trybach zainspirowały mnie do napisania własnego trybu pomocniczego.

Najlepszą częścią korzystania z tego podejścia jest to, że jeśli chcesz sprawdzić, co robią przypisania klawiszy w domyślnej konfiguracji emacsa, po prostu wyłączasz tryb pomocniczy; następnie włącz go ponownie i odzyskaj niestandardowe powiązania klawiszy.

;; 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
źródło
5

Aby powiązanie globalne zastąpiło powiązanie w trybie głównym, wystarczy ustawić powiązanie nilw trybie głównym:

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

Nie jest możliwe, aby globalne wiązanie miało pierwszeństwo przed wszystkimi trybami w ogóle (w przeciwnym razie nie byłoby sensu mieć głównych trybów), ale możesz włamać się do niego, tworząc własny mniejszy tryb z najważniejszymi powiązaniami. Wtedy miałbyś przynajmniej pierwszeństwo przed większością trybów (choć niekoniecznie wszystkich).

shosti
źródło
Czy zadziała to w przypadku dowolnego trybu głównego? Innymi słowy, jeśli zainstaluję tryb główny o nazwie „tryb foo”, mogę umieścić (define-key foo-mode (kbd "C-j") nil)plik .emacs i oczekiwać, że zadziała?
nispio
Właściwie chcesz, żeby tak było foo-mode-map(mój przykład w odpowiedzi był zły), ale tak, to wyłączy przypisanie klawiszy w trybie głównym, więc zamiast tego zostanie użyte globalne przypisanie (chyba, że ​​używa innego trybu podrzędnego, który go używa).
shosti
Czy jest dość uniwersalne, że foo-modezostanie wywołana mapa klawiszy foo-mode-map?
nispio
@nispio tak, dotyczy to ogromnej większości trybów (chociaż jest tam kilku łotrzyków).
shosti
0

Możesz użyć tych makr:

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

EDYCJA :

Sprawdź przykład poniżej:

Jeśli mapa klawiszy X zastępuje globalne powiązanie Y, piszesz:

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

A wtedy zastąpienie zostanie „cofnięte”.

Renan Ranelli
źródło
makra można skorzystać z użyciem backquote: `(define-key, mapa, wiązania (lookup-key (current-global-map), oprawa))
Sigma
Czy możesz skomentować, co robi makro i jak z niego korzystać? Z kodu nie wynika mi jasno.
nispio
Pierwsze makro po prostu wyszukuje klucz na mapie globalnej i przypisuje wynik do other map, w ten sposób odsłaniając powiązanie mapy globalnej przez other map. Drugi pozwala po prostu zastosować ten pierwszy do listy powiązań.
Renan Ranelli
Przepraszam, jeśli jestem gęsty, ale nadal nie rozumiem całkowicie użycia. Czy muszę zdefiniować własną specjalną globalną mapę klawiszy? Czy musi należeć do mniejszego trybu? Czy expose-bindingsnajpierw wykonuję , a następnie globalnie wiążę te klucze z poleceniami, które chcę? Może mógłbyś pokazać przykład tego, co mógłbym umieścić w moim pliku init, aby to zadziałało.
nispio
2
Zauważ, że te nazwy makr są błędnymi nazwami. Nie „ujawniają” globalnego powiązania, ale raczej duplikują globalne powiązanie. Jeśli rzeczywiste globalne wiązanie ulegnie zmianie, te zduplikowane kopie nie zmienią się.
phils