From 169e9c87ff2322ddcd8795a34b3df6a151ee9489 Mon Sep 17 00:00:00 2001 From: Kevin W Matthews Date: Sun, 22 Feb 2026 15:07:56 -0500 Subject: [PATCH] fix(vi-mode): re-render cursor after exisiting visual mode (ohmyzsh#11705) Following the suggestion in #11705, this PR removes the wrapping of the visual-mode widget and instead uses the zli-line-pre-redraw hook ([special widget](https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Special-Widgets)) to check state and change the cursor accordingly. My understanding is that the [REGION_ACTIVE](https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#index-REGION_005fACTIVE) variable indicates which characters/regions are to be highlighted, and it has values of: - 0: no highlight - 1: character/region highlight active - 2: line highlight active and that for visual/visual-line modes, VI_KEYMAP must be updated by the plugin itself. fixes 11705 --- plugins/vi-mode/vi-mode.plugin.zsh | 34 ++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/plugins/vi-mode/vi-mode.plugin.zsh b/plugins/vi-mode/vi-mode.plugin.zsh index 85208cfc9..1d307be9e 100644 --- a/plugins/vi-mode/vi-mode.plugin.zsh +++ b/plugins/vi-mode/vi-mode.plugin.zsh @@ -31,24 +31,32 @@ function _vi-mode-set-cursor-shape-for-keymap() { # https://vt100.net/docs/vt510-rm/DECSCUSR local _shape=0 case "${1:-${VI_KEYMAP:-main}}" in - main) _shape=$VI_MODE_CURSOR_INSERT ;; # vi insert: line - viins) _shape=$VI_MODE_CURSOR_INSERT ;; # vi insert: line - isearch) _shape=$VI_MODE_CURSOR_INSERT ;; # inc search: line - command) _shape=$VI_MODE_CURSOR_INSERT ;; # read a command name - vicmd) _shape=$VI_MODE_CURSOR_NORMAL ;; # vi cmd: block - visual) _shape=$VI_MODE_CURSOR_VISUAL ;; # vi visual mode: block - viopp) _shape=$VI_MODE_CURSOR_OPPEND ;; # vi operation pending: blinking block - *) _shape=0 ;; + main) _shape=$VI_MODE_CURSOR_INSERT ;; # vi insert: line + viins) _shape=$VI_MODE_CURSOR_INSERT ;; # vi insert: line + isearch) _shape=$VI_MODE_CURSOR_INSERT ;; # inc search: line + command) _shape=$VI_MODE_CURSOR_INSERT ;; # read a command name + vicmd) _shape=$VI_MODE_CURSOR_NORMAL ;; # vi cmd: block + visual) _shape=$VI_MODE_CURSOR_VISUAL ;; # vi visual mode: block + visual-line) _shape=$VI_MODE_CURSOR_VISUAL ;; # vi visual line mode: block + viopp) _shape=$VI_MODE_CURSOR_OPPEND ;; # vi operation pending: blinking block + *) _shape=0 ;; esac printf $'\e[%d q' "${_shape}" } -function _visual-mode { - typeset -g VI_KEYMAP=visual - _vi-mode-set-cursor-shape-for-keymap "$VI_KEYMAP" - zle .visual-mode +function zle-line-pre-redraw() { + if [[ "$REGION_ACTIVE" -eq 0 && ("$VI_KEYMAP" == visual || "$VI_KEYMAP" == visual-line) ]]; then + typeset -g VI_KEYMAP=$KEYMAP + _vi-mode-set-cursor-shape-for-keymap "$VI_KEYMAP" + elif [[ "$REGION_ACTIVE" -eq 1 && "$VI_KEYMAP" != "visual" ]]; then + typeset -g VI_KEYMAP=visual + _vi-mode-set-cursor-shape-for-keymap "$VI_KEYMAP" + elif [[ "$REGION_ACTIVE" -eq 2 && "$VI_KEYMAP" != "visual-line" ]]; then + typeset -g VI_KEYMAP=visual-line + _vi-mode-set-cursor-shape-for-keymap "$VI_KEYMAP" + fi } -zle -N visual-mode _visual-mode +zle -N zle-line-pre-redraw function _vi-mode-should-reset-prompt() { # If $VI_MODE_RESET_PROMPT_ON_MODE_CHANGE is unset (default), dynamically