Skip to content

Learning tmux

Tags: learning, tmux • Categories: Productivity

Table of Contents

I’ve tried tmux before for about a day at a time and promptly forgot about it and never learned how it worked deeply. However, a friend recently reminded me about tmux’s copy mode and I was intrigued. I had the time to dig in and try to retrain my muscle memory around it.

I use VS Code for my editor, but otherwise, I’m a very terminal-oriented developer. I love interactive REPLs and find that CLI tools generally allow you to move faster than GUI equivalents.

Something that always frustrated me about my development loop was the need to put my hands on the mouse in order to interact (select/copy/find/etc) with the contents of my terminal screen. Tmux’s main promise is to solve that problem, enabling you to interact with the contents of your terminal programmatically via keyboard shortcuts. Very cool.

For instance, one of the things I was specifically excited about is the ability to identify common patterns in my terminal usage and create a one-click extraction script so I can easily copy to my clipboard. Combine this with fzf, fd, and other "modern" CLI tools and you have a very powerful and very fast development experience.

Here’s my finalized tmux config. Below is everything I learned along the way.

Requirements

Here are some specifics I was trying to achieve:

  • Same functionality in iTerm and VS Code Integrated terminal. I switch between these and use the VS Code terminal a lot more (which is xterm.js under the hood)
  • I want to be able to find and copy text very quickly. This is my primary reason for learning tmux.
  • I don’t care about pane splitting since I have that with VS Code terminal and iTerm
  • I want to be able to select + scroll text with a mouse and I don’t want it to be janky (this is one of my primary frustrations with attempting to learn tmux in the past)

Learning Tmux

