Como posso fazer o modo vi do zsh se comportar mais como o modo vi do bash?

19

Eu estou realmente gostando da velocidade geral do zsh, mas duas coisas estão me incomodando.

  1. Eu tenho que bater esperar um momento entre bater escape e apertar barra para chegar à pesquisa de histórico (se ele bateu barra muito rapidamente diz zsh: do you wish to see all 514 possibilities (172 lines) )
  2. Depois de inserir o modo de inserção por causa de a ou A , não consigo voltar atrás no ponto em que entrei no modo de inserção.

Eu sei que o 2 é como o vi clássico, mas eu gosto mais do estilo vim.

    
por Chas. Owens 18.09.2012 / 13:20

2 respostas

17

(1) Por alguma razão, bindkey comporta-se estranhamente quando se trata de "/": <esc> seguido rapidamente por / é interpretado como <esc-/> . (Eu observei esse comportamento no outro dia; não tenho certeza do que o causa.) Eu não sei se isso é um bug ou um recurso, e se é um recurso se ele pode ser desabilitado, mas você pode contorná-lo facilmente .

Esta combinação de teclas provavelmente está vinculada a _history-complete-older , que está gerando o resultado indesejado - você pode usar bindkey -L para ver se este é o caso.

De qualquer forma, se você não se importa em sacrificar a ligação real <esc-/> (pressionada junto, como um acorde), você pode vinculá-la novamente ao comando de busca do histórico do modo vi , de modo que digitar <esc> seguido por / faça a mesma coisa em qualquer velocidade de digitação. =)

Como isso será tratado como um acorde, ele não terá o efeito de inserir primeiro o modo de comando do vi, então teremos que garantir que isso aconteça primeiro. Primeiro, você precisa definir uma função; coloque em algum lugar no seu fpath se você usar isso, ou coloque-o no seu .zshrc caso contrário:

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

O resto vai no seu .zshrc de qualquer forma:

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

Deve ser bom para ir.

(2) Você pode corrigir a tecla de retrocesso da seguinte maneira:

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

Além disso, se você quiser um comportamento semelhante para outros comandos do estilo vi:

bindkey "^W" backward-kill-word 
bindkey "^H" backward-delete-char      # Control-h also deletes the previous char
bindkey "^U" backward-kill-line            
    
por 13.01.2013 / 22:34
12

Eu só vou abordar a questão (1).

Seu problema é KEYTIMEOUT. Cito de zshzle (1):

When ZLE is reading a command from the terminal, it may read a sequence that is bound to some command and is also a prefix of a longer bound string. In this case ZLE will wait a certain time to see if more characters are typed, and if not (or they don't match any longer string) it will execute the binding. This timeout is defined by the KEYTIMEOUT parameter; its default is 0.4 sec. There is no timeout if the prefix string is not itself bound to a command.

Esse 0,4s é o atraso que você está enfrentando depois de atingir o ESC. A correção é definir o KEYTIMEOUT diretamente em 0.01s em um dos arquivos de inicialização do shell:

export KEYTIMEOUT=1

Infelizmente, isso tem um efeito indireto: outras coisas começam a dar errado ...

Em primeiro lugar, existe agora um problema no modo de comando do VI: Digitar ESC faz o cursor travar e, em seguida, qualquer caractere digitado em seguida é engolido. Isso ocorre porque ESC não está vinculado a nada por padrão no modo de comando do VI, mas há widgets de vários caracteres que começam com ESC (teclas de cursor!). Então, quando você apertar ESC, ZLE aguarda o próximo caractere ... e depois consome.

A correção é ligar o ESC a algo no modo de comando, garantindo assim que o algo seja passado para o ZLE após centésimos de segundo de $ KEYTIMEOUT. Agora podemos manter as ligações iniciando com ESC no modo de comando sem esses efeitos nocivos. Eu vinculo ESC ao caractere da campainha, que eu acho ainda menos intrusivo do que auto-insert (e meu shell é silenciado):

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

Update 2017:

I have since found an even better solution for binding ESC — the undefined-key widget. I’m not sure whether this widget was available in zsh when I originally wrote this answer.

bindkey -M vicmd '^[' undefined-key

Próximo problema: Por padrão, há alguns widgets de duas teclas começando em ^ X no modo de inserção vi; eles se tornam inutilizáveis se $ KEYTIMEOUT estiver definido até o fim. O que eu faço é desvincular ^ X no modo de inserção vi (é auto-insert por padrão); isso permite que esses widgets de duas chaves continuem trabalhando.

bindkey -rM viins '^X'

Você perde a ligação para inserção automática, mas pode vinculá-la a outra coisa, é claro. (Eu não, já que não tenho utilidade para isso.)

O último problema (que encontrei até agora): Existem alguns atalhos de teclado padrão restantes que "perdemos" devido à configuração de $ KEYTIMEOUT, ou seja: aqueles que começam com ESC no modo de inserção vi que são não teclas do cursor. Pessoalmente, ligo-lhes para começar com ^ X:

bindkey -M viins '^X,' _history-complete-newer \
                 '^X/' _history-complete-older \
                 '^X'' _bash_complete-word
    
por 20.09.2013 / 15:59