¿Cómo vincular sus claves a mapas de teclas que aún no están cargados?

9

Utilizo use-packagepara administrar paquetes instalados y bind-keypara asignar acciones a claves personalizadas que me gustan.

Anulo la mayoría de las combinaciones de teclas predeterminadas de Emacs (por ejemplo , se C-nconvierte M-k, se C-pconvierte M-i), pero estoy de acuerdo con que otros modos anulen mi esquema de combinación de teclas. Sin embargo, a veces quiero que mi combinación de teclas persista. Quiero M-kdecir algo más que, por defecto, Gnus o Helm.

Sin embargo, todos entran en conflicto entre sí en el inicio de Emacs, porque no puedo agregar un enlace a un mapa de teclas, si no existe (porque a use-packageveces difiere la carga de un paquete). Por ejemplo, los siguientes comandos arrojan errores (por ejemplo (void-variable helm-map)), porque Helm y Gnus aún no están completamente cargados.

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

Tengo todas mis use-packageinvocaciones en un archivo y las bind-keycombinaciones de teclas personalizadas en otro archivo. No quiero poner enlaces en las use-packagellamadas, porque tal vez quiero publicar mi esquema de combinación de teclas personalizado como un paquete independiente. ¿Qué sucede si quiero que alguien que instale mi esquema también tenga anuladas las combinaciones de teclas locales de Helm y Gnus?

¿Cómo administro las combinaciones de teclas locales de modo usando bind-key, de modo que todas las teclas se configuren incluso si los paquetes se han cargado últimamente, y todas las configuraciones de teclas están dentro de un archivo?

Mirzhan Irkegulov
fuente

Respuestas:

20

Puede usar with-eval-after-loadpara diferir el enlace de teclas hasta que se haya cargado un determinado módulo (y, por lo tanto, se haya definido el mapa de teclas):

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

Use C-h v helm-mappara encontrar en qué módulo se define el mapa de teclas y, por lo tanto, qué poner en la cadena en la primera línea.


with-eval-after-loadfue introducido en Emacs 24.4. Si tiene una versión anterior de Emacs, debe usar eval-after-loaden su lugar y poner una comilla simple al frente de la bind-keyllamada:

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

Si desea realizar varias bind-keyllamadas en este formulario, with-eval-after-loadsimplemente colóquelas una tras otra, pero con las eval-after-loadque necesita envolverlas todas en 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)))
legoscia
fuente
9

Solución

Con el fin de ejecutar las cosas, después de cargar un paquete determinado, hay que poner que después :configde use-package.

Aquí hay un ejemplo usando el fragmento en su pregunta:

Fragmento # 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))

Explicación

Está bien tener los 2 fragmentos a continuación en diferentes lugares en sus emacs init.elo en cualquiera de los archivos anidados cargados / requeridos.

Fragmento # 2

(use-package gnus)

Fragmento # 3

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

La razón es que no importa cuál de los 2 fragmentos anteriores se ejecute primero.

Aquí es por qué ... a continuación es a lo que se expande Snippet # 3.

Obtiene lo siguiente haciendo M-x pp-macroexpand-last-sexpcuando el punto (cursor) está después del último paréntesis de cierre de ese fragmento.

Fragmento n. ° 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)

El fragmento anterior básicamente significa que

  • gnusse requiere primero y luego bind-keyse ejecuta el formulario.
  • Si gnusno se encuentra, verá un mensaje en el búfer * Messages * que dice que ese paquete no se pudo cargar.
  • Lanzará un error si hay algún problema en la ejecución (bind-key "M-s" #'other-window gnus-summary-mode-map)

Además, si gnusya es requerido por el Snippet # 2 anterior y es requerido nuevamente por el Snippet # 3 , no importa porque requireno vuelve a cargar un paquete si ya está cargado.


Referencia

De lo use-packagebásico en su github,

:configse puede usar para ejecutar código después de cargar un paquete. En los casos en que la carga se realiza de forma perezosa (consulte más información sobre la carga automática a continuación), esta ejecución se aplaza hasta que se produce la carga automática:

Fragmento # 5

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

Arriba ejecuta la :initsección ( (setq foo-variable t)) antes de foo cargar el paquete. Pero (foo-mode 1)en la :configsección se ejecuta después de que foo se carga.

Kaushal Modi
fuente
3

Al contrario de las otras respuestas, siempre he usado ganchos para esto:

(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)
Jesse
fuente
Yo también, pensé que esta era la forma preferida de hacerlo.
Nombre de usuario significativo
2

Como ya está utilizando bind-key, directamente desde la documentación de bind-key.el:

Si desea que la combinación de teclas anule todos los modos menores que también pueden vincular la misma clave, use el formulario `bind-key * ':

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

Para desvincular una clave dentro de un mapa de teclas (por ejemplo, para evitar que su modo principal favorito cambie un enlace que no desea anular en todas partes), use unbind-key:

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

El último formulario se desglosa si el mapa de teclas no está definido actualmente porque la definición del archivo some-other-mode-mapaún no se ha cargado. Entonces podría poner esto en use-packagefor some-other-mode(la definición del paquete some-other-mode-map), o usar with-eval-after-load:

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

Otra alternativa sería definir su propio modo menor que contenga todos los enlaces que no deberían ser anulados por los modos principales:

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