there’s no place like code

Project–specific tmux sessions

The Problem

If you use a terminal multiplexer tmux, you might find yourself re–creating the same pane layout every time you want to start working on project. There are some tools that allows you to configure tmux sessions, like tmuxinator, but they never felt natural to me – why would I want to use YAML config file, when tmux has it’s own config syntax?

The Solution

Let’s put tmux.conf files in project directories.

  1. Set up Git globally to ignore tmux.conf files in the root of repository.

    Add this to ~/.config/git/ignore (create the directory and the file if it doesn’t exist):

    /tmux.conf
    
  2. Load tmux.conf from current directory, when starting a new tmux session.

    Add this to your ~/.config/tmux/tmux.conf (or ~/.tmux.conf, if you prefer):

    if-shell -b '[ -e ./tmux.conf ]' {
      set-hook -g after-new-session 'source-file -q ./tmux.conf'
    }
    
  3. If you have tmux, running, re–read the config file:

    tmux source-file ~/.config/tmux/tmux.conf
    

Et voilà!

Examples

The one I use most often:

rename-session 'project-foo'

# Create short pane at the top of the window
split-window -vbdl 6
# Run npm install and `dev` npm script on the top pane
send -t "{previous}" "npm install && npm run --if-present dev" Enter
# Fetch latest changes in the main pane
send "git fetch" Enter

Here’s a much more involved example with multiple panes on top of the screen:

rename-session 'project-bar'

# | watch+build | serve | api |
# |-------------|-------|-----|
# | main                      |

split-window -vbl10 ; send 'npm install && nodemon -e "npm run build"' Enter
split-window -h     ; send 'npm run serve' Enter
split-window -h     ; send 'cd ../api && npm install && npm run dev' Enter

select-layout -E

select-pane -t'{next}'
send 'git fetch' Enter

Or even more complex one that waits for npm install to finish before running things in other panes:

rename-session 'project-zar'
set-environment -g SOME_IP_ENV_VAR '10.13.32.3'

# | dev | storybook | api |
# |-----------------------|
# | main                  |

# dev
split-window -vbl10

# storybook
split-window -h

send -t 0 '  \
  npm install; \
  tmux send -t0 "npm run dev " Enter; \
  tmux send -t1 "npm run storybook --no-open" Enter; \
' Enter

# api
split-window -h
select-pane -t'{next}'
send 'cd ../api' Enter
send 'git fetch' Enter
send 'npm install && npm run dev' Enter

select-layout -E

# main working area
select-pane -t'{next}'
send 'git fetch' Enter

Default configuration

You can go very complex, but most of the time you will probably want to use the same config for most of your projects. But why copy the same tmux.conf to each directory?

Let’s expand our after-new-sesssion hook in tmux.conf. If ./tmux.conf is not found, it will detect if project is using npm or yarn and run run dev npm script in the top pane. While we are here, let’s use current directory name as a session name.

set-hook -g after-new-session '\
    run-shell "tmux rename-session \"$( basename \"#{pane_current_path}\" \)\""; \
    \
    if-shell -b "[ -e ./tmux.conf ]" { \
        source-file ./tmux.conf; \
    } { \
        if-shell -b "[ -e ./yarn.lock ]" { \
            split-window -vbdl 6; \
            send -t "{previous}" "yarn install && yarn run --if-present dev" Enter; \
            send "git fetch" Enter; \
        } { \
            if-shell -b "[ -e ./package.json ]" { \
                split-window -vbdl 6; \
                send -t "{previous}" "npm install && npm run --if-present dev" Enter; \
                send "git fetch" Enter; \
            }; \
        }; \
    }; \
'