在 vi 覆盖模式下使 zsh zle 恢复退格键上的字符
Make zsh zle restore characters on backspace in vi overwrite-mode
我在 Vi 模式下使用 Zsh。
当 $KEYMAP == vicmd
(即 命令模式 )时,我想按退格键将光标向左移动一个字符,而不删除任何内容。 [工作]
当$KEYMAP == viins && $ZLE_STATE == *insert*
(即插入模式)时,我想按退格键将光标向左移动一个字符,删除紧接在前面的字符线。 [工作]
当$KEYMAP == viins && $ZLE_STATE == *overwrite*
(即overwrite-mode / replace-mode)时,我想按退格键移动光标向左一个字符,恢复该行的前一个字符与进入覆盖模式之前最初存在的字符. [不工作]
这是一个例子:
# [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
上面的例子显示了当我在 overwrite-mode; 中按下退格键时我想要发生的事情;然而,实际情况如下:
# [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"
) , 光标右侧的字符向左移动。
我如何获得我想要的行为?
好的,所以我最终找到了解决我遇到的问题的方法,我会post在这里以防其他人遇到同样的问题。
解决方案
把这个放在你的 .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 的 copy/paste,并且这就是我喜欢的)。如果您不想这样做,而只想进行简单的修复,请将 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
}
我在 Vi 模式下使用 Zsh。
当 $KEYMAP == vicmd
(即 命令模式 )时,我想按退格键将光标向左移动一个字符,而不删除任何内容。 [工作]
当$KEYMAP == viins && $ZLE_STATE == *insert*
(即插入模式)时,我想按退格键将光标向左移动一个字符,删除紧接在前面的字符线。 [工作]
当$KEYMAP == viins && $ZLE_STATE == *overwrite*
(即overwrite-mode / replace-mode)时,我想按退格键移动光标向左一个字符,恢复该行的前一个字符与进入覆盖模式之前最初存在的字符. [不工作]
这是一个例子:
# [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
上面的例子显示了当我在 overwrite-mode; 中按下退格键时我想要发生的事情;然而,实际情况如下:
# [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"
) , 光标右侧的字符向左移动。
我如何获得我想要的行为?
好的,所以我最终找到了解决我遇到的问题的方法,我会post在这里以防其他人遇到同样的问题。
解决方案
把这个放在你的 .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 的 copy/paste,并且这就是我喜欢的)。如果您不想这样做,而只想进行简单的修复,请将 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
}