July 20, 2015 urxvt zsh tmux vim

'(Vi-Mode Ubiquitous Cursor Indicator)

Audience: programmers

It should be as easy as possible to tell if you’re in normal or insert mode. Your eye tends to be on your cursor, so you’ll find the cursor to be the best indicator of your mode. Of course, you’re effectively in Vim when in Zsh (bindkey -v, right?), too, so it should have the same cursor indicator.

This recipe has been described scattershot in various places, but never comprehensively. This version will give you a solid block cursor in normal mode, and a blinking, gray underscore cursor in insert mode. I’ve also set it to green block for Zsh, blue for Vim. Change it to whatever you like. It’s big and bright so you can easily find it. Then it’s small and flashing to remind you to get out of insert mode. Whatever colors/styles you choose, rejoice that it now works!

I worked for a couple years without having this working in Tmux and it drove me crazy. Now that it works well, I find myself using Tmux much more often.

This has been battle tested in URxvt, but it also seems to work in Xterm and Termite.

Zsh

~/.zshrc
# Modal cursor color for vi's insert/normal modes.
# http://stackoverflow.com/questions/30985436/
# https://bbs.archlinux.org/viewtopic.php?id=95078
# http://unix.stackexchange.com/questions/115009/
zle-line-init () {
  zle -K viins
  #echo -ne "\033]12;Grey\007"
  #echo -n 'grayline1'
  echo -ne "\033]12;Gray\007"
  echo -ne "\033[4 q"
  #print 'did init' >/dev/pts/16
}
zle -N zle-line-init
zle-keymap-select () {
  if [[ $KEYMAP == vicmd ]]; then
    if [[ -z $TMUX ]]; then
      printf "\033]12;Green\007"
      printf "\033[2 q"
    else
      printf "\033Ptmux;\033\033]12;red\007\033\\"
      printf "\033Ptmux;\033\033[2 q\033\\"
    fi
  else
    if [[ -z $TMUX ]]; then
      printf "\033]12;Grey\007"
      printf "\033[4 q"
    else
      printf "\033Ptmux;\033\033]12;grey\007\033\\"
      printf "\033Ptmux;\033\033[4 q\033\\"
    fi
  fi
  #print 'did select' >/dev/pts/16
}
zle -N zle-keymap-select

Vim

~/.vimrc
" http://vim.wikia.com/wiki/Configuring_the_cursor
" Tmux details: http://reza.jelveh.me/2011/09/18/zsh-tmux-vi-mode-cursor
if &term =~ "xterm\\|rxvt"
  " Insert
  let &t_SI  = "\<Esc>]12;gray\x7"
  let &t_SI .= "\<Esc>[3 q"
  " Normal
  let &t_EI  = "\<Esc>]12;green\x7"
  let &t_EI .= "\<Esc>[2 q"
  autocmd VimLeave * silent !echo -ne "\033]112\007"
elseif &term =~ "screen-it"
  " Insert
  let &t_SI  = "\<Esc>Ptmux;\<Esc>\<Esc>]12;gray\x7\<Esc>\\"
  let &t_SI .= "\<Esc>Ptmux;\<Esc>\<Esc>[3 q\<Esc>\\"
  " Normal
  let &t_EI  = "\<Esc>Ptmux;\<Esc>\<Esc>]12;blue\x7\<Esc>\\"
  let &t_EI .= "\<Esc>Ptmux;\<Esc>\<Esc>[2 q\<Esc>\\"
  autocmd VimLeave * silent !printf "\033Ptmux;\033\033]12;grey\007\033\\"
endif

Tmux

I like to keep these lines in my .tmux.conf so I can easily access the infocmp and tic commands to run on new machines.

Note that TERM will be auto-set by Tmux now to screen-it.

One time tmux setup commands (keep around in ~/.tmux.conf)
# One-time setup for italic support (instead of reverse).
# http://tmux.svn.sourceforge.net/viewvc/tmux/trunk/FAQ
# % infocmp screen-256color |
#   sed -e 's/^screen[^|]*|[^,]*,/screen-it|screen with italics support,/' \
#     -e 's/%?%p1%t;3%/%?%p1%t;7%/' \
#     -e 's/smso=[^,]*,/smso=\\E[7m,/' \
#     -e 's/rmso=[^,]*,/rmso=\\E[27m,/' \
#     -e '$s/$/ sitm=\\E[3m, ritm=\\E[23m,/' \
#     >| /tmp/screen.terminfo
# Compile and create binary file ~/.terminfo/s/screen-it
# % tic /tmp/screen.terminfo

# This is actually active.
set -g default-terminal "screen-it"

Addendum

There is still an issue where on Tmux pane/window changing, the cursor stays in the mode that its last pane/window was in.