Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I use bindkey -v (for bash-ers set -o vi I think that works in zsh too) or vi(m) mode. but it bugs me that I don't have any visual cue to tell me whether I'm in insert mode or command mode. Does anyone know how I can make my prompt display the mode?

share|improve this question

You've already found zle-keymap-select which is executed whenever the mode changes. You could use it to set some other visual indicator than the prompt, depending on what your terminal supports it (and your taste in mode indicator display, of course).

There is a standard terminfo capability to change the shape of the cursor. However some terminals display the same cursor in both modes. Xterm's notion of a less visible cursor is to make it blink (and this must be enabled with the -bc command line argument or cursorBlink resource).

zle-keymap-select () {
  case $KEYMAP in
    vicmd) print -rn -- $terminfo[cvvis];; # block cursor
    viins|main) print -rn -- $terminfo[cnorm];; # less visible cursor

With some terminals, you can also change the cursor color with print -n '\e]12;pink\a' (by color name) or print -n '\e]12;#abcdef\a' (by RGB specification). These sequences are described in the xterm documentation, in the ctlseqs file; modern terminal emulators typically emulate xterm, though they might not support all its features.

share|improve this answer
For some reason I get main for KEYMAP instead of viins, not sure why. – Graeme Apr 15 '14 at 1:07
@Graeme main is an alias for viins or emacs depending on whether zsh thought your favorite editor was vi or not when it started. I thought it would use viins when switching the mode back from vicmd, but it seems that it uses main instead. Updated. – Gilles Apr 15 '14 at 1:16
You still need to double up with zle-line-init (or whatever alternative) since zle-keymap-select does not get called if hitting enter changes the keymap. – Graeme Apr 15 '14 at 1:28
up vote 9 down vote accepted

I found this via SU. Here's the basic example, though I'm still customizing it for myself:

function zle-line-init zle-keymap-select {
    RPS1="${${KEYMAP/vicmd/-- NORMAL --}/(main|viins)/-- INSERT --}"
    zle reset-prompt

zle -N zle-line-init
zle -N zle-keymap-select

I'd explain it except I don't really understand it yet

share|improve this answer
I tried this approach but found one issue. If you do something like CTRL+C while in vi-command mode, the prompt will reset, but indicate youre in command mode when youre really in insert mode. zle-line-init should always change the indicator to insert mode. For some reason $KEYMAP is not updated properly when zle-line-init is called. – Patrick Apr 21 '12 at 19:25
zle reset-prompt will delete 1 (or more) lines above the prompt (if your prompt is multiline) when redrawing :( This is a showstopper for me. – Paweł Gościcki Sep 22 '12 at 19:59
@PawełGościcki it seems to be an issue when you have two or more lines of PS1. – Metaphox Mar 8 '13 at 15:14
@Metaphox I know, that why I've said "(if your prompt is multiline)". Any fix for that? – Paweł Gościcki Mar 8 '13 at 15:53
@PawełGościcki aww sorry i somehow skipped the words in parentheses , bad habit. No, I didn't find a fix for that. What platform are you on? Was wondering if this is OS X specific. – Metaphox Mar 21 '13 at 8:11

you can try VimMode

share|improve this answer
I was hoping for something a bit less linkish and a bit more explanatory. I like to know how things work. – xenoterracide Aug 19 '10 at 6:29
Actually it is all there. Look at the comments the functions and how they are bound to the mode change events. – Martin Dec 30 '11 at 18:38

For the people having problems using reset-prompt with multiline prompts, this worked for me: http://zeitlens.com/posts/2014-06-29-howto-zsh-vi-style.html In combination with http://stackoverflow.com/questions/3622943/zsh-vi-mode-status-line I ended up doing:


function insert-mode () { echo "-- INSERT --" }
function normal-mode () { echo "-- NORMAL --" }

precmd () {
    # yes, I actually like to have a new line, then some stuff and then 
    # the input line
    print -rP "
[%D{%a, %d %b %Y, %H:%M:%S}] %n %{$fg[blue]%}%m%{$reset_color%}"

    # this is required for initial prompt and a problem I had with Ctrl+C or
    # Enter when in normal mode (a new line would come up in insert mode,
    # but normal mode would be indicated)
    PS1="%{$terminfo_down_sc$(insert-mode)$terminfo[rc]%}%~ $ "
function set-prompt () {
    case ${KEYMAP} in
      (vicmd)      VI_MODE="$(normal-mode)" ;;
      (main|viins) VI_MODE="$(insert-mode)" ;;
      (*)          VI_MODE="$(insert-mode)" ;;
    PS1="%{$terminfo_down_sc$VI_MODE$terminfo[rc]%}%~ $ "

function zle-line-init zle-keymap-select {
    zle reset-prompt
preexec () { print -rn -- $terminfo[el]; }

zle -N zle-line-init
zle -N zle-keymap-select
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.