Evil: mapear atalhos de teclado da maneira vim?

13

Eu estou tentando fazer a função do mal evil-jump-to-tag, C-]comportam-se como ligando as Emacs M-..

O comportamento normal é bom para navegar nos arquivos Tags, mas quero que ele funcione também para Slime slime-edit-definition, Elisps elisp-slime-nav-find-elisp-thing-at-point, Clojures cider-jump-to-var, etc.

Esses modos principais e muitos outros vincularam algum equivalente de salto para definição à combinação de teclas M-..

Para obter o mesmo comportamento no modo Evil, preciso vincular uma combinação de teclas localmente para cada um desses modos ou é possível usar uma combinação de teclas e informar ao Emacs que, sempre que essa tecla for pressionada, use a função associada a essa tecla no modo Emacs?

Martin
fonte
Relacionado (uma abordagem mais direta do tipo vim): emacs.stackexchange.com/q/12287/8283
idbrii

Respostas:

10

Estou trabalhando agora, graças às suas respostas:

(defun my-jump-to-tag ()
  (interactive)
  (evil-emacs-state)
  (call-interactively (key-binding (kbd "M-.")))
  (evil-change-to-previous-state (other-buffer))
  (evil-change-to-previous-state (current-buffer)))

(define-key evil-normal-state-map (kbd "C-]") 'my-jump-to-tag)

Isso definirá o estado mau como "Emacs", chamará a função vinculada a M-. E voltará ao estado emacs anterior no outro buffer. Eu tentei com elisp, slime and go e está funcionando para todos eles.

Martin
fonte
1
O que eu uso é mais simples e parece funcionar totalmente bem: (define-key evil-normal-state-map (kbd "C-]") (kbd "\\ M-.")(onde "\" está vinculado evil-execute-in-emacs-state).
Shosti
@ shosti: Sim, isso deve funcionar pelo menos também. Já fiz assim, mas não inclui o espaço entre a segunda \ e M.
martin
3

Tente algo como

(global-set-key "\C-]" "\M-.")

ou, se eviljá usa esse atalho de teclas, pode ser necessário fazer algo parecido.

(define-key evil-mode-map "\C-]" "\M-.")

Isso substituirá completamente o comportamento de C-], se você quiser manter o comportamento do mal, dependendo do modo principal atual, a solução do @ Tyler é mais apropriada porque você pode ter uma função que decide se deseja chamarM-. ou fazer alguma coisa.

Isso ajuda?

Malabarba
fonte
2

Eu não entendo evilos mapas de teclas, mas a seguinte função faz o que M-.estiver vinculado no momento:

(defun my-tag-jump ()
    (interactive)
    (call-interactively (key-binding (kbd "M-."))))

Vincular isso ao evilmapa de teclas apropriado deve fazer o que você deseja. Pode haver maisevil maneira específica de fazer isso.

evilse liga C-]em evil-motion-state-map, por isso, tente o seguinte:

(eval-after-load "evil-maps"
    '(define-key evil-motion-state-map "\C-]" 'my-tag-jump))
Tyler
fonte
Eu não vejo como isso poderia funcionar assim, porque se liga de modo mal M-.para evil-repeat-pop-next. Se você mudar sua função para isso: (defun my-tag-jump () (interativo) (evil-emacs-state) (chame interativamente (key-binding (kbd "M-."))) (Evil-normal- state))
martin
Como eu disse, não conheço mapas mal-intencionados. Talvez a melhor solução seja encontrar o mapa de teclas onde evil-jump-to-tagestá definido e religá-lo à minha função lá.
Tyler
2

Em geral, não é possível.

O motivo é que pode haver vários mapas que definem a mesma ligação e não há como descobrir automaticamente qual você deseja. (no seu exemplo, elisp-slime-nav-modeé um modo tão secundário). Portanto, a única abordagem realmente confiável é você descobrir exatamente qual definição deseja.

Dito isso ... existe um possível hack (nem sempre existe ...) Parte do que torna complicado é que a ligação que você deseja remapear já está potencialmente mascarada por um mapa de teclas ativo mal, obtendo a ligação atual de M-.é inútil.

(defun lookup-no-evil (key)
  ;; excluding evil maps from the lookup. not sure if 
  ;; anything more than evail-normal-state-map is needed
  (let* ((evil-maps (list evil-normal-state-map))
         (bindings
          (remq nil
                (mapcar
                 (lambda (map)
                   (unless (memq map evil-maps)
                     (lookup-key map key)))
                 (current-active-maps)))))
    (when bindings
      ;; let's assume the first one is the right one. 
      ;; Given that minor modes are at the beginning 
      ;; (although this is *not* documented so should not 
      ;; be relied upon), it might be what we would have 
      ;;without evil-mode indeed
      (car bindings))))

