How do I customize zsh's vim mode?
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Isolated
--
Chapters
00:00 How Do I Customize Zsh'S Vim Mode?
00:40 Accepted Answer Score 27
01:27 Answer 2 Score 5
01:38 Answer 3 Score 24
02:16 Answer 4 Score 1
03:13 Thank you
--
Full question
https://superuser.com/questions/151803/h...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#vim #zsh
#avk47
ACCEPTED ANSWER
Score 27
1.) (see http://zshwiki.org/home/examples/zlewidgets and http://pthree.org/2009/03/28/add-vim-editing-mode-to-your-zsh-prompt/ ):
function zle-line-init zle-keymap-select { RPS1="${${KEYMAP/vicmd/-- NORMAL --}/(main|viins)/-- INSERT --}" RPS2=$RPS1 zle reset-prompt } zle -N zle-line-init zle -N zle-keymap-select
Where:
"RPS" stands for 'right prompt string' and defines the prompt appearing on the right hand side of the terminal, and the ${variable/pattern/replacement} syntax is that of 'parameter expansion', see: http://mywiki.wooledge.org/BashSheet#Parameter_Operations.
'zle -N' causes the user-definable widgets 'zle-line-init' and 'zle-keymap-select' to be bound (to shell functions of same names), so that they will be called when the line editor is initialised and the keymap is changed respectively, see: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#SEC125.
2.) i suspect that you have to write another zsh-widget to do that, get inspired by the first of the two links for the first problem.
ANSWER 2
Score 24
akira's solution has the following problem when using multi-line prompts: when going from ins to cmd mode, the prompt redraw causes few lines to be deleted from the previous output (and the new prompt is displayed few lines above). How many lines depends on how many lines you have in your prompt.
The way to deal with that is to use zle-line-finish
, without using zle reset-prompt
there. An example:
# perform parameter expansion/command substitution in prompt
setopt PROMPT_SUBST
vim_ins_mode="[INS]"
vim_cmd_mode="[CMD]"
vim_mode=$vim_ins_mode
function zle-keymap-select {
vim_mode="${${KEYMAP/vicmd/${vim_cmd_mode}}/(main|viins)/${vim_ins_mode}}"
zle reset-prompt
}
zle -N zle-keymap-select
function zle-line-finish {
vim_mode=$vim_ins_mode
}
zle -N zle-line-finish
And then you can add it to your right-prompt, for example:
RPROMPT='${vim_mode}'
This is straight from my blog post about it:
ANSWER 3
Score 5
zle-line-init() { zle -K vicmd; }
zle -N zle-line-init
these two lines make sure it starts in command mode
ANSWER 4
Score 1
The below will set you up with a modified cursor and a prompt displaying which mode you are in. You can change DEFAULT_VI_MODE
to either viins
or vicmd
. Just paste the below into a fresh .zshrc
to get started:
# Prefer vi shortcuts
bindkey -v
DEFAULT_VI_MODE=viins
KEYTIMEOUT=1
__set_cursor() {
local style
case $1 in
reset) style=0;; # The terminal emulator's default
blink-block) style=1;;
block) style=2;;
blink-underline) style=3;;
underline) style=4;;
blink-vertical-line) style=5;;
vertical-line) style=6;;
esac
[ $style -ge 0 ] && print -n -- "\e[${style} q"
}
# Set your desired cursors here...
__set_vi_mode_cursor() {
case $KEYMAP in
vicmd)
__set_cursor block
;;
main|viins)
__set_cursor vertical-line
;;
esac
}
__get_vi_mode() {
local mode
case $KEYMAP in
vicmd)
mode=NORMAL
;;
main|viins)
mode=INSERT
;;
esac
print -n -- $mode
}
zle-keymap-select() {
__set_vi_mode_cursor
zle reset-prompt
}
zle-line-init() {
zle -K $DEFAULT_VI_MODE
}
zle -N zle-line-init
zle -N zle-keymap-select
# Optional: allows you to open the in-progress command inside of $EDITOR
autoload -Uz edit-command-line
bindkey -M vicmd 'v' edit-command-line
zle -N edit-command-line
# PROMPT_SUBST enables functions and variables to re-run everytime the prompt
# is rendered
setopt PROMPT_SUBST
# Single quotes are important so that function is not run immediately and saved
# in the variable
RPROMPT='$(__get_vi_mode)'
Note: I only tested this in Terminal.app (2.7.3) on MacOS (10.12.6) with zsh (5.3.1). Also, if you ever add edit-command-line
then the mode will be correctly set too.