Yesterday I was reading through my Ruby Weekly email and got sucked into a tmux workflow video. Although I have used tmux remotely, this video inspired me to add tmux to my local workflow. The following is a run down of my configuration, what I find awesome and what I find annoying.
- OSX 10.8.3, Mountain Lion
- iTerm2 (Dan Lowe explains why)
- zsh with oh-my-zsh
- vim with the molokai theme and plugins managed by pathogen
I assume these are installed and configured.
Before getting started I wanted to checkup on iTerm2’s native support of tmux. In the past, support was only available via a custom build of tmux. This was awkward and weird so I never tried it out. Now they support the official version of tmux. Whoop!
brew install tmux
In order to get iTerm2 tmux integration, you have to keep a window open that acts as the session communication channel for the tmux windows and panes. I agree with Dan and find that this will become annoying fast. Instead, I am not using the iTerm2 integration and using tmux as is.
To use this config and fix copy-paste issues with tmux on a mac, install reattach-to-user-namespace:
brew install reattach-to-user-namespace
Basically, tmux has it’s own pasteboard and is not able to read or write to the user’s pasteboard. This app reattaches tmux to the user’s pasteboard.
Back in the day I used ^Z and ^B as my tmux prefix key. I’m not a fan of finger gymnastics and changed it to ^A. Many people suggested this and I found it to be much easier to work with. However, after a few programming escapdes, I found that ^A was getting in my way.
When I am in tmux with vim, I am not able to increment numbers with ^A. It’s a small thing but I’d rather not retrain my brain to implement a longer key sequence.
I am also conditioned to being able to go to the beginning of the line in bash or zsh with ^A. (I could leverage vi-mode but I don’t feel like retraining my brain for that just yet.)
Chris' choice to use ^J as a prefix has made things flow quite nicely. I haven’t found any conflicts yet and I hope I don’t find any!
I like rebinding to characters that match the split action:
# window splitting unbind % bind | split-window -h unbind '"' bind - split-window -v
I also like to map vim navigation to pane resizing:
# resize panes bind -r H resize-pane -L 5 bind -r J resize-pane -D 5 bind -r K resize-pane -U 5 bind -r L resize-pane -R 5
After trying this configuration out for awhile, I began to realize that I kept trying to scroll back with the mouse. So, I needed my mouse to function according to my habits:
set -g mode-mouse on set -g mouse-resize-pane on set -g mouse-select-pane on set -g mouse-select-window on
Dan’s mouse mode post has a pair of useful mouse toggle shortcuts:
bind m \ set -g mode-mouse on \;\ set -g mouse-resize-pane on \;\ set -g mouse-select-pane on \;\ set -g mouse-select-window on \;\ display 'Mouse: ON' bind M \ set -g mode-mouse off \;\ set -g mouse-resize-pane off \;\ set -g mouse-select-pane off \;\ set -g mouse-select-window off \;\ display 'Mouse: OFF'
I am a huge proponent of keeping your fingers on the home row and using the mouse as little as possible. However, every once in a while I just want to resize a pane with my mouse or scroll back with my mouse. Having that extra nicety just feels good.
There is one draw back: mouse mode does not support selection.
When I scroll back to find something and select it, the mouse up event causes the buffer to jump back to the command prompt and deselects the selection.
There are two ways around this:
- Disable mouse mode before selecting text:
- Hold down the option key while selecting text
Both are not ideal but tolerable, given the benefits of tmux.
What really solidified tmux for me was Dan’s iterm2 keymaps for tmux customization. iTerm2’s ability to send hex codes definitely causes Angels to dance.
Here are my key sequences:
|Key Sequence||Hex Sequence||Purpose|
|^D||0x04||disconnect from pane or window|
|^J c||0x0A 0x63||create new window|
|^J d||0x0A 0x64||detach from session|
|^J <||0x0A 0x3C||previous window|
|^J >||0x0A 0x3E||next window|
|^J |||0x0A 0x7C||Split pane horizontally|
|^J -||0x0A 0x2D||Split pane vertically|
|^J K||0x0A 0x4B||Resize pane up|
|^J J||0x0A 0x4A||Resize pane down|
|^J H||0x0A 0x48||Resize pane left|
|^J L||0x0A 0x4C||Resize pane right|
|^J <arrow-up>||0x0A 0x1B 0x5B 0x41||move to pane above|
|^J <arrow-down>||0x0A 0x1B 0x5B 0x42||move to pane below|
|^J <arrow-right>||0x0A 0x1B 0x5B 0x43||move to pane at right|
|^J <arrow-left>||0x0A 0x1B 0x5B 0x44||move to pane at left|
Here are my iTerm2 tmux shortcuts:
|Cmd-w||0x04||disconnect from pane or window|
|Cmd-t||0x0A 0x63||create new window|
|Cmd-d||0x0A 0x64||detach from session|
|Cmd-Left||0x0A 0x3C||previous window|
|Cmd-Shift-h||0x0A 0x3C||previous window|
|Cmd-Right||0x0A 0x3E||next window|
|Cmd-Shift-l||0x0A 0x3E||next window|
|Cmd-Shift-|||0x0A 0x7C||Split pane horizontally|
|Cmd-Shift-_||0x0A 0x2D||Split pane vertically|
|Cmd-Opt-k||0x0A 0x4B||Resize pane up|
|Cmd-Opt-j||0x0A 0x4A||Resize pane down|
|Cmd-Opt-h||0x0A 0x48||Resize pane left|
|Cmd-Opt-l||0x0A 0x4C||Resize pane right|
|Cmd-k||0x0A 0x1B 0x5B 0x41||move to pane above|
|Cmd-j||0x0A 0x1B 0x5B 0x42||move to pane below|
|Cmd-l||0x0A 0x1B 0x5B 0x43||move to pane at right|
|Cmd-h||0x0A 0x1B 0x5B 0x44||move to pane at left|
Here are all of my iTerm2 global shortcuts:
Last but not least I added a few helpful commands to my .zshrc to make session management a little easier.
Command completion is always nice, so I added session name completion for my session function.
Make sure custom command completion is enabled:
fpath=(~/.zsh/completion $fpath) autoload -U compinit compinit
…and add the completion script:
Tmux is great at managing sessions and recoving from accidental disconnects. It still feels in the way at times but new things always do. It will take some time getting use to but I’m sure it has found a home.