(defmacro evil-remap (from to)
  ;; assuming that we want to put it in the normal-state map.
  ;; not sure about that
  `(define-key evil-normal-state-map ,to
       (lambda ()
         (interactive)
         (call-interactively (lookup-no-evil ,from)))))

(evil-remap (kbd "M-.") (kbd "C-]"))

Normalmente eu não uso o mal, então pode haver ajustes necessários (veja os comentários incorporados)

Além disso, uma abordagem mais limpa seria procurar as ligações uma vez (em um gancho de modo, por exemplo), em vez de procurar dinamicamente toda vez que a tecla for pressionada. Mas não tenho certeza de qual gancho usar, então isso é deixado como um exercício;) (e dependendo da ordem que você usa para seus modos menores, ou se você os alternar dinamicamente, pode estar incorreto)

Sigma
fonte
2

A solução aceita por @severin quase funciona para mim, mas, quando a tag não é encontrada, o buffer não retorna ao modo normal. Essa alternativa funciona para mim em todos os casos:

(defun my-jump-to-tag ()
    (interactive)
    (evil-execute-in-emacs-state)
    (call-interactively (key-binding (kbd "M-."))))
(define-key evil-normal-state-map (kbd "C-]") 'my-jump-to-tag)
Zak King
fonte
1

Eu acho que a maneira mais limpa é

(define-key evil-normal-state-map (kbd "M-.") 'xref-find-definitions)

(e também adicione todos os outros mapas de seu interesse)

xref-find-definitionsé a função binded para M-.no emacs, como você pode ver usando o comando C-h k.

mookid
fonte
1

Algumas funções de encadernação no estilo vim.

Aqui estão algumas funções que defini para permitir a ligação no estilo vim no mapa global e em vários estados malignos, bem como duas funções gerais que usam um mapa de chave arbitrário ou uma função de ligação arbitrária. Eu coloquei essas funções em uma essência .

(defun kbd+ (keyrep &optional need-vector)
  (if (vectorp keyrep) keyrep (edmacro-parse-keys keyrep need-vector)))

(defun gmap (keyrep defstr)
  "Vim-style global keybinding. Uses the `global-set-key' binding function."
  (global-set-key (kbd+ keyrep) (edmacro-parse-keys defstr t)))

(defun fmap (keybind-fn keyrep defstr)
  "Vim-style keybinding using the key binding function KEYBIND-FN."
  (call keybind-fn (kbd+ keyrep) (edmacro-parse-keys defstr t)))

(defun xmap (keymap keyrep defstr)
  "Vim-style keybinding in KEYMAP. Uses the `define-key' binding function."
  (define-key keymap (kbd+ keyrep) (edmacro-parse-keys defstr t)))

(defun nmap (keyrep defstr) "Vim-style keybinding for `evil-normal-state.' Uses the `define-key' binding function."
      (xmap evil-normal-state-map keyrep defstr))
(defun imap (keyrep defstr) "Vim-style keybinding for `evil-insert-state'. Uses the `define-key' binding function."
      (xmap evil-insert-state-map keyrep defstr))
(defun vmap (keyrep defstr) "Vim-style keybinding for `evil-visual-state'. Uses the `define-key' binding function."
      (xmap evil-visual-state-map keyrep defstr))
(defun mmap (keyrep defstr) "Vim-style keybinding for `evil-motion-state'. Uses the `define-key' binding function."
      (xmap evil-motion-state-map keyrep defstr))

Em geral, é melhor usar essas funções apenas para ligações no estilo de teclado e macro (como o caso de uso na pergunta) e usar a combinação de teclas no estilo emacs para todo o resto.

notas

  • A bind-keymacro da use-packageembalagem é uma excelente e versátil função de combinação de teclas.
  • Se você deseja substituir um comando por outro, pode usar os comandos de remapeamento do Emacs .
  • Se você as usar para ligações de teclas regulares, lembre-se de que não há versões "noremap"; portanto, se as ligações em sua definição mudarem, as ligações definidas pelo usuário também serão alteradas.

Vinculação C-]a M-..

Note-se que no estado normal, você vai querer ligar para \M-.acessar os emacs de ligação, uma vez que se liga-estado normal M-.para 'evil-repeat-pop-next. Portanto, uma ligação de estado normal pode ser definida com:

(nmap "C-]" "\\ M-.")

ou (religando qualquer evil-jump-to-tagchave no estado normal:

(nmap [remap evil-jump-to-tag] "\\ M-.")
pirocrasty
fonte