Tmux in zsh with vi mode, toggle cursor shape between normal and insert mode

shelltmuxvimzsh

(note: I wrote this question on stackoverflow, but got flagged as offtopic, so I'm re-writing it here)

Here's what I want:

Consistent behavior (as much as possible) between Vim, MacVim, zsh with vi-mode and tmux. In Vim and MacVim the insert-normal mode switch is working just fine, and in Vim case, works fine by running Vim both from zsh shell and within tmux. The Zsh shell also works well with the switch by doing this: https://raw.github.com/ramiroaraujo/dotfiles/master/zsh/vi-mode.zsh, which I don't seem to remember where I found it.

Now this doesn't work within tmux. The cursor stays the same in insert and normal mode, and it's really naggy. Suposly, the code avove should kinda work if a correct escape sequence is printed when in tmux, and I found this http://reza.jelveh.me/2011/09/18/zsh-tmux-vi-mode-cursor, but seems kind of old and looks like it's for bash, not zsh. Any help on any direction is very welcomed 🙂

Best Answer

After a few hours of diving into a wide variety of GitHub issues and man-pages I've found a setup that works for me, achieving what I believe you set out to achieve.

I am using rxvt-unicode v9.22, tmux 2.9a, neovim v0.4.2 and zsh 5.7.1 under Arch Linux. My setup is configured to use non-blinking beam cursor in insert mode, and non-blinking block cursor in normal mode.

For zsh, I use vi-mode with the following in my .zshrc:

bindkey -v
export KEYTIMEOUT=1

# Change cursor with support for inside/outside tmux
function _set_cursor() {
    if [[ $TMUX = '' ]]; then
      echo -ne $1
    else
      echo -ne "\ePtmux;\e\e$1\e\\"
    fi
}

function _set_block_cursor() { _set_cursor '\e[2 q' }
function _set_beam_cursor() { _set_cursor '\e[6 q' }

function zle-keymap-select {
  if [[ ${KEYMAP} == vicmd ]] || [[ $1 = 'block' ]]; then
      _set_block_cursor
  else
      _set_beam_cursor
  fi
}
zle -N zle-keymap-select
# ensure beam cursor when starting new terminal
precmd_functions+=(_set_beam_cursor) #
# ensure insert mode and beam cursor when exiting vim
zle-line-init() { zle -K viins; _set_beam_cursor }

For my tmux configuration, the only important lines are:

set -g default-terminal "tmux-256color"
set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'

To make sure that neovim behaves correctly for urxvt, $TERM needs to be correct. I do this with .Xresources with the following line (don't forget to merge afterwards with xrdb -merge ~/.Xresources):

URxvt*termName: rxvt-256color

Now for neovim, no configuration should be necessary, other than making sure not to tamper with guicursor. Some GitHub issues mention setting set guicursor= in your init.vim, however my setup does not work if I add that.

Hope this setup is reproducible given what I've provided, otherwise my dotfiles are available on GitHub.