Come associare le tue chiavi a keymap che non sono ancora state caricate?


9

Uso use-packageper gestire i pacchetti installati e bind-keyassegnare azioni alle chiavi personalizzate che mi piacciono.

Sovrascrivo la maggior parte del keybinding predefinito di Emacs (es. C-nDiventa M-k, C-pdiventa M-i), ma sto bene con altre modalità che sovrascrivono il mio schema di keybinding. A volte, tuttavia, voglio che il mio keybinding persista. Voglio M-kdire qualcos'altro, rispetto a Gnus o Helm predefiniti.

Tuttavia, sono tutti in conflitto tra loro all'avvio di Emacs, perché non riesco ad aggiungere un'associazione a una keymap, se non esiste (perché a use-packagevolte difende il caricamento di un pacchetto). Ad esempio, i seguenti comandi generano errori (ad esempio (void-variable helm-map)), poiché Helm e Gnus non sono ancora completamente caricati.

(bind-key "M-Y" 'helm-end-of-buffer helm-map)
(bind-key "M-k" 'helm-next-line helm-find-files-map)
(bind-key "M-s" 'other-window gnus-summary-mode-map)

Ho tutte le mie use-packageinvocazioni in un file e bind-keyper le combinazioni di tasti personalizzate in un altro file. Non voglio mettere i bind nelle use-packagechiamate, perché forse voglio pubblicare il mio schema di keybinding personalizzato come pacchetto autonomo. E se volessi che qualcuno che installava il mio schema avesse anche le combinazioni di tasti locali di Helm e Gnus?

Come posso gestire le associazioni di tasti in modalità locale usando bind-key, in modo che tutte le chiavi siano impostate anche se i pacchetti sono stati caricati di recente e tutte le impostazioni delle chiavi sono all'interno di un file?

Risposte:


20

È possibile utilizzare with-eval-after-loadper rinviare l'associazione dei tasti fino a quando non è stato caricato un determinato modulo (e quindi definito il keymap):

(with-eval-after-load "helm"
  (bind-key "M-Y" #'helm-end-of-buffer helm-map))

Utilizzare C-h v helm-mapper trovare in quale modulo è definita la keymap e quindi cosa inserire nella stringa sulla prima riga.


with-eval-after-loadè stato introdotto in Emacs 24.4. Se hai una versione precedente di Emacs, devi usare eval-after-loadinvece e mettere un unico preventivo davanti alla bind-keychiamata:

(eval-after-load "helm"
  '(bind-key "M-Y" #'helm-end-of-buffer helm-map))

Se si desidera effettuare più bind-keychiamate in questo modulo, with-eval-after-loadè sufficiente inserirle una dopo l'altra, ma eval-after-loadè necessario avvolgerle tutte in un progn:

(eval-after-load "helm"
  '(progn
     (bind-key "M-Y" #'helm-end-of-buffer helm-map)
     (bind-key "M-k" #'helm-next-line helm-find-files-map)))

9

Soluzione

Per eseguire roba dopo un determinato pacchetto è caricato, è necessario mettere che dopo :configa use-package.

Ecco un esempio usando lo snippet nella tua domanda:

Snippet n. 1

(use-package helm
  :config
  (progn
    (bind-key "M-Y" #'helm-end-of-buffer helm-map)
    (bind-key "M-k" #'helm-next-line helm-find-files-map)))

(use-package gnus
  :config
  (bind-key "M-s" #'other-window gnus-summary-mode-map))

Spiegazione

È OK avere i 2 frammenti di seguito in diversi punti in emacs init.elo in uno qualsiasi dei file nidificati caricati / richiesti.

Snippet # 2

(use-package gnus)

Snippet # 3

(use-package gnus
  :config
  (bind-key "M-s" #'other-window gnus-summary-mode-map))

Il motivo è che non importa quale dei 2 frammenti sopra sia eseguito per primo.

Ecco perché .. di seguito è riportato lo snippet n. 3 che si espande.

Si ottiene quanto segue facendo M-x pp-macroexpand-last-sexpquando il punto (cursore) è dopo l'ultima parentesi di chiusura dello snippet.

Snippet # 4

(if (not (require 'gnus nil t))
    (ignore (message (format "Could not load %s" 'gnus)))
  (condition-case-unless-debug err
      (bind-key "M-s" #'other-window gnus-summary-mode-map)
    (error
     (ignore
      (display-warning 'use-package
                       (format "%s %s: %s" "gnus" ":config"
                               (error-message-string err))
                       :error))))
  t)

Lo snippet di cui sopra sostanzialmente significa che

  • gnusè necessario prima e poi il bind-keymodulo viene eseguito.
  • Se gnusnon viene trovato, vedrai un messaggio nel buffer * Messaggi * che dice che quel pacchetto non può essere caricato.
  • Verrà generato un errore se si verificano problemi nell'esecuzione (bind-key "M-s" #'other-window gnus-summary-mode-map)

Inoltre, se gnusè già richiesto dallo Snippet # 2 sopra ed è richiesto nuovamente dallo Snippet # 3 , non importa perché requirenon carica nuovamente un pacchetto se è già caricato.


Riferimento

Dalle use-packagebasi del suo github,

:configpuò essere utilizzato per eseguire il codice dopo aver caricato un pacchetto. Nei casi in cui il caricamento viene eseguito pigramente (vedere di più sull'autocaricamento di seguito), questa esecuzione viene rinviata fino a quando non si verifica l'autocaricamento:

Snippet # 5

(use-package foo
  :init
  (setq foo-variable t)
  :config
  (foo-mode 1))

Sopra esegue la :initsezione ( (setq foo-variable t)) prima che il foo pacchetto venga caricato. Ma (foo-mode 1)nella :configsezione viene eseguito dopo il foo caricamento.


3

Al contrario delle altre risposte, ho sempre usato ganci per questo:

(defun my-company-maps()
  (define-key company-active-map "\C-x\M-h" 'company-show-doc-buffer)
  (define-key company-active-map "\C-n" 'company-select-next)
  (define-key company-active-map "\C-p" 'company-select-previous)
  (define-key company-active-map "\C-h" 'delete-backward-char))

(add-hook 'company-mode-hook 'my-company-maps)

Anch'io, ho pensato che questo fosse il modo preferito di farlo.
Nome utente significativo

2

Dato che stai già utilizzando bind-key, direttamente dalla documentazione di bind-key.el:

Se vuoi che il keybinding abbia la precedenza su tutte le modalità minori che possono anche associare la stessa chiave, usa il modulo `bind-key * ':

(bind-key* "<C-return>" 'other-window)

Per separare una chiave all'interno di una keymap (ad esempio, per impedire alla modalità principale preferita di modificare un'associazione che non si desidera ignorare ovunque), utilizzare unbind-key:

(unbind-key "C-c x" some-other-mode-map)

L'ultimo modulo si interrompe se la keymap non è attualmente definita perché il file che definisce some-other-mode-mapnon è ancora stato caricato. Quindi potresti metterlo in un use-packagefor some-other-mode(la definizione del pacchetto some-other-mode-map), oppure usando with-eval-after-load:

(with-eval-after-load 'some-other-mode
  (unbind-key "C-c x" some-other-mode-map))

Un'altra alternativa sarebbe la definizione della propria modalità minore contenente tutti i binding che non devono essere ignorati dalle modalità principali:

(defvar my-very-own-keymap (make-keymap) "my very own keymap.")

(define-key my-very-own-keymap (kbd "M-i") 'my-foo)

(define-minor-mode my-very-own-keys-minor-mode
  "Minor mode with my very own keybindings."
  t " my-own-keys" my-very-own-keymap)
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.