使 zsh zle 在 vi 覆盖模式下恢复退格上的字符



我在Vi模式下使用Zsh。


$KEYMAP == vicmd(即命令模式(时,我想按退格键将光标向左移动一个字符,而不删除任何内容。[工作]

$KEYMAP == viins && $ZLE_STATE == *insert*(即插入模式(时,我想按退格键将光标向左移动一个字符,删除该行前面的字符。[工作]

$KEYMAP == viins && $ZLE_STATE == *overwrite*(即覆盖模式/替换模式(时,我想按退格键将光标向左移动一个字符,将行上紧接的字符恢复为进入覆盖模式之前最初存在的字符。[不工作]


下面是一个例子:

# [COMMAND MODE] We start with the following string on the command line:
$ Hello, world!
^
cursor position
# [REPLACE MODE] Now, I hit "R" to enter replace-mode and I type "stuff".
$ Helstufforld!
^
cursor position
# [REPLACE MODE] Finally, I hit backspace 3 times.
$ Helst, world!
^
cursor position


上面的例子显示了我在覆盖模式下点击退格键时想要发生的事情;但是,实际发生的情况如下:

# [COMMAND MODE] We start with the following string on the command line:
$ Hello, world!
^
cursor position
# [REPLACE MODE] Now, I hit "R" to enter replace-mode and I type "stuff".
$ Helstufforld!
^
cursor position
# [REPLACE MODE] Finally, I hit backspace 3 times.
$ Helstworld!
^
cursor position


请注意,在第二个示例中点击退格键时,如何恢复刚刚被覆盖的原始 3 个字符(即", w"(,而不是替换这些字符的最后 3 个字符(即 删除了"uff"(,光标右侧的字符向左移动。


如何获得我想要的行为?

好的,所以我最终为我遇到的问题制定了一个解决方案,如果其他人遇到同样的问题,我将在这里发布。


溶液

把它放在你的.zshrc中:

readonly ZLE_VI_MODE_CMD=0
readonly ZLE_VI_MODE_INS=1
readonly ZLE_VI_MODE_REP=2
readonly ZLE_VI_MODE_OTH=3
function zle-vi-mode {
if [[ $KEYMAP == vicmd ]]; then
echo -n $ZLE_VI_MODE_CMD
elif [[ $KEYMAP == (viins|main) ]] && [[ $ZLE_STATE == *insert* ]]; then
echo -n $ZLE_VI_MODE_INS
elif [[ $KEYMAP == (viins|main) ]] && [[ $ZLE_STATE == *overwrite* ]]; then
echo -n $ZLE_VI_MODE_REP
else
echo -n $ZLE_VI_MODE_OTH
fi
}
function zle-backward-delete-char-fix {
case "$(zle-vi-mode)" in
$ZLE_VI_MODE_REP)
if [[ $CURSOR -le $MARK ]]; then
CURSOR=$(( $(($CURSOR-1)) > 0 ? $(($CURSOR-1)) : 0 ))
MARK=$CURSOR
else
zle undo
fi
;;
*)
zle backward-delete-char
;;
esac
}
zle -N zle-backward-delete-char-fix
## Change cursor shape according to the current Vi-mode.
function zle-line-init zle-keymap-select {
case "$(zle-vi-mode)" in
$ZLE_VI_MODE_CMD) echo -ne 'e[2 q' ;; # cursor -> block
$ZLE_VI_MODE_INS) echo -ne 'e[6 q' ;; # cursor -> vertical bar
$ZLE_VI_MODE_REP)
echo -ne 'e[4 q' # cursor -> underline
MARK=$CURSOR
;;
*)
;;
esac
}
zle -N zle-line-init
zle -N zle-keymap-select
bindkey -v
bindkey '^?' zle-backward-delete-char-fix
bindkey '^h' zle-backward-delete-char-fix


此外,上面的代码会导致您的光标形状根据您当前所处的 vi 模式而变化(即,因为这是从我的.zshrc复制/粘贴,这就是我喜欢的(。如果您不想要这个,而只是想要普通修复,请将zle-init-line/zle-keymap-select函数替换为以下内容:

function zle-line-init zle-keymap-select {
case "$(zle-vi-mode)" in
$ZLE_VI_MODE_REP)
MARK=$CURSOR
;;
*)
;;
esac
}

最新更新