Changing cursor style based on mode in both zsh and vim

13,615

Solution 1

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

# .zshrc

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

precmd_functions+=(_fix_cursor)

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.

Solution 2

This works perfectly for me, it's taken from here: https://gist.github.com/LukeSmithxyz/e62f26e55ea8b0ed41a65912fbebbe52

# vi mode
bindkey -v
export KEYTIMEOUT=1

# 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'
  fi
}
zle -N zle-keymap-select
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"
}
zle -N zle-line-init
echo -ne '\e[5 q' # Use beam shape cursor on startup.
preexec() { echo -ne '\e[5 q' ;} # Use beam shape cursor for each new prompt.

You can customise the type of cursor you want (blinking or not, |, rectangle or _) by changing the numbers in the following sequences \e[5 q (5 is for beam, 1 is for block) as follows:

Set cursor style (DECSCUSR), VT520.
0  ⇒  blinking block.
1  ⇒  blinking block (default).
2  ⇒  steady block.
3  ⇒  blinking underline.
4  ⇒  steady underline.
5  ⇒  blinking bar, xterm.
6  ⇒  steady bar, xterm.

Solution 3

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'" 

Solution 4

Simply add the line:

export VI_MODE_SET_CURSOR=true

to your ~/.zshrc. It is mentioned in the discussion of issue #9570.

Share:
13,615

Related videos on Youtube

maddingl
Author by

maddingl

Updated on September 18, 2022

Comments

  • maddingl
    maddingl over 1 year

    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.
        KEYTIMEOUT=5
    
        # 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'
          fi
        }
        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.

  • maddingl
    maddingl over 5 years
    yes, this is nicer and works pretty well. Thanks! :)
  • statox
    statox about 5 years
    @maddingl Maybe you should consider changing the accepted answer to this one instead :)
  • Unix
    Unix over 4 years
    I use zle-line-finish() { echo -ne "\e[2 q" } and it's working as well.
  • Max Coplan
    Max Coplan over 4 years
    But this doesn't change back to a block when going into normal mode
  • kctong529
    kctong529 about 3 years
    Works like a charm. For those who want to extend this feature to interactive shell (e.g. python, scheme), you might find this helpful (unix.stackexchange.com/a/409587/459602)