我怎样才能使 zsh 的 vi 模式更像 bash 的 vi 模式?

我怎样才能使 zsh 的 vi 模式更像 bash 的 vi 模式?

我真的很喜欢 zsh 的整体速度,但有两件事让我很烦。

  1. 我必须在按下 Esc 键和按下斜线键之间等待片刻才能进入历史记录搜索(如果按下斜线键太快,则会显示zsh: do you wish to see all 514 possibilities (172 lines)
  2. 由于点击a或进入插入模式后A,我无法退格进入插入模式的位置。

我知道 2 就像经典的 vi,但我更喜欢 vim 风格。

答案1

(1). 出于某种原因,bindkey 在遇到“/”时会表现得很奇怪:<esc>紧接着的/被解释为<esc-/>。(前几天我观察到了这种行为;不太确定是什么原因造成的。)我不知道这是一个错误还是一个功能,如果是一个功能,是否可以禁用它,但你可以相当轻松地解决这个问题。

此组合键可能与 绑定_history-complete-older,从而产生不良结果——您可以使用bindkey -L来查看是否是这种情况。

无论如何,如果你不介意牺牲实际的 <esc-/>(按下在一起,作为和弦)绑定,您可以将其重新绑定到 vi 模式历史搜索命令,以便键入<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            

答案2

我仅要回答问题(1)。

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

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

0.4 秒是按下 ESC 后遇到的延迟。解决方法是在其中一个 shell 启动文件中将 KEYTIMEOUT 设置为 0.01 秒:

export KEYTIMEOUT=1

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

首先,vi 命令模式现在存在一个问题:输入 ESC 会导致光标挂起,然后您接下来输入的任何字符都会被吞掉。这是因为在 vi 命令模式下,ESC 默认不与任何内容绑定,但存在以 ESC(光标键!)开头的多字符小部件。因此,当您按下 ESC 时,ZLE 会等待下一个字符……然后吞掉它。

修复方法是将 ESC 绑定到某物在命令模式下,从而确保某物在 $KEYTIMEOUT 厘秒后传递给 ZLE。现在我们可以在命令模式下保持从 ESC 开始的绑定,而不会产生这些不良影响。我将 ESC 绑定到铃声字符,我发现这比自插入更不具侵入性(并且我的 shell 被静音了):

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年更新:

事实证明,上面的整个部分(“更新 2017”之后)不是必需的。可以使用以下方法在键盘映射中将 META 键设置为等同于 ESC:

bindkey -mv

因此有可能不是解除绑定 ^X,并通过按 META 作为引导键(现代键盘上为 ALT 或 OPT)来访问以 ESC 开头的键绑定。

如果你有权阅读本书从 Bash 到 Z Shell由 Kiddle 等人撰写,在第 4 章第 78-79 页的边栏中讨论了键绑定中 ESC 和 META 的等效性。

答案3

我选择重新执行历史补全以消除冲突。compinit绑定和弦后运行以下命令。

bindkey -M viins -r "^[/"
bindkey -M viins "^[." _history-complete-newer

相关内容