Here’s what

  • Prefix key. Think of it as an activation key combination (ctrl+b by default). This is the key combo that activates input into tmux.
  • Copy mode enables you to interact with the terminal contents, selecting and copying text. It’s activated by prefix+[ by default but you can customize this.
  • Emacs mode is the default copy mode, here are the bindings If you are like me, you haven’t used emacs keybindings for copying text (you probably use them for key navigation in zsh), and you’ll need to commit some new keystrokes to memory.
    • I ended up finding the vi-mode keybindings for copy mode a bit more intuitive.
  • The notation used to describe keybindings (via tmux list-keys | fzf) is confusing. Here’s how they work:
    • C-b %, press ctrl+b (to activate the prefix), then press cntrl-%. This example splits panes.
    • M-Down, press ctrl+b then press your modifier key and the down arrow.
    • Note that cmd cannot be bound by default unless you do some fancy remapping in your terminal
  • If you are using less here are important keyboard shortcuts. This is important because I attempt to align some of the tmux shortcuts to less for consistency.
    • / to search
    • u for page up
    • d for page down
    • n for next match
    • N for previous match
    • h for help
    • shift+g for EOL
    • g for beginning of file
  • tmux kill-server to destroy everything in case config doesn’t seem to be reloading properly. There are some configuration options that only take effect on a full server restart.
  • Keybindings are per-server, so unless you kill-server a config reload won’t impact these
  • Show an option config tmux show-option -g set-clipboard. You can run this command via the terminal, without being in a tmux session (great for debugging config)
  • I use both iTerm and the integrated VS Code terminal. Getting word chars (the characters that are treated as word boundaries) aligned is a pain. Here’s the xterm config, here’s the more extensive tmux default This is useful to make sure mouse interactions work in an identical way across multiple terminals. Here’s where I configured word chars in my tmux config.
  • Start tmux without your standard configtmux -L test -f /dev/null
  • In VS Code, cmd+k clears the terminal via workbench.action.terminal.clear. This sends escape sequences to the terminal and does not use the clear or reset commands in zsh, so there’s no way to use an overload or a hook to be notified about this. This causes weird formatting issues in tmux. You’ll want to setup an alternative clear command, here’s what I ended up doing.
  • If you get a server exited unexpectedly error, try wiping tmux tmp rm -rf /tmp/tmux*
  • A helpful tool for inspecting what characters are being received by the terminal is od -tx1. ChatGPT is really helpful for decoding escape sequences.
  • Arrow keys are weird. I think it has something to do with default keybindings in xterm and iterm, but M-Right (i.e. opt+right arrow key) is translated to M-f. M-Left and is translated to M-b.
  • Use tmux -vvv to create a debug log as a separate file in your current directory.
  • xterm-keys is on by default in recent versions of tmux. This was confusing when reading older versions of blog posts.
  • There are many command aliases in the tmux config language. bind and bind-key, if and if-shell. It’s confusing. I’m always a fan of developing any sort of DSL with a single happy path, even if it’s more verbose.
  • By default, your colors will be messed up. Here’s what fixed it for me.
  • There is no else in a if-shell statement, which was confusing.
  • In if-shell there are special "format string variables" which can be used as conditionals. This is really powerful for extracting terminal state and changing keybinding logic based on that state. For instance, starting a selection if one doesn’t already exist. These are all documented in the manpages under "FORMATS".
  • The manpage for tmux contains a ton of great stuff that can’t be found elsewhere. It’s worth grepping the manpages for information.

Plugins

There’s a plugin that’s helpful for extending tmux functionality and learning it. The plugin ecosystem isn’t very big, but still big enough to be really useful.

  • All plugins are located in ~/.tmux/plugins but it seems like TPM is the only system using ~/.tmux
  • Remove all plugins except tpm: cd ~/.tmux/plugins && fd -t d -d 1 -E tpm -X rm -rf
  • tmuf-fzf is helpful for quickly viewing all available commands and keybindings, which is really helpful for learning tmux.
  • tmux-yank has a lot of helpful shortcuts. Finding the default bindings is not easy, here they are.
  • Here’s the plugins I ended up using

Copy Mode

Quick guide to using copy mode:

Mouse Interaction Changes

Mouse interaction by default is strange. Here are some notes on how to make it better.

Here’s what I wanted in my mouse configuration:

  • Don’t remove selection when click and dragging
  • Don’t remove selection when double or triple clicking
  • Don’t automatically copy selection when mouse is up

To do this:

VS Code Integration

I like the VS Code pane management, so I wanted a new named session for each tab or new window, but if I could save history I’d like to do that.

Here’s what I did:

  "terminal.integrated.defaultProfile.osx": "tmux",
  "terminal.integrated.profiles.osx": {
    "tmux": {
        "path": "/Users/mike/.tmux-shell.sh",
        "icon": "terminal-tmux",
    },
  },

And here’s the .tmux-shell.sh

#!/usr/bin/env zsh

session_name=${PWD:t}
counter=0

while [[ $counter -lt 10 ]]; do
  if [[ $counter -gt 0 ]]; then
    session="${session_name} $counter"
  else
    session="${session_name}"
  fi

  # if the session doesn't exist, create it
  if ! tmux has-session -t "$session" 2>/dev/null; then
    tmux new -ADs "$session"
    break
  fi

  # if the session exists but isn't attached, attach it
  if [ -z "$(tmux list-sessions -F "#{session_name} #{session_attached}" | grep "^$session 1")" ]; then
    tmux attach -t "$session"
    break
  fi

  counter=$((counter + 1))
done

if [[ $counter -ge 10 ]]; then
  echo "Could not find an unattached session after 10 attempts."
fi

iTerm Integration

The iTerm documentation recommends you use tmux -CC to connect iTerm to a tmux session. What this does is disables of the tmux prefix keybindings and enables you to use iTerm keyboard shortcuts instead.

This is cool, but doesn’t work for me since I use VS Code terminal as well. I found just using the standard tmux command to open up a new session worked better for me.

Open Questions

  • I could see how it would be useful to understand how to switch sessions (especially if you want a long-running process in a hidden terminal window). I’d like to understand how this works.
  • One of the interesting features of tmux is the ability to share a terminal session with others. I’d like to understand how to do this, although most of my sharing is done via Zoom or VS Code Live Sharing.
  • Some strange key rebindings are going on in iTerm, specifically with C-M-* keys. This could be bad config from long ago that I need to wipe out.
  • I have some good regexes in place for url, file, etc identification. As I start to get used to tmux more, I’d love to create more complex shortcuts to find text, mutate it, and copy to clipboard. This could be a huge timesaver.
  • It looks like shell integration could be broken with iTerm. Need to investigate this further and tune my iTerm config.
  • Some shortcuts to start selection and copy to end of line, or end of command (if it’s possible to detect escape sequences identifying end of command) would be great. Should be possible to do this by building on tmux-yank.

Resources

Getting Started

Here are some helpful intro resources I found to understanding how tmux works:

Example Configurations

Some fancy configurations I found.

I also found it useful to do a GitHub code search with path:.tmux to locate examples containing keywords or commands I was trying to understand.