¿Cómo puedo hacer que el modo vi de zsh se comporte más como el modo vi de bash?

21

Realmente me gusta la velocidad general de zsh, pero dos cosas me están molestando.

  1. Tengo que esperar un momento entre golpear y escapar para llegar a la búsqueda de historial (si golpea con demasiada rapidez, dice zsh: do you wish to see all 514 possibilities (172 lines) )
  2. Después de entrar en el modo de inserción debido a golpear a o A, No puedo retroceder más allá del punto en el que ingresé al modo de inserción.

Sé que 2 es como el vi clásico, pero me gusta más el estilo vim.

Chas. Owens
fuente
Si alguien se está topando con el muy molesto problema de la doble fuga, tienes que golpear i dos veces para volver al modo de inserción, recomiendo altamente esta ¡fijar!
cchamberlain

Respuestas:

18

(1). Por alguna razón, bindkey se comporta de manera extraña cuando se trata de "/": <esc> seguido rápidamente por / se interpreta como <esc-/>. (Observé este comportamiento el otro día; no estoy muy seguro de cuál es su causa). No sé si se trata de un error o una característica, y si es una característica si puede desactivarse, pero puede solucionarlo con bastante facilidad. .

Esta combinación de teclas probablemente está vinculada a _history-complete-older, que está generando el resultado no deseado - puedes usar bindkey -L Para ver si este es el caso.

En cualquier caso, si no te importa sacrificar el real <esc-/> (presionado juntos, como un acorde) enlace, puede volver a enlazarlo al comando de búsqueda de historial en modo vi, de modo que al escribir <esc> seguido por / hace lo mismo a cualquier velocidad de escritura. =)

Como esto se tratará como un acorde, no tendrá el efecto de ingresar primero al modo de comando vi, por lo que tendremos que asegurarnos de que eso suceda primero. Primero, necesitas definir una función; ponlo en algún lugar de tu fpath si usa eso, o póngalo en su .zshrc de lo contrario:

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

El resto va en tu .zshrc de cualquier manera:

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

Debería ser bueno para ir.

(2). Puede corregir la tecla de retroceso de la siguiente manera:

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

Además, si desea un comportamiento similar para otros comandos de estilo 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
fuente
Estaba bajo ^[/ no \e/, pero esas son ambas formas válidas de decir escapar. El cambio funciona perfectamente. Ahora que estoy jugando con él más completamente, parece que el modo vi de zsh apesta en comparación con el de bash (o al menos no está completamente configurado de forma predeterminada). Un ejemplo de esto es el hecho de que te deja en el modo de inserción después del historial de búsqueda. Tengo que volver al modo de comando para golpear n para encontrar el siguiente elemento de búsqueda.
Chas. Owens
1
Bueno, no sé si tienes otros ejemplos, pero el que mencionas es culpa mía, no de zsh. =) Lo que sucedió es que he enlazado un comando de editor de modo vi-cmd en modo de inserción vi: el comando espera que el shell ya esté en modo cmd y se comporta en consecuencia. Necesitamos escribir un comando de editor que primero llame al comando "entrar en modo cmd", y entonces ejecuta .vi-history-search-backward. Lo escribiré y editaré mi respuesta. Vuelve hoy más tarde.
Marshall Eubanks
OK, actualicé mi respuesta. Pruébalo.
Marshall Eubanks
Con respecto a (2), cuando lo hago bindkey | grep <searchterm> para cualquiera de los términos, todos están prefijados por vi-. ¿Necesito configurar bindkey comandos que no tienen el prefijo vi-?
adam_0
1
Gracias. Estos hacks (y los de wjv a continuación) hacen que el modo zsh vi pase de casi inutilizable a excelente. Creé una cuenta de superusuario para poder votar. :-)
ctrueden
13

Solo voy a abordar la pregunta (1).

Tu problema es KEYTIMEOUT. Cito de zshzle (1):

