Sobrescrever globalmente a vinculação de teclas no Emacs

100

Como posso definir uma associação de chave que substitui globalmente e tem precedência sobre todas as outras associações dessa chave? Desejo substituir todos os mapas de modo principal / secundário e garantir que minha vinculação esteja sempre em vigor.

É claro que isso não funciona:

(global-set-key "\C-i" 'some-function)

Funciona text-mode, mas quando eu uso lisp-mode, C-ié rebote para lisp-indent-line.

Posso passar e substituir essa ligação em lisp-modetodos os outros modos individualmente, mas deve haver uma maneira mais fácil. Cada vez que instalo um novo modo para um novo tipo de arquivo, tenho que voltar e verificar se todas as minhas combinações de teclas não estão sendo substituídas pelo novo modo.

Quero fazer isso porque quero emular ligações que já aprendi e arraigou de outros editores.

Brian Carper
fonte

Respostas:

149

Eu uso um modo menor para todas as minhas combinações de teclas "substituir":

(defvar my-keys-minor-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "C-i") 'some-function)
    map)
  "my-keys-minor-mode keymap.")

(define-minor-mode my-keys-minor-mode
  "A minor mode so that my key settings override annoying major modes."
  :init-value t
  :lighter " my-keys")

(my-keys-minor-mode 1)

Isso tem o benefício adicional de poder desligar todas as minhas modificações de uma só vez (apenas desabilitar o modo menor) no caso de outra pessoa estar dirigindo o teclado ou se eu precisar ver o que um atalho de tecla padrão faz.

Observe que você pode precisar desligar isso no minibuffer:

(defun my-minibuffer-setup-hook ()
  (my-keys-minor-mode 0))

(add-hook 'minibuffer-setup-hook 'my-minibuffer-setup-hook)
scottfrazer
fonte
1
Parece uma boa ideia. Existe alguma maneira de garantir que seu modo menor não combata com outros modos menores?
Brian Carper,
3
Certifique-se de que seu modo menor seja o primeiro na lista de mapeamento de modo menor.
Trey Jackson,
2
Trey está certo. Normalmente, colocar isso perto do final do seu .emacs é o suficiente. Além disso, a maioria das ligações que você substituiria seriam aquelas que os modos principais estão definindo ... os modos secundários geralmente ficam fora do caminho.
scottfrazer
Eu segui essa abordagem, mas então percebi que qualquer coisa que eu vincular a Ci também é vinculada à tecla TAB. Alguma sugestão?
Steve
3
Brian Carper: Aqui está uma melhoria para lidar com modos secundários carregados subsequentemente: stackoverflow.com/questions/683425/…
phils
30

Como complemento à resposta de scottfrazer , escrevi o seguinte para que meus atalhos de teclado mantenham a precedência, mesmo que as bibliotecas carregadas posteriormente tragam novos mapas de teclado próprios.

Como os mapas de teclado podem ser gerados em tempo de compilação, load parecia o melhor lugar para fazer isso.

(add-hook 'after-load-functions 'my-keys-have-priority)

