Comment puis-je faire en sorte que le mode vi de zsh se comporte plus comme le mode vi de bash?

24

J'aime vraiment la vitesse générale de zsh, mais deux choses me dérangent énormément.

  1. Je dois frapper un moment entre frapper échapper et frapper barre oblique pour accéder à la recherche d'historique (si elle frappe trop rapidement, elle dit zsh: do you wish to see all 514 possibilities (172 lines))
  2. Après être entré en mode insertion en raison de la frappe de aou A, je ne peux pas revenir en arrière au-delà du point où je suis entré en mode insertion.

Je sais que 2 est comme le vi classique, mais j'aime mieux le style vim.

Chas. Owens
la source
Si quelqu'un rencontre le problème très ennuyeux du double échappement, ce qui vous oblige à frapper ideux fois pour revenir en mode insertion, je recommanderais fortement cette correction!
cchamberlain
Il y a aussi un bon résumé ici: dougblack.io/words/zsh-vi-mode.html
jackcogdill

Réponses:

22

(1). Pour une raison quelconque, bindkey se comporte bizarrement quand il s'agit de "/": <esc>suivi rapidement de /est interprété comme <esc-/>. (J'ai observé ce comportement l'autre jour; je ne sais pas exactement ce qui en est la cause.) Je ne sais pas s'il s'agit d'un bogue ou d'une fonctionnalité, et si c'est une fonctionnalité si elle peut être désactivée, mais vous pouvez la contourner assez facilement .

Ce combo de touches est probablement lié à _history-complete-older, ce qui génère le résultat indésirable - vous pouvez utiliser bindkey -Lpour voir si c'est le cas.

Quoi qu'il en soit, si cela ne vous dérange pas de sacrifier la liaison réelle <esc-/> (pressée ensemble, comme un accord), vous pouvez la relier à la commande de recherche d'historique vi-mode, de sorte que la frappe <esc>suivie de /fait la même chose à n'importe quelle frappe la vitesse. =)

Comme cela sera traité comme un accord, cela n'aura pas pour effet de passer d'abord en mode de commande vi, nous devons donc nous assurer que cela se produit en premier. Tout d'abord, vous devez définir une fonction; mettez-le quelque part dans votre fpathsi vous l'utilisez, ou mettez-le dans votre .zshrc sinon:

vi-search-fix() {
zle vi-cmd-mode
zle .vi-history-search-backward
}

Le reste va dans votre .zshrc de toute façon:

autoload vi-search-fix
zle -N vi-search-fix
bindkey -M viins '\e/' vi-search-fix

Ça devrait être bon d'y aller.

(2). Vous pouvez corriger la touche de retour arrière comme suit:

`bindkey "^?" backward-delete-char`

De plus, si vous souhaitez un comportement similaire pour d'autres commandes de style vi:

bindkey "^W" backward-kill-word 
bindkey "^H" backward-delete-char      # Control-h also deletes the previous char
bindkey "^U" backward-kill-line            
Marshall Eubanks
la source
Ce n'était ^[/pas le cas \e/, mais ce sont deux façons valables de dire évasion. Le changement fonctionne parfaitement. Maintenant que je joue avec lui plus complètement, il semble que le mode vi de zsh soit nul par rapport à bash (ou du moins n'est pas entièrement configuré par défaut). Un exemple de ceci est le fait qu'il vous place en mode insertion après l'historique de recherche. Je dois revenir en mode commande pour appuyer sur n pour trouver l'élément de recherche suivant.
Chas. Owens
1
Eh bien, je ne sais pas si vous avez d'autres exemples, mais celui que vous mentionnez est ma faute, pas celle de zsh. =) Ce qui s'est passé, c'est que j'ai lié une commande d'éditeur de mode vi-cmd en mode d'insertion vi - la commande s'attend à ce que le shell soit déjà en mode cmd et se comporte en conséquence. Nous devons écrire une commande d'éditeur qui appelle d'abord la commande "enter cmd mode", puis s'exécute .vi-history-search-backward. Je vais l'écrire et modifier ma réponse - revenez plus tard dans la journée.
Marshall Eubanks
OK, j'ai mis à jour ma réponse. Essaye le.
Marshall Eubanks
En ce qui concerne (2), quand je le fais bindkey | grep <searchterm>pour l'un des termes, ils sont tous préfixés par vi-. Dois-je configurer des bindkeycommandes sans préfixe vi-?
adam_0
1
Merci. Ces hacks (et ceux de wjv ci-dessous aussi) font passer le mode vi de zsh de presque inutilisable à excellent. J'ai créé un compte superutilisateur afin de pouvoir vous voter. :-)
ctrueden
14

Je vais seulement répondre à la question (1).

Votre problème est KEYTIMEOUT. Je cite zshzle (1):

Lorsque ZLE lit une commande à partir du terminal, il peut lire une séquence qui est liée à une commande et est également un préfixe d'une chaîne liée plus longue. Dans ce cas, ZLE attendra un certain temps pour voir si plus de caractères sont saisis et sinon (ou ils ne correspondent plus à une chaîne plus longue), il exécutera la liaison. Ce délai est défini par le paramètre KEYTIMEOUT; sa valeur par défaut est de 0,4 s. Il n'y a pas de délai d'expiration si la chaîne de préfixe n'est pas elle-même liée à une commande.

Ce 0,4 s est le délai que vous rencontrez après avoir appuyé sur ESC. Le correctif consiste à définir KEYTIMEOUT jusqu'à 0,01 s dans l'un des fichiers de démarrage du shell:

export KEYTIMEOUT=1

Malheureusement, cela a un effet d'entraînement: d'autres choses commencent à mal se passer…

Premièrement, il y a maintenant un problème en mode de commande vi: Taper ESC fait bloquer le curseur, puis le caractère que vous tapez ensuite est avalé. C'est parce que ESC n'est lié à rien par défaut en mode de commande vi, mais il existe des widgets à plusieurs caractères qui commencent par ESC (touches de curseur!). Ainsi, lorsque vous appuyez sur ÉCHAP, ZLE attend le personnage suivant… puis le consomme.

Le correctif consiste à lier ESC à quelque chose en mode commande, garantissant ainsi que le quelque chose est passé à ZLE après $ KEYTIMEOUT centisecondes. Nous pouvons maintenant conserver les liaisons commençant par ESC en mode commande sans ces effets néfastes. Je lie ESC au personnage de la cloche, que je trouve encore moins intrusif que l'auto-insertion (et ma coque est réduite au silence):

bindkey -sM vicmd '^[' '^G'

Mise à jour 2017:

Depuis, j'ai trouvé une meilleure solution pour lier ESC - le undefined-keywidget. Je ne sais pas si ce widget était disponible dans zsh lorsque j'ai écrit cette réponse à l'origine.

bindkey -M vicmd '^[' undefined-key

Problème suivant: Il existe par défaut des widgets à deux touches commençant par ^ X en mode d'insertion vi; ceux-ci deviennent inutilisables si $ KEYTIMEOUT est défini à fond. Ce que je fais, c'est délier ^ X en mode d'insertion vi (c'est auto-inséré par défaut); cela permet à ces widgets à deux touches de continuer à fonctionner.

bindkey -rM viins '^X'

Vous perdez la liaison pour l'auto-insertion, mais vous pouvez bien sûr la lier à autre chose. (Je ne le fais pas, car je n'en ai aucune utilité.)

Le dernier problème (que j'ai trouvé jusqu'à présent): il reste quelques raccourcis clavier par défaut que nous "perdons" en raison de la définition de $ KEYTIMEOUT vers le bas, à savoir: ceux commençant par ESC en mode d'insertion vi qui ne sont pas des touches de curseur. Je les lie personnellement pour commencer par ^ X à la place:

bindkey -M viins '^X,' _history-complete-newer \
                 '^X/' _history-complete-older \
                 '^X`' _bash_complete-word

Mise à jour 2018:

Il s'avère que la section entière ci-dessus (après la «mise à jour 2017») n'est pas nécessairement requise. Il est possible de définir la touche META pour qu'elle soit équivalente à ESC dans les mappages de clavier en utilisant:

bindkey -mv

Il est donc possible de ne pas dissocier ^ X, et d'accéder aux raccourcis clavier qui commencent dans ESC en appuyant à la place sur META en tant que leader (ALT ou OPT sur les claviers modernes).

Si vous avez accès au livre From Bash to Z Shell de Kiddle et al., L'équivalence de ESC et META dans les raccourcis clavier est discutée dans l'encadré du chapitre 4 aux pages 78–79.

wjv
la source
Merci. Ces hacks (et ceux de marshaul ci-dessus aussi) font passer le mode vi de zsh de presque inutilisable à excellent. J'ai créé un compte superutilisateur afin de pouvoir vous voter. :-)
ctrueden
1
Merci! Je trouve un peu inquiétant qu'après tout ce temps, nous ayons encore besoin de ce qui est essentiellement un hack et une solution de contournement pour rendre un noyau de fonctionnalité zsh utilisable!
wjv