Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit.

  • Loading branch information...
commit a8a23e865cdf8792ff2bdfa42070059fe07d0e80 0 parents
@sjl authored
19 LICENSE.markdown
@@ -0,0 +1,19 @@
+Copyright (C) 2012 Steve Losh and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.**
+
29 README.markdown
@@ -0,0 +1,29 @@
+Vitality
+========
+
+(Vit)ality is a plugin that makes (V)im play nicely with (i)Term 2 and
+(t)mux.
+
+Features
+--------
+
+Vitality restores the `FocusLost` and `FocusGained` autocommand functionality.
+Now Vim can save when iTerm 2 loses focus, even if it's inside tmux!
+
+It also handles switching the cursor to a bar shaped one when in insert mode,
+and restoring it when not.
+
+Pull requests for other helpful behavior are welcome.
+
+Installation and Usage
+----------------------
+
+Use Pathogen to install.
+
+You shouldn't need to do anything else, but you can read `:help vitality` if
+you're curious.
+
+License
+-------
+
+MIT/X11
351 doc/vitality.txt
@@ -0,0 +1,351 @@
+*vitality.txt* make Vim play nicely with iTerm2 and tmux
+
+ █████ █ ██ ███
+ ██████ █ █████ █ █ ███ █ █
+██ █ █ ██████ ██ ██ ███ ██
+ █ ██ █ ███ ██ ██ █ ██
+ █ ███ █ ████████ ██ ████████
+ ██ ██ █ ███ ████████ ████ ██ ███ ████████ ██ ████
+ ██ ██ █ ███ ██ █ ███ █ ██ ███ ██ ██ ███ █
+ ██ ██ █ ██ ██ █ ████ ██ ██ ██ ██ ████
+ ██ ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██ ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██ ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██ █ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ███ █ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ███████ ███ █ ██ █████ ██ ███ █ ███ █ ██ █████████
+ ███ ███ ███ ██ ███ ███ ████ ███
+ ███
+ Vitality makes [V]im play nicely with █████ ███
+ [i]Term2 and ████████ ██
+ [t]mux. █ ████
+
+==============================================================================
+CONTENTS *clam-contents*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ 1. Overview and Usage .............. |VitalityOverview|
+ 2. How it Works .................... |VitalityHow|
+ 3. License ......................... |VitalityLicense|
+ 4. Bugs ............................ |VitalityBugs|
+ 5. Contributing .................... |VitalityContributing|
+ 6. 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. 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.
+
+------------------------------------------------------------------------------
+2.1 I WILL BE YOUR HANDS AND EYES
+
+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: >
+
+ <Esc>]50;CursorShape=1<C-G>
+
+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)
+respectively.
+
+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): >
+ <Esc>]50;CursorShape=1<C-G>
+
+Set cursor to a block shape (19 bytes): >
+ <Esc>]50;CursorShape=0<C-G>
+
+Enable focus reporting (8 bytes): >
+ <Esc>[?1004h
+
+Disable focus reporting (8 bytes): >
+ <Esc>[?1004l
+
+Save screen (kind of) (8 bytes): >
+ <Esc>[?1049h
+
+Restore screen (kind of) (8 bytes): >
+ <Esc>[?1049h
+
+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.
+
+------------------------------------------------------------------------------
+2.2 YOU ARE IN A MAZE OF TWISTY LITTLE TERMINAL CODES, NONE ALIKE
+
+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: >
+
+ <Esc>Ptmux;
+ <Esc>\
+
+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"
+ wwwwwwwwww----DDDD---------------------wwwwww
+ 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!
+
+------------------------------------------------------------------------------
+2.3 IT IS PITCH BLACK
+
+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: >
+
+ <Esc>[I
+
+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: >
+
+ <Esc>[O
+
+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
+ ^[[O^[[I^[[O^[[I
+
+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.
+
+------------------------------------------------------------------------------
+2.4 YOU ARE LIKELY TO BE EATEN BY A GRUE
+
+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: >
+
+ <Esc>OC
+
+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.
+
+------------------------------------------------------------------------------
+2.4 WITH WHAT? YOUR BARE HANDS?
+
+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: 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!
+
+==============================================================================
+3. License *VitalityLicense*
+
+Vitality is MIT/X11 licensed.
+
+==============================================================================
+4. Bugs *VitalityBugs*
+
+If you find a bug please post it on the issue tracker:
+http://github.com/sjl/vitality.vim/issues/
+
+==============================================================================
+5. Contributing *VitalityContributing*
+
+Think you can make this plugin better? Awesome! Fork it on BitBucket or
+GitHub and send a pull request.
+
+BitBucket: http://bitbucket.org/sjl/vitality.vim/
+GitHub: http://github.com/sjl/vitality.vim/
+
+==============================================================================
+6. Changelog *VitalityChangelog*
+
+v1.0.0
+ * Initial stable release.
+
+==============================================================================
3  package.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+hg archive ~/Desktop/vitality.zip -I 'doc' -I 'plugin' -I README.markdown -I LICENSE.markdown
135 plugin/vitality.vim
@@ -0,0 +1,135 @@
+" ============================================================================
+" File: vitality.vim
+" Description: Make Vim play nicely with iTerm2 and tmux.
+" Maintainer: Steve Losh <steve@stevelosh.com>
+" License: MIT/X11
+" ============================================================================
+
+" Init {{{
+
+if has('gui_running')
+ finish
+endif
+
+if !exists('g:vitality_debug') && (exists('loaded_vitality') || &cp)
+ finish
+endif
+
+let loaded_vitality = 1
+
+if !exists('g:vitality_fix_cursor') " {{{
+ let g:vitality_fix_cursor = 1
+endif " }}}
+if !exists('g:vitality_fix_focus') " {{{
+ let g:vitality_fix_focus = 1
+endif " }}}
+
+let s:inside_iterm = exists('$ITERM_PROFILE')
+let s:inside_tmux = exists('$TMUX')
+
+" }}}
+
+function! s:WrapForTmux(s) " {{{
+ " To escape a sequence through tmux:
+ "
+ " * Wrap it in these sequences.
+ " * Any <Esc> characters inside it must be doubled.
+ let tmux_start = "\<Esc>Ptmux;"
+ let tmux_end = "\<Esc>\\"
+
+ return tmux_start . substitute(a:s, "\<Esc>", "\<Esc>\<Esc>", 'g') . tmux_end
+endfunction " }}}
+
+function! s:Vitality() " {{{
+ " Escape sequences {{{
+
+ " iTerm2 allows you to turn "focus reporting" on and off with these
+ " sequences.
+ "
+ " When reporting is on, iTerm2 will send <Esc>[O when the window loses focus
+ " and <Esc>[I when it gains focus.
+ "
+ " TODO: Look into how this works with iTerm tabs. Seems a bit wonky.
+ let enable_focus_reporting = "\<Esc>[?1004h"
+ let disable_focus_reporting = "\<Esc>[?1004l"
+
+ " These sequences save/restore the screen.
+ " They should NOT be wrapped in tmux escape sequences for some reason!
+ let save_screen = "\<Esc>[?1049h"
+ let restore_screen = "\<Esc>[?1049l"
+
+ " These sequences tell iTerm2 to change the cursor shape to a bar or block.
+ let cursor_to_bar = "\<Esc>]50;CursorShape=1\x7"
+ let cursor_to_block = "\<Esc>]50;CursorShape=0\x7"
+
+ if s:inside_tmux
+ " Some escape sequences (but not all, lol) need to be properly escaped
+ " to get them through tmux without being eaten.
+
+ let enable_focus_reporting = s:WrapForTmux(enable_focus_reporting)
+ let disable_focus_reporting = s:WrapForTmux(disable_focus_reporting)
+
+ let cursor_to_bar = s:WrapForTmux(cursor_to_bar)
+ let cursor_to_block = s:WrapForTmux(cursor_to_block)
+ endif
+
+ " }}}
+ " Startup/shutdown escapes {{{
+
+ " When starting Vim, enable focus reporting and save the screen.
+ " When exiting Vim, disable focus reporting and save the screen.
+ "
+ " The "focus/save" and "nofocus/restore" each have to be in this order.
+ " Trust me, you don't want to go down this rabbit hole. Just keep them in
+ " this order and no one gets hurt.
+ let &t_ti = enable_focus_reporting . save_screen
+ let &t_te = disable_focus_reporting . restore_screen
+
+ " }}}
+ " Insert enter/leave escapes {{{
+
+ " When entering insert mode, change the cursor to a bar.
+ let &t_SI = cursor_to_bar
+
+ " When exiting insert mode, change it back to a block.
+ let &t_EI = cursor_to_block
+
+ " }}}
+ " Focus reporting keys/mappings {{{
+
+ " Map some of Vim's unused keycodes to the sequences iTerm2 is going to send
+ " on focus lost/gained.
+ "
+ " If you're already using f24 or f25, change them to something else. Vim
+ " supports up to f37.
+ "
+ " Doing things this way is nicer than just mapping the raw sequences
+ " directly, because Vim won't hang after a bare <Esc> waiting for the rest
+ " of the mapping.
+ execute "set <f24>=\<Esc>[O"
+ execute "set <f25>=\<Esc>[I"
+
+ " Handle the focus gained/lost signals in each mode separately.
+ "
+ " The goal is to fire the autocmd and restore the state as cleanly as
+ " possible. This is easy for some modes and hard/impossible for others.
+ "
+ " EXAMPLES:
+ nnoremap <silent> <f24> :doautocmd FocusLost %<cr>
+ nnoremap <silent> <f25> :doautocmd FocusGained %<cr>
+
+ onoremap <silent> <f24> <esc>:doautocmd FocusLost %<cr>
+ onoremap <silent> <f25> <esc>:doautocmd FocusGained %<cr>
+
+ vnoremap <silent> <f24> <esc>:doautocmd FocusLost %<cr>gv
+ vnoremap <silent> <f25> <esc>:doautocmd FocusGained %<cr>gv
+
+ inoremap <silent> <f24> <c-o>:doautocmd FocusLost %<cr>
+ inoremap <silent> <f25> <c-o>:doautocmd FocusGained %<cr>
+
+ " }}}
+endfunction " }}}
+
+if s:inside_iterm
+ call s:Vitality()
+endif
Please sign in to comment.
Something went wrong with that request. Please try again.