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

24

Estou gostando muito da velocidade geral do zsh, mas duas coisas me incomodam.

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

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

Chas. Owens
fonte
Se alguém estiver enfrentando a questão muito irritante da fuga dupla, fazendo com que seja necessário bater iduas vezes para voltar ao modo de inserção, eu recomendo essa correção!
Cchamberlain 24/07
Há também um bom resumo aqui: dougblack.io/words/zsh-vi-mode.html
jackcogdill

Respostas:

22

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

Provavelmente, essa combinação de teclas está vinculada _history-complete-older, o que está gerando um resultado indesejável - você pode usar bindkey -Lpara ver se esse é o caso.

De qualquer forma, se você não se importa de sacrificar a ligação real <esc-/> (pressionada em conjunto, como um acorde), é possível vinculá-la novamente ao comando de pesquisa de histórico do modo vi, para que a digitação <esc>seguida por /faça o mesmo em qualquer digitação Rapidez. =)

Como isso será tratado como um acorde, não terá o efeito de entrar primeiro no modo de comando vi, portanto, teremos que garantir que isso aconteça primeiro. Primeiro, você precisa definir uma função; coloque-o em algum lugar do seu fpathse você o usar ou 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 maneira:

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

Deveria estar bom para ir.

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

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

Além disso, se você deseja 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            
Marshall Eubanks
fonte
Estava sob ^[/não \e/, mas essas são as duas formas válidas de dizer fuga. A mudança funciona perfeitamente. Agora que estou jogando com ele mais completamente, parece que o modo vi do zsh é péssimo em comparação com o bash (ou pelo menos não está totalmente configurado por padrão). Um exemplo disso é o fato de o colocar no modo de inserção após o histórico de pesquisa. Eu tenho que voltar ao modo de comando para pressionar n para encontrar o próximo item de pesquisa.
Chas. Owens
1
Bem, não sei se você tem outros exemplos, mas o que você mencionou é minha culpa, não do zsh. =) O que aconteceu é que vinculei um comando do editor do modo vi-cmd no modo vi insert - o comando espera que o shell já esteja no modo cmd e se comporte de acordo. Precisamos escrever um comando de editor que primeiro chame o comando "enter cmd mode" e depois execute .vi-history-search-backward. Vou escrever e editar minha resposta - volte hoje mais tarde.
Marshall Eubanks
OK, atualizei minha resposta. Experimente.
Marshall Eubanks
Com relação a (2), quando eu faço bindkey | grep <searchterm>para qualquer um dos termos, todos são prefixados por vi-. Preciso configurar bindkeycomandos que não sejam prefixados vi-?
14133 adam_0
1
Obrigado. Esses hacks (e os do wjv abaixo também) fazem com que o modo vi do zsh passe de quase inutilizável a excelente. Criei uma conta de superusuário para poder votar em você. :-)
ctrueden
14

Vou abordar apenas a questão (1).

Seu problema é KEYTIMEOUT. Cito zshzle (1):

Quando o ZLE está lendo um comando do terminal, ele pode ler uma sequência que está vinculada a algum comando e também é um prefixo de uma cadeia de caracteres mais longa. Nesse caso, o ZLE aguardará um certo tempo para ver se mais caracteres são digitados e, se não (ou não corresponderem a nenhuma sequência mais), ele executará a ligação. Esse tempo limite é definido pelo parâmetro KEYTIMEOUT; seu padrão é 0,4 seg. Não haverá tempo limite se a sequência do prefixo não estiver vinculada a um comando.

0,4s é o atraso que você está enfrentando depois de pressionar ESC. A correção é definir KEYTIMEOUT até 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 ...

Primeiro, agora existe um problema no modo de comando vi: digitar ESC faz com que o cursor seja interrompido e, em seguida, o caractere digitado a seguir é engolido. Isso ocorre porque o ESC não está vinculado a nada por padrão no modo de comando vi, mas existem widgets com vários caracteres que começam com ESC (teclas do cursor!). Então, quando você pressiona ESC, o ZLE aguarda o próximo caractere ... e o 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 $ KEYTIMEOUT centisegundos. Agora podemos manter as ligações começando com ESC no modo de comando sem esses efeitos nocivos. Vinculo ESC ao caractere bell, que considero menos invasivo do que a inserção automática (e meu shell é silenciado):

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

Atualização 2017:

Desde então, encontrei uma solução ainda melhor para vincular o ESC - o undefined-keywidget. Não tenho certeza se este widget estava disponível no zsh quando escrevi originalmente esta resposta.

bindkey -M vicmd '^[' undefined-key

Próximo problema: por padrão, existem widgets de duas teclas iniciando em ^ X no modo de inserção vi; eles se tornarão inutilizáveis ​​se $ KEYTIMEOUT estiver definido até o fim. O que faço é desvincular ^ X no modo vi insert (é auto-inserir por padrão); isso permite que esses widgets de duas chaves continuem funcionando.

bindkey -rM viins '^X'

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

O último problema (que encontrei até agora): Existem algumas combinações de teclas padrão restantes que "perdemos" devido à configuração de $ KEYTIMEOUT logo abaixo, ou seja: aquelas que começam com ESC no modo de inserção vi que não são teclas de cursor. Pessoalmente, eu os religuei para começar com ^ X:

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

Atualização 2018:

Acontece que a seção inteira acima (após a "Atualização 2017") não é necessariamente necessária. É possível definir a tecla META como equivalente ao ESC nos mapeamentos de teclado usando:

bindkey -mv

Portanto, é possível não desvincular ^ X e acessar as combinações de teclas iniciadas no ESC pressionando META como líder (ALT ou OPT nos teclados modernos).

Se você tiver acesso ao livro From Bash to Z Shell de Kiddle et al., A equivalência de ESC e META em combinações de teclas é discutida na barra lateral do Capítulo 4 nas páginas 78–79.

wjv
fonte
Obrigado. Esses hacks (e os do marshaul acima também) fazem com que o modo vi do zsh passe de quase inutilizável para excelente. Criei uma conta de superusuário para poder votar em você. :-)
ctrueden
1
Obrigado! Acho um pouco preocupante o fato de que, depois de todo esse tempo, ainda precisamos do que é essencialmente um hack e uma solução alternativa para tornar utilizável um pouco da funcionalidade zsh!
Wjv 6/02