如何使zsh的vi模式表现得更像bash的vi模式?


24

我真的很喜欢zsh的一般速度,但是有两件事使我烦恼。

  1. 我必须在点击逃脱和点击斜线之间稍等片刻才能进入历史记录搜索(如果搜索斜线的速度过快,它会说zsh: do you wish to see all 514 possibilities (172 lines)
  2. 由于按a或进入插入模式后A,我不能退格到进入插入模式的位置。

我知道2就像经典vi,但我更喜欢vim样式。


如果有人遇到令人讨厌的双重转义问题,导致您不得不打i两次才能回到插入模式,我强烈建议您修复问题!
cchamberlain

这里也有一个很好的摘要:dougblack.io/words/zsh-vi-mode.html
jackcogdill

Answers:


22

(1)。出于某种原因,bindkey在涉及“ /”时会表现出奇怪的现象:<esc>快速后跟/被解释为<esc-/>。(前几天我观察到了此行为;不太清楚是什么原因造成的。)我不知道这是错误还是功能,是否可以禁用它是否是功能,但是您可以很轻松地解决它。

该组合键可能已绑定到_history-complete-older,从而生成了不希望的结果–您可以bindkey -L查看是否存在这种情况。

无论如何,如果您不介意牺牲实际 <esc-/>(按在一起的和弦)绑定,则可以将其重新绑定到vi-mode历史搜索命令,以便键入<esc>后跟/键入在任何键入时都可以执行相同的操作速度。=)

由于这将被视为和弦,因此先进入vi命令模式不会产生效果,因此我们必须确保首先发生这种情况。首先,您需要定义一个函数;fpath如果使用它,请将其放在您的某个位置,否则将其放在您的.zshrc中:

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

其余的以两种方式进入您的.zshrc:

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

应该很好走。

(2)。您可以按以下方式修复退格键:

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

另外,如果您希望其他vi样式命令具有类似的行为:

bindkey "^W" backward-kill-word 
bindkey "^H" backward-delete-char      # Control-h also deletes the previous char
bindkey "^U" backward-kill-line            

^[/不是在之下\e/,但这些都是说逃避的有效方法。所做的更改非常完美。现在,我正在更全面地使用它,与bash相比,zsh的vi模式看起来很烂(或者至少在默认情况下未完全配置)。一个例子是事实,它使您在搜索历史记录之后进入插入模式。我必须返回命令模式以按n查找下一个搜索项。
Chas。Owens

1
好吧,我不知道您是否还有其他示例,但是您提到的是我的错,不是zsh的错。=)发生了什么事,是我在vi插入模式下绑定了一个vi-cmd模式编辑器命令-该命令希望shell在cmd模式下已经存在并且表现相应。我们需要编写一个编辑器命令,该命令首先调用“ enter cmd mode”命令,然后执行.vi-history-search-backward。我将编写并编辑答案-今天晚些时候再回来查看。
马歇尔·尤班克斯

好的,我更新了答案。试试看。
Marshall Eubanks 2013年

关于(2),当我bindkey | grep <searchterm>为任何一个术语做时,它们都以前缀vi-。我是否需要设置bindkey没有前缀的命令vi-
adam_0

1
谢谢。这些技巧(以及下面的wjv技巧)使zsh的vi模式从几乎无法使用变为出色。我创建了一个超级用户帐户,以便可以投票给您。:-)
ctrueden

14

我只想解决问题(1)。

您的问题是KEYTIMEOUT。我引用zshzle(1):

当ZLE从终端读取命令时,ZLE可能会读取绑定到某个命令的序列,该序列也是更长绑定字符串的前缀。在这种情况下,ZLE将等待一段时间以查看是否键入了更多的字符,如果没有键入(或它们不再与任何更长的字符串匹配),它将执行绑定。此超时是由KEYTIMEOUT参数定义的;其默认值为0.4秒。如果前缀字符串本身未绑定到命令,则没有超时。

0.4s是您按下ESC后所经历的延迟。解决方法是在其中一个外壳启动文件中将KEYTIMEOUT正确设置为0.01s:

export KEYTIMEOUT=1

不幸的是,这会产生连锁反应:其他事情开始出错…

首先,现在在vi命令模式下存在一个问题:键入ESC会使光标挂起,然后吞下下一个键入的字符。这是因为在vi命令模式下,默认情况下ESC没有绑定任何东西,但是有许多以ESC开头的多字符小部件(光标键!)。因此,当您按下ESC时,ZLE等待下一个字符…然后使用它。

解决方法是在命令模式下将ESC绑定到某事物,从而确保该事物在$ KEYTIMEOUT厘秒后传递到ZLE。现在,我们可以在命令模式下使绑定从ESC开始,而不会产生不良影响。我将ESC绑定到响铃字符,发现它比自动插入更具侵入性(并且我的外壳已静音):

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

2017年更新:

从那以后,我找到了绑定ESC的更好解决方案- undefined-key小部件。我不确定最初写此答案时此小部件是否在zsh中可用。

bindkey -M vicmd '^[' undefined-key

下一个问题:在vi插入模式下,默认情况下有一些以^ X开头的两键小部件;如果将$ KEYTIMEOUT设置为最低,则这些将变得不可用。我要做的是在vi插入模式下取消绑定^ X(默认情况下是自动插入);这使这些两键小部件可以继续工作。

bindkey -rM viins '^X'

您会失去自我插入的绑定,但是当然可以将其绑定到其他对象。(我没有,因为我没有用。)

最后一个问题(到目前为止,我已经找到了):由于将$ KEYTIMEOUT正确设置为负,因此我们遗失了一些默认的键绑定:在vi插入模式下以ESC开头的不是光标键。我个人将它们重新绑定为以^ X开头:

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

更新2018:

事实证明,不一定需要上面的整个部分(“ Update 2017”之后)。可以使用以下命令在键盘映射中将META键设置为与ESC等效:

bindkey -mv

因此,可以解除^ X的绑定,而可以通过按META键(而不是现代键盘上的ALT或OPT)来访问以ESC开始的键绑定。

如果您可以访问Kiddle等人的《从Bash到Z Shell》一书,则在第78-79页的第4章侧栏中将讨论键绑定中ESC和META的等效性。


谢谢。这些技巧(以及上面的marshaul的技巧)使zsh的vi模式从几乎无法使用变为出色。我创建了一个超级用户帐户,以便可以投票给您。:-)
ctrueden

1
谢谢!我感到有些担心,毕竟,在所有这些时间之后,我们仍然需要真正的hack和变通办法来使zsh功能的核心功能可用!
wjv 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.