(defun my-keys-have-priority (_file)
  "Try to ensure that my keybindings retain priority over other minor modes.

Called via the `after-load-functions' special hook."
  (unless (eq (caar minor-mode-map-alist) 'my-keys-minor-mode)
    (let ((mykeys (assq 'my-keys-minor-mode minor-mode-map-alist)))
      (assq-delete-all 'my-keys-minor-mode minor-mode-map-alist)
      (add-to-list 'minor-mode-map-alist mykeys))))
phils
fonte
Colei seu script, mas não fez nenhum efeito :(
alper
@alper Eu sugiro que você poste uma pergunta com todos os detalhes relevantes, incluindo o código que você está realmente usando, e um exemplo / receita específica para reproduzir o problema.
phils
21

Instale use-package, avalie e pronto:

(require 'bind-key)
(bind-key* "C-i" 'some-function)
Mirzhan Irkegulov
fonte
5
Instalar apenas a chave de ligação é suficiente para o caso de uso, embora o pacote de uso dependa da chave de ligação.
xuchunyang de
2
Este (pacote 'bind-key') parece ser a solução mais conveniente; obrigado por compartilhar.
oligilo
Não consegui encontrar correspondência para use-package:Install package: use-package [No Match]
alper
14

Eu encontrei esta questão enquanto procurava por "emacs undefine org mode keybindings", porque eu queria desvincular o comportamento Cc Cb existente para permitir que meu mapa global enterrasse o buffer para funcionar em um buffer org.

Isso acabou sendo a solução mais simples para mim:

(add-hook 'org-mode-hook
      (lambda ()
        (local-unset-key (kbd "C-c C-b"))))
Jay Doane
fonte
1
Isso é específico do modo e não aborda o panorama geral, embora funcione para o seu único caso de uso.
RichieHH
12

Embora a resposta de Scottfrazer seja exatamente o que você pediu, mencionarei para a posteridade outra solução.

Do Manual Emacs :

"Não defina a letra Cc como uma chave em programas Lisp. As sequências que consistem em Cc e uma letra (maiúscula ou minúscula) são reservadas para os usuários; são as únicas sequências reservadas para os usuários, portanto, não as bloqueie."

Se você vincular suas ligações globais pessoais a Cc mais uma letra, "deveria" estar seguro. No entanto, isso é apenas uma convenção, e qualquer modo ainda pode substituir suas ligações.

Kirkland
fonte
3
Eu não esperava que o modo org, de todos os modos, quebrasse essa regra. `Cc Ch 'me diz que Cc a, b, c e l estão vinculados a org-agenda, org-iswitchb, org-capture e org-store-link, respectivamente.
Nate Parsons
7
Afaik, vinculá-los é a primeira etapa que o modo org sugere para usá-lo, mas o usuário deve defini-los (ou seja, não é feito por padrão) e pode escolher qualquer outro enquanto faz isso. (também, é porque essas vinculações devem ser globais, não vinculadas ao modo org major)
Nikana Reklawyks,
3

Se você quiser "sempre usar os atalhos de teclado no mapa, a menos que eu os substitua explicitamente por um mapa de modo específico" e presumindo que está usando a abordagem de scottfrazier , você deseja:

(defun locally-override (key cmd)
  (unless (local-variable-p 'my-keys-minor-mode-map)
    (set (make-variable-buffer-local 'my-keys-minor-mode-map)
         (make-sparse-keymap))
    (set-keymap-parent my-keys-minor-mode-map 
                       (default-value 'my-keys-minor-mode-map)))
  (define-key my-keys-minor-mode-map key cmd))

assim

(locally-override "\C-i" nil)

deve remover a ligação "\ Ci" do modo secundário apenas no buffer atual. Aviso: isso não foi testado, mas parece ser a abordagem certa. O objetivo de definir o pai em vez de apenas lidar com o valor global de my-keys-minor-mode-map é que quaisquer alterações posteriores no valor global sejam automaticamente refletidas no valor local.

gbrunick
fonte
2

Eu não acho que você pode. Isso é aproximadamente equivalente a dizer que você deseja definir uma variável global que não pode ser oculta por declarações de variáveis ​​locais em funções. O escopo simplesmente não funciona dessa maneira.

No entanto, pode haver uma maneira de escrever uma função elisp para percorrer a lista de modos e reatribuí-la em cada um para você.

TED
fonte
Essa ideia de escopo é tecnicamente correta, mas overriding-local-mapfoi projetada especificamente para substituir todos os outros mapas. No entanto, é perigoso usá-lo.
event_jr
2

A menos que você realmente queira fazer isso sozinho, deve verificar ao redor e ver se alguém já fez isso.

Existe um pacote para Emacs que fornece atalhos de teclado semelhantes aos do Windows. Você deve ser capaz de encontrá-lo no google.

JesperE
fonte
4
O pacote em que você está pensando é provavelmente cua-mode.
Desenhou em
1
Sim, esse é o pacote.
JesperE