Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
446 lines (311 sloc) 18 KB
*vitality.txt* make Vim play nicely with iTerm2 and tmux
█████ █ ██ ███
██████ █ █████ █ █ ███ █ █
██ █ █ ██████ ██ ██ ███ ██
█ ██ █ ███ ██ ██ █ ██
█ ███ █ ████████ ██ ████████
██ ██ █ ███ ████████ ████ ██ ███ ████████ ██ ████
██ ██ █ ███ ██ █ ███ █ ██ ███ ██ ██ ███ █
██ ██ █ ██ ██ █ ████ ██ ██ ██ ██ ████
██ ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ █ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
███ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
███████ ███ █ ██ █████ ██ ███ █ ███ █ ██ █████████
███ ███ ███ ██ ███ ███ ████ ███
Vitality makes [V]im play nicely with █████ ███
[i]Term2 and ████████ ██
[t]mux. █ ████
CONTENTS *vitality-contents*
1. Overview and Usage ................... |VitalityOverview|
2. Configuration ........................ |VitalityConfig|
2.1 g:vitality_fix_cursor ........... |vitality_fix_cursor|
2.2 g:vitality_normal_cursor ........ |vitality_normal_cursor|
2.3 g:vitality_insert_cursor ........ |vitality_insert_cursor|
2.4 g:vitality_fix_focus ............ |vitality_fix_focus|
2.5 g:vitality_always_assume_iterm .. |vitality_always_assume_iterm|
3. How it Works ......................... |VitalityHow|
4. License .............................. |VitalityLicense|
5. Bugs ................................. |VitalityBugs|
6. Contributing ......................... |VitalityContributing|
7. Changelog ............................ |VitalityChangelog|
1. Overview and Usage *VitalityOverview* *VitalityUsage*
Vitality is a Vim plugin designed to restore some sanity to the fairly common
combination of Vim, iTerm2 and (optionally) tmux.
It currently performs two main functions. First, it makes the |FocusGained|
and |FocusLost| autocommands fire when iTerm2 gains or loses focus. Yes, even
through tmux.
It also handles setting the cursor to and from a bar when entering and exiting
insert mode.
You shouldn't have to do anything other than install the plugin for all this
to work.
If you encounter bugs, please report them (|VitalityBugs|). Terminal codes
are a fiddly little rabbit hole, so it's entirely possible that things will
sometimes break.
2. Configuration *VitalityConfig*
You can tweak the behavior of Vitality by setting a few variables in your
.vimrc file. Currently five options are available: >
let g:vitality_fix_cursor = 0
let g:vitality_normal_cursor = 0
let g:vitality_insert_cursor = 1
let g:vitality_fix_focus = 0
let g:vitality_always_assume_iterm = 1
You might also need to add some configuration to your tmux setup. Later
versions of tmux (>= 1.9) have Focus events disabled by default. Add the
following line to your ~/.tmux.conf and then restart or reload all your tmux
set -g focus-events on
2.1 g:vitality_fix_cursor *vitality_fix_cursor*
Set this to 0 if you want to disable normal/insert block/bar cursor switching.
Default: 1
2.2 g:vitality_normal_cursor *vitality_normal_cursor*
Set this to the cursor type you want to have in normal mode. iTerm2 has three
cursor types:
Block = 0
Bar = 1
Underline = 2
Default: 0
2.3 g:vitality_insert_cursor *vitality_insert_cursor*
Set this to the cursor type you want to have in insert mode. iTerm2 has three
cursor types:
Block = 0
Bar = 1
Underline = 2
Default: 1
2.4 g:vitality_fix_focus *vitality_fix_focus*
Set this to 0 if you don't want focus events to be processed.
Default: 1
2.5 g:vitality_always_assume_iterm *vitality_always_assume_iterm*
By default, Vitality only enables iTerm-specific features when $ITERM_PROFILE
is present in your environment. However, this value is not preserved once
you have connected by ssh to another system. If you only use iTerm2, you can
tell Vim to always assume iTerm features will work.
Set this to 1 if you want Vitality to always assume it is running in iTerm.
Warning: if you set this to true and you run Vim from a terminal other
than iTerm, it certainly will not work, and you may even get strange output.
Default: depends on the existence of $ITERM_PROFILE.
3. How it Works *VitalityHow*
You don't need to know this.
You don't WANT to know this.
Turn back now or skip to the |VitalityLicense|.
You've been warned.
DISCLAIMER: I am not a terminal expert by any means. Some of this may not be
entirely correct. However, it should at least give you a general
understanding about what's going on.
DISCLAIMER 2: Any specific examples given apply only to Vim, iTerm 2, and
tmux, because that's what Vitality targets. Other terminals
do things differently.
First: some background. Vim and your terminal program (iTerm2) communicate by
sending and receiving special strings that you'd never want to type or read in
real life. For the sake of brevity we'll call these "escape sequences" in
this document.
For example: if a program outputs the text: >
iTerm will change the cursor to a bar shape.
This particular sequence is 19 bytes long. The <Esc> and <C-G> characters are
actual single-byte characters: ASCII points 27 and 7 (033 and 07 in octal)
You can try this out yourself. Open a new iTerm2 window (WITHOUT tmux) and
run bash, then try running something that will output that magic string: >
echo -e "hello \033]50;CursorShape=1\007 world"
Your cursor will now be a bar. Run the command again with a 0 instead of
a 1 to turn it into a block, or 2 to turn it into an underline.
iTerm2 supports a number of escape sequences. Here are the ones Vitality
uses to accomplish its goals (don't worry about what they mean yet):
Set cursor to a bar shape (19 bytes): >
Set cursor to a block shape (19 bytes): >
Enable focus reporting (8 bytes): >
Disable focus reporting (8 bytes): >
Save screen (kind of) (8 bytes): >
Restore screen (kind of) (8 bytes): >
You can play with these in bash with echo -e "..." if you want. Just remember
to replace <Esc> with \033 and <C-G> with \007.
Before we get to how Vitality uses these codes, there's one other wrinkle to
take care of: tmux.
When you run Vim inside of tmux it's no longer talking directly to your
terminal. Vim talks to tmux, and tmux talks to your terminal. Tmux will
intercept the escape sequences and deal with them in its own way.
Usually this is what you want, but in Vitality's case it needs to get some of
the escape codes all the way through to iTerm.
Recent versions of tmux allow you to wrap escape sequences in two more escape
sequences, kind of like an envelope. When tmux sees this special wrapper it
will strip it off and send the contents directly to your terminal.
The wrapping sequences are: >
The <Esc> is still a literal escape character.
You also need to double any escape characters in your original sequence. tmux
will collapse them into single characters before sending them along.
You can try this out by opening up an iTerm2 window, starting tmux, running
bash, and then trying to change the cursor shape like before: >
echo -e "hello \033]50;CursorShape=1\007 world"
It won't work, because tmux intercepts and discards the sequences. Try
wrapping it in the tmux wrapping sequences: >
echo -e "hello \033Ptmux;\033\033]50;CursorShape=1\007\033\\ world"
w = wrapper
- = original sequence
D = doubled escape character
This time your cursor should change!
The cursor-changing and focus reporting (we'll get to those soon) sequences
need to go to iTerm 2 and so need to be escaped like this. The screen
restoring ones, however, need to go to tmux, because tmux is handling the
drawing of the screen!
The trickest part of Vitality is how it restores the |FocusGained| and
|FocusLost| autocommands.
The first thing to know is that iTerm 2 supports two escape sequences: one
that turn on "focus reporting" and one that turns it off. You send these
sequences as described in the previous section.
When "focus reporting" is turned on, whenever an iTerm 2 window gains focus it
will send the following string over standard in to whatever program is
currently running, as if you had typed it yourself: >
This sequence is three bytes long. Once again, the <Esc> is a literal ASCII
escape. A similar sequence is sent when the window loses focus: >
That's an "oh", not a "zero".
Play around with this by opening up a new iTerm 2 window without tmux, and
running the following command: >
echo -e "hello \033[?1004h world" && read
Focus and unfocus the window a few times and notice that something like this
will be appearing on your screen: >
$ echo -e "hello \033[?1004h world" && read
The "^[" stands for an escape character.
At this point you might think we could just add mappings that watch for
<Esc>[O and <Esc>[I to Vim and be done. That will work, but isn't ideal.
Imagine adding the following mapping: >
inoremap <Esc>[O <c-o>:doautocmd FocusLost %<cr>
This will work, but not perfectly.
When you're in insert mode and press <Esc> Vim will wait to see what you type
next instead of exiting. Vim doesn't know if [O is coming (and therefore the
mapping should happen) or if it's you pressing the escape key and wanting it
to happen right away.
We can get around this by using a combination of Vim features. But first:
more background.
When you press your right arrow key, what happens? There's no "right" ASCII
code, so how can it tell Vim that you pressed the arrow?
The answer is the iTerm 2 (or any terminal) will send a series of codes to
represent the key. Vim looks for these series, and when it sees them it
treats it as the appropriate key. For example, when you press the right arrow
key Vim will see: >
Three bytes: an escape, an O, then a C. You can test this by mapping that
sequence in Vim: >
nnoremap <Esc>OC :echom "Right was pressed!"<cr>
Press the right arrow key in normal mode and you'll see the message.
You can find out what bytes Vim expects to see for various keys by using the
|set| command on the keycode: >
:set <left>?
This will show you what Vim considers your left arrow key to be.
You can try this with other keys too. Check out the |termcap| help if you
want to know more about the format of the settings.
Open a new instance of Vim and try typing out the <Esc>, O, and C characters
manually. Why doesn't it act like the right arrow key?
To fully understand you'll want to read the |ttimeout|, |timeout|, and
|ttimeout| helps, but in a nutshell: Vim only treats a sequence as a keycode
if it happens really, really fast -- far faster than you could possibly type,
but well within your terminal's ability to send.
So now we know how Vim receives non-ASCII key codes. Why does that matter?
Because we're going two more little facts to fix the <Esc>-hanging property of
our focus-related mappings.
The first fact: although there are usually only 12 or 15 function keys (like
F1, F2, etc) on a physical keyboard, Vim include support for up to thirty
seven of them!
Try the following commands in Vim: >
:set <f37>?
:set <f38>?
The second command will fail saying "Unknown option", but the first fails only
because Vim doesn't know what to expect to receive for F37.
The other fact: you can change these key codes with :set! If you run this: >
:set <f37>=<Esc>A
(you need to enter an actual escape character by pressing Ctrl-V and then
the escape key)
Whenever Vim sees an <Esc> followed very quickly by an A it will treat it as
a press of the F37 key. You can then map that key like any other: >
nnoremap <f37> :echo "HOW BIG IS YOUR KEYBOARD?!"<cr>
This avoids the hanging-<Esc> problem that simply mapping the sequence
manually has. Well, technically it doesn't, but it only hangs for
|ttimeoutlen| milliseconds which is usually set to something on the order of
10, which is far too quick for humans to notice.
You might be able to see where we're going from here. Vitality maps iTerm 2's
focus-reporting sequences to two of these higher F keys (F24 and F25 by
default) and then maps those to the "fire the autocommand" functionality.
Now that we know how all this stuff works we can put it all together.
Vitality works by changing four Vim settings:
|t_ti| Sent to the terminal from Vim when Vim starts up.
|t_te| Sent to the terminal from Vim when Vim shuts down.
|t_SI| Sent to the terminal from Vim when Vim enters insert mode.
|t_EI| Sent to the terminal from Vim when Vim leaves insert mode.
These settings are strings of the bytes we want to send.
It builds the proper escape codes by checking to make sure we're in iTerm
2 (and possibly tmux) by looking at some environment variables. Then it sets
the four variables above to do the following:
|t_ti| Vim startup: change cursor to block, enable focus reporting, save the screen.
|t_te| Vim shutdown: disable focus reporting, restore the screen.
|t_SI| Enter insert mode: change cursor to bar.
|t_EI| Leave insert mode: change cursor to block.
All that's left is to map the focus-reporting keys as described in the last
section and it's done!
You now have a friendlier Vim/tmux/iTerm 2 environment!
4. License *VitalityLicense*
Vitality is MIT/X11 licensed.
5. Bugs *VitalityBugs*
If you find a bug please post it on the issue tracker:
6. Contributing *VitalityContributing*
Think you can make this plugin better? Awesome! Fork it on BitBucket or
GitHub and send a pull request.
7. Changelog *VitalityChangelog*
* Fix a bug in FocusGained/FocusLost when omnicompletion is active (thanks
* Add vitality_normal_cursor and vitality_insert_cursor to allow configuring
more cursor types (thanks lnikkila).
* Preserve existing terminal codes the user has set (thanks aaronjensen).
* Don't spew errors on FocusGained if no autocommands are defined (thanks
* Force cursor to a block on start (in case another program has changed it).
* Updated for the latest versions of tmux and iTerm with some much nicer
* Handle focus events in command line mode (thanks aaronjensen).
* Make features configurable (thanks dmedvinsky).
* Initial stable release.