Skip to content

Instantly share code, notes, and snippets.

@randymorris
Created February 14, 2011 14:23
Show Gist options
  • Save randymorris/825937 to your computer and use it in GitHub Desktop.
Save randymorris/825937 to your computer and use it in GitHub Desktop.
This setup is very ugly and will hopefully get better when some of the TODOs
for tmux get implemented.
---
1. Add the stuff from the vimrc file to your .vimrc or some file that is sorced
from your .vimrc.
- This just adds a few maps and functions to escape vim with ^w<motion>
when you are on an edge window in vim.
2. Add the stuff from the tmux.conf to your .tmux.conf, making changes to the
prefix as you wish. Just be sure to leave ^w as a prefix.
- This sets _almost_ all of the vim ^W commands to use our new shell script
to determine whether or not we are currently in a vim session. The
exceptions are the g* commands which rely on chaining. Chaining afaict
is not possible with this approach.
3. Add tmux_vim_integration.sh somewhere in your $PATH.
- NOTE: You will *need* to edit this file to set any tmux keybinds that
conflict with vim ^w commands.
- This is an ugly artifact of tmux not being able to set a second key-table
like ratpoison can. Once that is possible we will be able to only map
motion commands using ^w as a prefix for a different key-table.
---
You should now be able to move around between vim splits and tmux panes with
the same hjkl keybinds.
# global settings
set -g prefix C-a,C-w
# Vim passthrough keys.
# for the rest of the tmux keybinds, see tmux_vim_integration.sh. This will
# be a lot prettier once tmux supports multiple key-tables.
bind-key C-w run-shell "tmux_vim_integration.sh 'w'"
bind-key w run-shell "tmux_vim_integration.sh 'w'"
bind-key + run-shell "tmux_vim_integration.sh '+'"
bind-key - run-shell "tmux_vim_integration.sh '-'"
bind-key < run-shell "tmux_vim_integration.sh '<'"
bind-key = run-shell "tmux_vim_integration.sh '='"
bind-key > run-shell "tmux_vim_integration.sh '>'"
bind-key H run-shell "tmux_vim_integration.sh 'H'"
bind-key J run-shell "tmux_vim_integration.sh 'J'"
bind-key K run-shell "tmux_vim_integration.sh 'K'"
bind-key L run-shell "tmux_vim_integration.sh 'L'"
bind-key P run-shell "tmux_vim_integration.sh 'P'"
bind-key R run-shell "tmux_vim_integration.sh 'R'"
bind-key S run-shell "tmux_vim_integration.sh 'S'"
bind-key T run-shell "tmux_vim_integration.sh 'T'"
bind-key W run-shell "tmux_vim_integration.sh 'W'"
bind-key ] run-shell "tmux_vim_integration.sh ']'"
bind-key ^ run-shell "tmux_vim_integration.sh '^'"
bind-key _ run-shell "tmux_vim_integration.sh '_'"
bind-key b run-shell "tmux_vim_integration.sh 'b'"
bind-key c run-shell "tmux_vim_integration.sh 'c'"
bind-key d run-shell "tmux_vim_integration.sh 'd'"
bind-key f run-shell "tmux_vim_integration.sh 'f'"
bind-key F run-shell "tmux_vim_integration.sh 'F'"
bind-key h run-shell "tmux_vim_integration.sh 'h'"
bind-key i run-shell "tmux_vim_integration.sh 'i'"
bind-key j run-shell "tmux_vim_integration.sh 'j'"
bind-key k run-shell "tmux_vim_integration.sh 'k'"
bind-key l run-shell "tmux_vim_integration.sh 'l'"
bind-key n run-shell "tmux_vim_integration.sh 'n'"
bind-key o run-shell "tmux_vim_integration.sh 'o'"
bind-key p run-shell "tmux_vim_integration.sh 'p'"
bind-key q run-shell "tmux_vim_integration.sh 'q'"
bind-key r run-shell "tmux_vim_integration.sh 'r'"
bind-key s run-shell "tmux_vim_integration.sh 's'"
bind-key t run-shell "tmux_vim_integration.sh 't'"
bind-key v run-shell "tmux_vim_integration.sh 'v'"
bind-key w run-shell "tmux_vim_integration.sh 'w'"
bind-key x run-shell "tmux_vim_integration.sh 'x'"
bind-key z run-shell "tmux_vim_integration.sh 'z'"
bind-key | run-shell "tmux_vim_integration.sh '|'"
bind-key } run-shell "tmux_vim_integration.sh '}'"
#!/bin/bash
#
# This script sets up vim<->tmux integration.
#
# It allows one to use a single set of keybinds to navigate between
# vim windows and tmux panes. This makes it feel like vim has a
# terminal emulator built in.
[ -z $1 ] && exit
log(){
# echo $@
# echo $@ >> ~/tmp/log
return
}
passthrough=$1
tmux=( $(tmux display -p "#S #I #P") )
log ${tmux[@]}
sedstr="1,/^Sessions:/ d
:session
/^ ${tmux[0]}:/{ :window; n;
/^ ${tmux[1]}:/{ :pane; n
/^ ${tmux[2]}:/{ s/[^:]\+:\s\S\+ \([[:digit:]]\+\).*/\1/; p; q
}
b pane
}
b window
};
n
/^$/q
b session"
pid=$(tmux info | sed -n "$sedstr")
log $pid
if ps --ppid $pid | grep -q vim; then
log 'Current app is `vim`'
# pass any key through to vim
exec tmux send-keys C-w $passthrough
else
log 'Current app is `tmux`'
# this list is pulled from `tmux list-keys`. once tmux supports multiple
# key-tables, this should be a lot prettier.
case $passthrough in
'L') passthrough="switch-client -l";;
'R') passthrough="kill-pane";;
'S') passthrough="split-window -h";;
'c') passthrough="new-window";;
'd') passthrough="detach-client";;
'f') passthrough="command-prompt \"find-window '%%'\"";;
'h') passthrough="select-pane -L";;
'i') passthrough="display-message";;
'j') passthrough="select-pane -D";;
'k') passthrough="select-pane -U";;
'l') passthrough="select-pane -R";;
'n') passthrough="next-window";;
'o') passthrough="select-pane -t :.+";;
'p') passthrough="previous-window";;
'q') passthrough="display-panes";;
'r') passthrough="refresh-client";;
's') passthrough="split-window";;
't') passthrough="clock-mode";;
'w') passthrough='last-pane';;
'x') passthrough="confirm-before kill-pane";;
esac
tmux $passthrough
# explicitly exit 0 to hush tmux in the case $passthrough isn't a
# tmux command
exit 0
fi
nnoremap <silent> <C-w>h :call WindowMotion('h')<cr>
nnoremap <silent> <C-w>j :call WindowMotion('j')<cr>
nnoremap <silent> <C-w>k :call WindowMotion('k')<cr>
nnoremap <silent> <C-w>l :call WindowMotion('l')<cr>
function WindowMotion(dir) "{{{
let dir = a:dir
let old_winnr = winnr()
execute "wincmd " . dir
if old_winnr != winnr()
return
endif
if dir == 'h'
let dir = '-L'
elseif dir == 'j'
let dir = '-D'
elseif dir == 'k'
let dir = '-U'
elseif dir == 'l'
let dir = '-R'
endif
call system('tmux select-pane ' . dir)
endfunction
@mrosset
Copy link

