Como substituir ligações de modo principais

38

Às vezes, minhas combinações de teclas globais são substituídas por um modo principal. Um exemplo fácil é a seguinte configuração no meu arquivo init

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

Mas irritantemente essa combinação de teclas é ocultada pelo modo principal "Interação Lisp", que é o modo padrão do buffer temporário.

Quando me encontro em uma situação em que um modo principal (ou modo secundário) está ocultando minha combinação de teclas global, como posso recuperá-lo?

Nota: A minha pergunta não é "Como posso vincular C-ja newline-and-indent'? Mode' em" Lisp Interaction Estou interessado em uma resposta muito mais geral sobre como lidar com mapas de teclas que se chocam ou com combinações de teclas de usuário que ficam ocultas por algum modo principal / secundário.

nispio
fonte

Respostas:

39

Também existe uma abordagem de "atalho" para a mesma solução, se você não quiser definir seu próprio modo secundário (sobre o qual falo na minha primeira resposta).

Você pode instalar o use-packagepacote disponível a partir Melpa e fazer uso bind-key*ou bind-keys*macro que é parte do bind-keypacote que vem com use-package.

A partir da documentação de 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
fonte
Caso alguém esteja curioso, observe que isso apenas usa um modo menor nos bastidores. É preciso uma abordagem diferente para conflitos com outros modos secundários, usando emulation-mode-map-alistspara impor precedência).
phils
Eu marquei isso como a resposta por causa de sua simplicidade. Essencialmente, está fazendo o mesmo que esta resposta , mas está convenientemente fazendo a maior parte do trabalho para você nos bastidores.
Nispio 12/10
@nispio Você está correto. Pela minha experiência, a única diferença é o método usado para fazer com que as ligações do modo secundário substituam outros modos globalmente. Mas a vantagem da outra abordagem (de ter seu próprio modo secundário) é que você pode desativá-la momentaneamente para experimentar as ligações originais do emacs, se você precisar (eu precisei fazer isso algumas vezes).
precisa
11
Adicione (bind-key* "C-M-&" 'override-global-mode)ao seu init e, geralmente, você pode obter rapidamente os vínculos, se necessário. Como override-global-modenão é um modo secundário "global", você ainda precisará desativá-lo por buffer. Portanto, se você costuma desativar as chaves de substituição global, essa solução não é conveniente.
Nispio
25

Você pode definir seu próprio modo secundário e seu mapa de teclas e fazer com que todos os outros modos sejam substituídos (menor + maior). Foi exatamente por isso que escolhi escrever meu próprio modo secundário.

Etapas para que suas ligações de teclas substituam todas as ligações:

  • Definindo seu próprio modo secundário e mapa de teclas, como mostrado abaixo.
  • Ative seu modo secundário globalmente
  • (define-key my-mode-map (kbd "C-j") #'newline-and-indent)

Da mesma forma, as outras combinações de teclas definidas no modo secundário substituirão as dos outros modos.

Eu recomendo a leitura do post de Christopher Wellons sobre como escrever um modo menor. Esse blog mais o aborrecimento de ter que definir várias ligações de teclas nilem vários modos principais e secundários me inspiraram a escrever meu próprio modo secundário.

A melhor parte do uso dessa abordagem é que, quando você deseja verificar o que as associações de teclas fazem na configuração padrão do emacs, você simplesmente desativa o modo secundário; você o ativa novamente e recupera suas associações de teclas personalizadas.

;; 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
fonte
5

Para que uma ligação global substitua uma ligação do modo principal, simplesmente defina a ligação nilno modo principal:

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

Não é possível fazer com que a ligação global tenha precedência sobre todos os modos em geral (caso contrário, não faria sentido ter modos principais), mas você poderia invadir isso criando seu próprio modo secundário com as ligações mais importantes. Então você teria pelo menos precedência sobre a maioria dos modos (embora não necessariamente todos).

shosti
fonte
Isso funcionará para qualquer modo principal arbitrário? Em outras palavras, se eu instalar um modo principal chamado "foo-mode", posso colocar (define-key foo-mode (kbd "C-j") nil)no meu arquivo .emacs e esperar que isso funcione?
Nispio 29/09/14
Na verdade, você deseja que seja foo-mode-map(meu exemplo na resposta foi ruim), mas sim, isso desativará a combinação de teclas no modo principal, para que a combinação global de teclas seja usada (a menos que exista um modo secundário diferente).
shosti 29/09/14
É bastante universal que o mapa-chave para foo-modeseja chamado foo-mode-map?
Nispio 29/09/14
@nispio sim, isso é verdade para a grande maioria dos modos (embora existam alguns bandidos por aí).
shosti 29/09/14
0

Você pode usar estas macros:

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

EDIT :

Veja o exemplo abaixo:

Se o mapa de teclas X estiver substituindo sua ligação global Y, você escreve:

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

E então a substituição será 'desfeita'.

Renan Ranelli
fonte
sua macro da qual você se beneficia usando a cota posterior: `(chave de definição, mapa, ligação (chave de pesquisa (mapa global atual), ligação)))
Sigma
Você pode comentar o que a macro está fazendo e como usá-la? Não está claro para mim a partir do código.
Nispio 27/09/14
A primeira macro apenas consulta a chave no mapa global e atribui o resultado a other map, portanto, expondo a ligação do mapa global por meio do other map. O segundo apenas permite aplicar o primeiro a uma lista de ligações.
Renan Ranelli
Desculpe se estou sendo denso, mas ainda não entendo completamente o uso. Preciso definir meu próprio mapa de teclas global especial? Ele precisa pertencer a um modo menor? Realizo expose-bindingsprimeiro e depois vinculo globalmente essas chaves aos comandos que desejo? Talvez você possa mostrar um exemplo do que eu poderia colocar no meu arquivo init para fazer isso funcionar.
Nispio 7/10
2
Observe que esses nomes de macro são desnômeros. Eles não "expõem" a ligação global, mas duplicam a ligação global. Se a ligação global real for alterada, essas cópias duplicadas não serão.
phils