Cuando ZLE está leyendo un comando desde el terminal, puede leer un   secuencia que está vinculada a algún comando y también es un prefijo de una   cuerda unida más larga. En este caso ZLE esperará un cierto tiempo para ver.   si se escriben más caracteres, y si no (o no coinciden con ninguno)   cadena más larga) ejecutará el enlace. Este tiempo de espera está definido   por el parámetro KEYTIMEOUT; su valor predeterminado es 0.4 seg. No hay   timeout si la cadena de prefijo no está vinculada a un comando.

Ese 0.4s es el retraso que estás experimentando después de presionar ESC. La solución es establecer KEYTIMEOUT hasta 0.01s en uno de los archivos de inicio del shell:

export KEYTIMEOUT=1

Desafortunadamente, esto tiene un efecto en cadena: otras cosas comienzan a ir mal ...

En primer lugar, ahora hay un problema en el modo de comando vi: escribir ESC hace que el cursor se bloquee, y luego el carácter que escriba a continuación se traga. Esto se debe a que ESC no está vinculado a nada de forma predeterminada en el modo de comando vi, pero hay widgets de múltiples caracteres que comienzan con ESC (teclas de cursor). Así que cuando presionas ESC, ZLE espera al siguiente personaje ... y luego lo consume.

La solución es vincular ESC a alguna cosa en modo comando, asegurando así que la alguna cosa se pasa a ZLE después de $ KEYTIMEOUT centisegundos. Ahora podemos mantener los enlaces que comienzan con ESC en modo de comando sin estos efectos negativos. Asilo ESC al carácter de campana, que me parece incluso menos intrusivo que el autoinserto (y mi shell está silenciado):

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

Actualización 2017:

Desde entonces he encontrado una solución aún mejor para unir el ESC: el undefined-key widget No estoy seguro de si este widget estaba disponible en zsh cuando escribí esta respuesta originalmente.

bindkey -M vicmd '^[' undefined-key

Problema siguiente: hay, por defecto, algunos widgets de dos teclas que comienzan en ^ X en el modo de inserción vi; estos se vuelven inutilizables si $ KEYTIMEOUT se establece completamente. Lo que hago es desenlazar ^ X en el modo de inserción vi (se inserta automáticamente de forma predeterminada); Esto permite que los widgets de dos teclas sigan funcionando.

bindkey -rM viins '^X'

Pierde el enlace de autoinserción, pero puede enlazarlo a otra cosa, por supuesto. (No lo hago, ya que no tengo uso para ello).

El último problema (lo he encontrado hasta ahora): quedan algunas combinaciones de teclas predeterminadas que "perdemos" debido a que configuramos $ KEYTIMEOUT a la derecha, a saber: aquellos que comienzan con ESC en el modo de inserción vi, que son no teclas de cursor Yo personalmente los reviento para comenzar con ^ X en su lugar:

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

Actualización 2018:

Resulta que la sección completa anterior (después de "Actualización 2017") no es necesariamente requerida. Es posible configurar la clave META para que sea equivalente a ESC en asignaciones de teclado usando:

bindkey -mv

Por lo tanto es posible no para desenlazar ^ X, y para acceder a las combinaciones de teclas que comienzan en ESC presionando META como líder (ALT o OPT en los teclados modernos).

Si tienes acceso al libro. De Bash a Z Shell por Kiddle et al., la equivalencia de ESC y META en las combinaciones de teclas se analiza en la barra lateral del Capítulo 4 en las páginas 78–79.

wjv
fuente
Gracias. Estos hacks (y los de marshall anteriores, para) hacen que el modo zsh vi pase de casi inutilizable a excelente. Creé una cuenta de superusuario para poder votar. :-)
ctrueden
1
¡Gracias! Me parece un poco preocupante que, después de todo este tiempo, ¡todavía necesitamos lo que es esencialmente un truco y una solución alternativa para poder utilizar un poco de funcionalidad zsh!
wjv