Learning tmux
Tags: learning, tmux • Categories: Productivity
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 %
, pressctrl+b
(to activate the prefix), then presscntrl-%
. This example splits panes.M-Down
, pressctrl+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 toless
for consistency./
to searchu
for page upd
for page downn
for next matchN
for previous matchh
for helpshift+g
for EOLg
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 config
tmux -L test -f /dev/null
- In VS Code,
cmd+k
clears the terminal viaworkbench.action.terminal.clear
. This sends escape sequences to the terminal and does not use theclear
orreset
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 tmprm -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
andbind-key
,if
andif-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 aif-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:
- Keyboard navigation
- Space to enable selection
- V for vertical selection
- D copy to end of line
- Esc clear selection
- {, } next and prev paragraph
- H is top, L is bottom
- Enter copy and clear
- V select line
- With tmux-yank, y to copy text and Shift+Y to copy and paste text
- Marks enable you to set a location to return to
- tmux-yank required this bracketed paste tweak
Here are some notes on how to make it better.
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:
- Enable mouse mode
- Disable yank with mouse (this introduces weird keybindings which didn’t work for me)
- Enable some custom mouse settings via the
nhdaly/tmux-better-mouse-mode
plugin - Some customized mouse bindings I had to write
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:
- https://www.hamvocke.com/blog/a-quick-and-easy-guide-to-tmux/
- https://pythonzeal.wordpress.com/2019/11/12/a-gentle-introduction-on-tmux/
- Good video intro to tmux
- tmux cheatsheet
- https://tmuxcheatsheet.com
- https://danielmiessler.com/study/tmux/
- https://www.thegeekstuff.com/2010/02/unix-less-command-10-tips-for-effective-navigation/
- https://www.seanh.cc/2020/12/28/binding-keys-in-tmux/
- https://minimul.com/increased-developer-productivity-with-tmux-part-8.html
- https://waylonwalker.com/tmux-copy-mode/
Example Configurations
Some fancy configurations I found.
- https://github.com/samoshkin/tmux-config#features
- this guy is next level
- complex copycat config
- https://github.com/eraserhd/tmux-copy-mode-kakoune/tree/a1bbfc93a9436a784bf5dd80b4c0cc6935139326
- if-shell with expansion strings
- https://github.com/wincent/wincent/blob/main/aspects/dotfiles/files/.tmux.conf
- https://github.com/mmerickel/dotfiles/blob/master/tmux.conf
- Complex multi-command keybinding example
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.