mrosset commented Feb 14, 2011

this is why I use screen or use screen bindings in tmux. :P

@randymorris
Copy link
Author

@str1ngs: even with screen you can't use the same keybind to move within vim windows and within screen windows, unless I'm missing something. This allows me to (say) have tmux up with two panes. One is running a shell and one is running vim with some splits in it. I can freely move around inside of vim and also between vim and the shell all via ^w. I'm very open to a better solution. Multiple keybinds for the "same" action sucks.

@Daenyth
Copy link

Daenyth commented Feb 15, 2011

Maybe this could use an abstraction layer that would give a unified API for both tmux and screen. Possible?

@mrosset
Copy link

mrosset commented Feb 16, 2011

@rson you mean slits in screen and tmux? because I dont use those just tabs. same with vim and I use dwm to handle all my window splitting.

@randymorris
Copy link
Author

@str1ngs I suppose I do mean splits in screen. tmux calls them panes. vim calls them windows.

@Daenyth It would be nice. I'd really like to get a bit to sit down with this and get the ugly parts cleaner somehow but I'm not sure the best way to go about it. If you have any ideas or suggestions, feel free to hack on it and let me know if you come up with anything. When I have free time I'll be doing the same.

My main points of hate with this is #3. It just sucks, but I don't see a better way. I don't remember if screen supports multiple key-tables or not (ratpoison was based largely on screen so it may) but that would make this a bit better anyway. Still wouldn't be perfect.

@Daenyth
Copy link

Daenyth commented Feb 16, 2011

Dunno. My free time is really limited for the foreseeable future. :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment