< expLog

Notes on Using Tmux

A collection of notes around how I (ab)use and configure tmux: this is a live document, edited as I refine my workflows.

This is not an introduction to tmux, but more a set of workflows of using it. I assume a familiarity with what tmux is, and perhaps that you already use it.

Some Terms

I always find myself somewhat befuddled when reading the man page for tmux; here's a list of the most important words:

  • Sessions: different workspaces, generally identified by numbers
  • Windows: one tab inside a session
  • Panes: the parts of windows, each representing one shell instance

Dual Wielding Tmuxes (tmuxen?)

I generally work in a shell on my laptop and as well as on development servers; and occasionally multiple devservers. To reduce the number of keyboard shortcuts I need to remember, I prefer to use a simple Terminal Emulator and rely on Tmux to manage all state for me.

To be able to send share run commands with some sanity, I bind Ctrl-b as my prefix-key for laptop tmux; and Ctrl-a as the prefix for all devserver tmuxes.

In general this flow has worked out remarkably well for me: I only need to remember one style of keyboard shortcuts to manipulate and add terminals, and my sessions persist against X-server restarts.

The fact that tmux works as a very sane window-manager is a significant bonus. I use dwm as my non-terminal window manager, and things work out fairly smoothly.

Emacs integration

I used to rely heavily on emamux to have a shell in parallel to my emacs session, but that doesn't work from a graphical emacs session.

Instead, I've written my own custom snippet to run commands on the last active tmux pane using tmux send-keys:

(setq tr--last-command nil)

(defun tr (command)
  "Run the specified command in the currently active tmux pane"
  (interactive "sCommand: ")
  (setq tr--last-command command)
  (call-process "tmux" nil nil nil "send-keys" command "Enter"))

(defun trr ()
  "Re-run the previous command"
  (interactive)
  (if tr--last-command
      (call-process "tmux" nil nil nil "send-keys" tr--last-command "Enter")
    (message "No available previous command!")))

(global-set-key (kbd "C-c x") 'tr)
(global-set-key (kbd "C-c r") 'trr)

It's fairly trivial, but remarkably speeds up iteration because I can easily trigger tests, builds and other shenanigans without having to navigate away from emacs: and I can use it in any scenario without having to go through a complex set up process first.

My current configuration

I've customized tmux a little bit to make it more convenient: it started out as a direct copy from a blog post , and I've tweaked it significantly over the years.

I'll only describe my additional customization:

Mouse support

Tmux has excellent mouse support: I've even used it to surprise my co-workers on occasion.

Round trip time was a little bit high while I was trying to resize a pane – so it looked more as if I wished something existed, and he started laughing. A few seconds later it actually resized, and he was astonished.

set-option -g mouse on

Vim Bindings

As the title says: being able to use v in copy mode or / to search forwards and ? to search backwards feels normal.

Similarly, using C-b h/j/k/l for navigating windows just works in my head; to the point I no longer consciously think about it.

set -g status-keys vi
set -g mode-keys vi

# Better window navigation
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

Minimalist UI

I prefer a line on the top with minimal formatting, including no visible "-" and "|" in the separators.

# Custom status line
set-option -g status-left '-- #[fg=colour253]#S - '
set-window-option -g window-status-format '#[fg=colour244]#I/#[fg=colour253] #W '
set-window-option -g window-status-current-format '#[fg=colour244]#I/#[fg=colour253,bg=colour238] #W '
set-option -g status-right '#[fg=colour250] %Y-%m-%d #[fg=colour254]%H.%M'
set-option -g status-bg colour234
set-option -g status-fg colour007
set-option -g status-position top

# Custom split colours
set -g pane-active-border-style bg=colour234,fg=colour234
set -g pane-border-style bg=colour234,fg=colour234

tmux.png

Large scroll back

For me, one of tmux's super powers is to be able to scroll back through output easily, particularly while looking at something with fairly spammy logs and forgetting to pipe it through less , tee,or saving it to a file.

# More scroll back!
set-option -g history-limit 10000

Nicer colors

To be perfectly honest I'm not sure if this is required anymore, but it did help emacs within tmux work better for me.

# Force 256 colours
set -g default-terminal screen-256color

Always stay in the same directory

I believe I copied from this from Stack Overflow a few years ago: but this lets me easily navigate splits without constantly having to change directories.

# Maintain the current path
bind '%' split-window -h -c '#{pane_current_path}'  # Split panes horizontal
bind '"' split-window -v -c '#{pane_current_path}'  # Split panes vertically
bind c new-window -c '#{pane_current_path}' # Create new window

Always renumber windows

Before using this option I would end up with windows going beyond 9, which are a mess to navigate – and then I'd end up closing several windows and cleaning up my sessions.

With automatic renumbering my shortcuts stay consistent. This might not be the best choice if you're a stickler about what goes into which window – I tend to do this opportunistically.

set-option -g renumber-windows on

The full configuration

# Stolen from http://mutelight.org/articles/practical-tmux
# and then heavily modified over the last several years.

# Make pbcopy and pbpaste work from tmux
# set-option -g default-command "reattach-to-user-namespace -l zsh"

# C-a C-a to jump to the last active window
bind-key C-b last-window

# Windows from 1
set -g base-index 1

# Resize based on smallest client actually active at the moment
setw -g aggressive-resize on

# Faster key repetition
set -s escape-time 0

# Mouse support!
set-option -g mouse on

# vi everywhere
set -g status-keys vi
set -g mode-keys vi

# Custom status line
set-option -g status-left '-- #[fg=colour253]#S - '
set-window-option -g window-status-format '#[fg=colour244]#I/#[fg=colour253] #W '
set-window-option -g window-status-current-format '#[fg=colour244]#I/#[fg=colour253,bg=colour238] #W '
set-option -g status-right '#[fg=colour250] %Y-%m-%d #[fg=colour254]%H.%M'
set-option -g status-bg colour234
set-option -g status-fg colour007
set-option -g status-position top

# Better window navigation
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# More scroll back!
set-option -g history-limit 10000

# Force 256 colours
set -g default-terminal screen-256color

# Custom split colours
set -g pane-active-border-style bg=colour234,fg=colour234
set -g pane-border-style bg=colour234,fg=colour234


# Maintain the current path
bind '%' split-window -h -c '#{pane_current_path}'  # Split panes horizontal
bind '"' split-window -v -c '#{pane_current_path}'  # Split panes vertically
bind c new-window -c '#{pane_current_path}' # Create new window

# Automatically fill holes
set-option -g renumber-windows on

Frequently used, somewhat obscure commands

  • Ctrl-b z: Zoom the current window: particularly useful for copying multi-line content.
  • Ctrl-b space: Quickly toggle between window layouts.
  • Ctrl-b : detach-clients -a: This one is useful to kill all other clients attached to the current session; good to clean up in case you got disconnected or have a terminal emulator lost in the labyrinth of your desktop window manager.
  • Ctrl-b : kill-pane: Useful when things are truly, completely, absolutely FUBAR. I've used it in cases my shell has become completely unresponsive (and yes, I also double checked that I hadn't accidentally hit Ctrl-S / ran Ctrl-Q).

References

History

  • 2020-07-07: First version.
  • 2020-07-08: Fleshed it out a little.