Video

Want to see the full-length video right now for free?

Notes

Advanced Workflow

Quick Panes

As quick as we've been able to make it to open a pane and interact with a process, it turns out that tmux accepts an optional shell-command argument to the split-window command that can make this even more efficient. I've been calling these interactions "quick panes" and have been using them more and more of late. They all take advantage of the fact that tmux will close the pane when the command provided as the shell-command argument exits. This makes for an extremely efficient workflow for things like accessing your todo list or wiki, interacting with commands like irb or [gitsh][]

In addition, we can specify a starting directory for the pane in addition to the shell-command using the -c start-directory flag.

Some examples are:

# Quickly view system & process info in htop
bind-key h split-window -h "htop"

# Quickly edit todo list
bind-key t split-window -h "vim ~/todo.md"

# Quickly edit a file in your wiki
bind-key w split-window -h -c ~/my-wiki "reattach-to-user-namespace vim +CtrlP"

Note, you will need to specifically include reattach-to-user-namespace on OS X for proper clipboard integration when using these quick panes.

[gitsh]: https://github.com/thoughtbot/gitsh

Fuzzy Session Switching

Building on the workflow of quick panes and incorporating the wonderful [fzf][] "Fuzzy finder for your shell" utility, I've been able to pull together a mapping that provides a prompted fuzzy matching list of the sessions for rapid and intuitive navigation between them, without losing the context of the work in your current pane.

The mapping and shell pipeline it uses are largely based on [an example from the fzf documentation][], but is specific to session switching. The final mapping is included in the tmux conk below.

[fzf]: https://github.com/junegunn/fzf [an example from the fzf documentation]: https://github.com/junegunn/fzf#using-fzf-with-tmux-splits

Prompted Bindings

Although the tmux authors have done an amazing job with defining a simple and consistent interface, at times a command may be a bit much to remember or type. Luckily, we can make use of the command-prompt tmux command, which takes a prompt string and a command template string, and can interpolate the output of the prompt into the command by replacing the placeholder value %%.

# Prompted join-pane
bind-key j command-prompt -p "join pane from: "  "join-pane -h -s '%%'"

# Easily swap a pane (targeted by pane number) with the current pane
bind-key s display-panes\; command-prompt -p "pane #: "  "swap-pane -t '%%'"

Never Leaving Tmux

After years of using tmux, I've reached a point where I feel a bit lost if I find myself not in a tmux session. I've come to rely on the ability to arbitrarily split my screen to display and compare content, easily switch between sessions, etc. As such, I've built up a collection of configurations and mappings that allow me to always remain inside of a tmux session.

Auto-attach New Shell Sessions

The first line of defense is initial shell startup. With the following shell functions added to your shell rc file (~/.bashrc or ~/.zshrc in my case), your shell will automatically connect to a tmux session on startup, even creating the session as needed.

Note, this functionality relies on the availability of the [tat script][] introduced in step 3.

# Add this to your zshrc or bzshrc file
_not_inside_tmux() { [[ -z "$TMUX" ]] }

ensure_tmux_is_running() {
  if _not_inside_tmux; then
    tat
  fi
}

ensure_tmux_is_running

[tat script]: https://github.com/thoughtbot/dotfiles/blob/master/bin/tat

Breaking Out Sessions

Occasionally I will want to open a new tmux session after having navigated into a different git repo. Usually this would involve detaching from the current session as tmux will kindly prevent what seems like a nested session.

The [tat script][] has functionality built in to be able to create a detached session, avoiding the concern for session nesting, and then attach to it. This allows us "break out" a session based on the current pane, even cleaning up the pane after creating the new session. The following key binding provides this behavior, mapping it to <prefix>C-b for "break":

bind-key C-b send-keys 'tat && exit' 'C-m'

Killing Sessions

By default, killing a session via closing the final process of the session, or explicitly running the kill-session command, will disconnect us from tmux. The following mapping of <prefix>K will instead kill the current session and switch us to another session, keeping us connected to tmux throughout.

bind-key K run-shell 'tmux switch-client -n \; kill-session -t "$(tmux display-message -p "#S")" || tmux kill-session'

Final Configuration

The .tmux.conf as of the end of step 5:

unbind C-b
set -g prefix C-s
bind-key -r C-s send-prefix

bind-key r source-file ~/.tmux.conf \; display-message "~/.tmux.conf reloaded"

is_vim='echo "#{pane_current_command}" | grep -iqE "(^|\/)g?(view|n?vim?)(diff)?$"'
bind -n C-h if-shell "$is_vim" "send-keys C-h" "select-pane -L"
bind -n C-j if-shell "$is_vim" "send-keys C-j" "select-pane -D"
bind -n C-k if-shell "$is_vim" "send-keys C-k" "select-pane -U"
bind -n C-l if-shell "$is_vim" "send-keys C-l" "select-pane -R"
bind -n C-\ if-shell "$is_vim" "send-keys C-\\" "select-pane -l"

set-option -g default-terminal "screen-256color"
set-option -g status-keys "emacs"

set-option -g status-left-length 50

set-option -g status-right ""

bind-key - split-window -v  -c '#{pane_current_path}'
bind-key \ split-window -h  -c '#{pane_current_path}'

bind -n S-Left resize-pane -L 2
bind -n S-Right resize-pane -R 2
bind -n S-Down resize-pane -D 1
bind -n S-Up resize-pane -U 1

bind -n C-Left resize-pane -L 10
bind -n C-Right resize-pane -R 10
bind -n C-Down resize-pane -D 5
bind -n C-Up resize-pane -U 5

bind c new-window -c '#{pane_current_path}'

set-option -g base-index 1
set-option -g renumber-windows on

bind-key b break-pane -d

bind-key C-j choose-tree

# Use vim keybindings in copy mode
setw -g mode-keys vi

# Setup 'v' to begin selection as in Vim
bind-key -t vi-copy v begin-selection
bind-key -t vi-copy y copy-pipe "reattach-to-user-namespace pbcopy"

# Update default binding of `Enter` to also use copy-pipe
unbind -t vi-copy Enter
bind-key -t vi-copy Enter copy-pipe "reattach-to-user-namespace pbcopy"

bind-key h split-window -h "htop"
bind-key t split-window -h -c ~/ "vim todo.md"
bind-key w split-window -h -c ~/my-wiki "vim +CtrlP"

bind C-j split-window -v "tmux list-sessions | sed -E 's/:.*$//' | grep -v \"^$(tmux display-message -p '#S')\$\" | fzf --reverse | xargs tmux switch-client -t"

# Prompted join-pane
bind-key j command-prompt -p "join pane from: "  "join-pane -h -s '%%'"

# Easily swap a pane (targeted by pane number) with the current pane
bind-key s display-panes\; command-prompt -p "pane #: "  "swap-pane -t '%%'"

bind-key C-b send-keys 'tat && exit' 'C-m'
bind-key K run-shell 'tmux switch-client -n \; kill-session -t "$(tmux display-message -p "#S")" || tmux kill-session'