Como vincular suas chaves a mapas de teclas que ainda não foram carregados?

9

Eu uso use-packagepara gerenciar pacotes instalados e bind-keyatribuir ações às chaves personalizadas que eu gosto.

Eu substituo a maioria das combinações de teclas padrão do Emacs (por exemplo , torna- C-nse M-k, C-ptorna-se M-i), mas estou bem com outros modos de substituir meu esquema de combinação de teclas. Às vezes, quero que minha combinação de teclas persista, no entanto. Eu quero M-kdizer algo mais, do que no padrão Gnus ou Helm.

No entanto, todos eles entram em conflito entre si na inicialização do Emacs, porque não posso adicionar uma ligação a um mapa de teclas, se ela não existir (porque use-packageàs vezes adia o carregamento de um pacote). Por exemplo, os comandos a seguir geram erros (por exemplo (void-variable helm-map)), porque Helm e Gnus ainda não estão totalmente carregados.

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

Eu tenho todas as minhas use-packagechamadas em um arquivo e bind-keypara ligações de teclas personalizadas em outro arquivo. Não quero colocar ligações em use-packagechamadas, porque talvez eu queira publicar meu esquema de ligação de teclas personalizado como um pacote autônomo. E se eu quiser que alguém que esteja instalando meu esquema também tenha bloqueado os atalhos de teclado locais Helm e Gnus?

Como gerenciar ligações de teclas locais no modo usando bind-key, para que todas as chaves sejam definidas mesmo que os pacotes sejam carregados recentemente e todas as configurações de teclas estejam dentro de um arquivo?

Mirzhan Irkegulov
fonte

Respostas:

20

Você pode usar with-eval-after-loadpara adiar a ligação de teclas até que um determinado módulo tenha sido carregado (e, assim, definido o mapa de teclas):

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

Use C-h v helm-mappara descobrir em qual módulo o mapa de teclas está definido e, portanto, o que colocar na string na primeira linha.


with-eval-after-loadfoi introduzido no Emacs 24.4. Se você possui uma versão anterior do Emacs, é necessário usá-la eval-after-loade colocar uma única citação na frente da bind-keychamada:

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

Se você deseja fazer várias bind-keychamadas neste formulário, with-eval-after-loadbasta colocá-las uma após a outra, mas com isso eval-after-loadé necessário agrupar todas elas em 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
fonte
9

Solução

Para executar as coisas depois que um determinado pacote é carregado, você precisa colocá-lo depois :configem use-package.

Aqui está um exemplo usando o snippet na sua pergunta:

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

Explicação

Não há problema em ter os 2 trechos abaixo em locais diferentes no seu emacs init.elou em qualquer um dos arquivos aninhados carregados / necessários.

Snippet # 2

(use-package gnus)

Snippet # 3

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

O motivo é que não importa qual dos 2 snippets acima é executado primeiro.

Aqui está o porquê .. abaixo está o que o snippet nº 3 se expande.

Você obtém o que está abaixo fazendo M-x pp-macroexpand-last-sexpquando o ponto (cursor) está após o último parêntese de fechamento desse 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)

O trecho acima significa basicamente que

  • gnusé necessário primeiro e, em seguida, o bind-keyformulário é executado.
  • Se gnusnão for encontrado, você verá uma mensagem no buffer * Messages * dizendo que esse pacote não pôde ser carregado.
  • Irá gerar erro se houver algum problema na execução (bind-key "M-s" #'other-window gnus-summary-mode-map)

Além disso, se gnusjá é exigido pelo Snippet # 2 acima e novamente pelo Snippet # 3 , isso não importa, porque requirenão carrega um pacote novamente se já estiver carregado.


Referência

Desde o use-packagebásico no seu github,

:configpode ser usado para executar código após o carregamento de um pacote. Nos casos em que o carregamento é feito preguiçosamente (veja mais sobre o carregamento automático abaixo), essa execução é adiada para depois que o carregamento automático ocorre:

Snippet # 5

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

Acima executa a :initseção ( (setq foo-variable t)) antes do foo pacote ser carregado. Mas (foo-mode 1)na :configseção é executada após o foo carregamento.

Kaushal Modi
fonte
3

Pelo contrário das outras respostas, sempre usei ganchos para isso:

(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
fonte
Eu também pensei que essa era a maneira preferida de fazê-lo.
Nome de usuário significativo
2

Como você já está usando a chave de ligação, diretamente da documentação de bind-key.el:

Se você deseja que a combinação de teclas substitua todos os modos secundários que também podem vincular a mesma tecla, use o formulário `bind-key * ':

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

Para desvincular uma chave em um mapa de teclas (por exemplo, para impedir que seu modo principal favorito altere uma ligação que você não deseja substituir em todos os lugares), use unbind-key:

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

O último formulário é dividido se o mapa de teclas não estiver definido no momento porque a definição do arquivo some-other-mode-mapainda não foi carregada. Então você pode colocar isso em um use-packagepara some-other-mode(a definição do pacote some-other-mode-map) ou usando with-eval-after-load:

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

Uma outra alternativa seria definir seu próprio modo secundário, contendo todas as ligações que não devem ser substituídas pelos modos principais:

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