I use vi-mode in oh-my-zsh with the af-magic theme.

I want the cursor style to indicate whether I am in normal mode (block) or insert mode (beam), both in zsh and in vim.

This is what I have so far:

In my ~/.zshrc:

    # vim mode config
    # ---------------

    # Activate vim mode.
    bindkey -v

    # Remove mode switching delay.

    # Change cursor shape for different vi modes.
    function zle-keymap-select {
      if [[ ${KEYMAP} == vicmd ]] ||
         [[ $1 = 'block' ]]; then
        echo -ne '\e[1 q'

      elif [[ ${KEYMAP} == main ]] ||
           [[ ${KEYMAP} == viins ]] ||
           [[ ${KEYMAP} = '' ]] ||
           [[ $1 = 'beam' ]]; then
        echo -ne '\e[5 q'
    zle -N zle-keymap-select

    # Use beam shape cursor on startup.
    echo -ne '\e[5 q'

    # Use beam shape cursor for each new prompt.
    preexec() {
       echo -ne '\e[5 q'

As found here.

In vim, I use Vundle and terminus.

With these configurations, both zsh and vim work as they should when considered independently. However, when I enter vim from zsh in insert mode, vim starts in normal mode (as it should) but still shows the beam shape cursor. Similarly, when I exit vim, I get back to zsh in insert mode, but the cursor is still in block shape (since the last mode in vim was normal).

When after this, I switch modes for the first time (in both zsh and vim), the cursor behaves the way it should again.

How can I make them display the correct cursor after entering and exiting vim as well?

I tried putting

    autocmd VimEnter * stopinsert
    autocmd VimLeave * startinsert

in my ~.vimrc, but this does not affect the cursor.


I think it's better to use precmd() instead of preexec():

# .zshrc

_fix_cursor() {
   echo -ne '\e[5 q'


This way:

  • you don't have to change .vimrc
  • cursor is fixed also when you create a new prompt without executing a command
  • you don't have to write echo -ne '\e[5 q' twice in your .zshrc.
  • yes, this is nicer and works pretty well. Thanks! :) – maddingl Jan 27 at 14:28
  • @maddingl Maybe you should consider changing the accepted answer to this one instead :) – statox Mar 19 at 11:05
  • I use zle-line-finish() { echo -ne "\e[2 q" } and it's working as well. – Gerard Nov 10 at 0:57
  • But this doesn't change back to a block when going into normal mode – Max Coplan Nov 30 at 18:17

I have found a solution:

I put this in my ~/.vimrc:

autocmd VimEnter * silent exec "! echo -ne '\e[1 q'"
autocmd VimLeave * silent exec "! echo -ne '\e[5 q'" 

You can also use zle-line-init()

zle-line-init() {
    zle -K viins # initiate `vi insert` as keymap (can be removed if `bindkey -V` has been set elsewhere)
    echo -ne "\e[5 q"

I think this problem is better solved using this, rather than precmd() or preexec() as both are intended to be used to execute commands, instead of fixing prompt.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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