Skip to content

Mastering Tmux

Tags: coding, learning, software • Categories: Software, Uncategorized

Table of Contents

Tmux has been one of the best things I’ve added to my developer toolkit. After my initial dive into tmux, I’ve slowly learned more about the system and made some additional improvements to my workflow.

Some notes from my tmux learnings over the last couple years:

  • It’s been great that tmux continues to get consistent updates. Mad props to the developer who has maintained this tool for quite a long time without too much funding.
  • In recent versions of tmux, you don’t need reattach-to-user-namespace but you do need to remap default keybindings to use pbcopy. From my research, it wasn’t clear that you still needed to modify your config to use pbcopy, but you definitely do.
  • tmux-copycat modifies all copy-mode keybindings which cancel, this is why your keybindings might look crazy mutated
  • tmux show-option -g | fzf is great for inspecting options that are set in the tmux session
  • Here’s all of the default keybindings tmux provides. Some of these are more complex than you’d think
  • tmux list-keys | fzf is great for listing out keybindings
  • I ran into '~/.tmux/plugins/tpm/tpm' returned 1 and it was causing strange issues. I had to nuke everything to fix the issue: rm -rf ~/.tmux/plugins && git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm && tmux kill-server
  • Another time, the returned 1 error reappeared. The core issue was launching tmux directly outside of a terminal session. This caused PATH to not be modified by shell configuration files to include homebrew. Setting the path explicitly in tmux config fixed this.
  • I found that tmux-copycat was really slow compared to native search and I opted for writing my own searching shortcuts.
  • You can specify a list of environment variables to be copied from the originating shell (if you are launching tmux from another shell) using update-environment. It seems like set-environment does not update the shell launched from tmux, but update-environment did the trick for me when attempting to propogage VSCODE* variables to tmux.
  • Tmux variables that are an array can be specified with a space-separated string. Example: set -g update-environment "VSCODE_GIT_ASKPASS_EXTRA_ARGS VSCODE_GIT_ASKPASS_MAIN VSCODE_GIT_IPC_HANDLE VSCODE_INJECTION"
  • copy-pipe-and-cancel and copy-selection-and-cancel are equivilent if there is only one param (pbcopy) passed. The pipe variant is useful when running a shell script to transform the selection before passing it to the clipboard tool.
  • Tmux differentiates between "physical" and "virtual" lines. Physical lines: Actual lines in the terminal window. Virtual lines: Lines in the tmux buffer, which can exceed visible area
  • You can create custom commands using a tmux config. Configuration can only be bound to keyboard bindings, not an arbitrary command string. You cannot create commands and then remix them.
  • This isn’t a tmux thing, but there is no casing difference when a keyboard shortcut is using the control modifier. In other words, C-D and C-d are equivilent.
  • tmux-sidebar is a cool concept, but seems messier than a simple ls or tree when you want to get a file listing.
  • tmux-jump is great, but the same functionality can basically be achieved with custom livesearch and tmux-fastcopy
  • You can use variables within tmux configuration

Upgrading Tmux & Plugins

Here’s how to upgrade tmux + TPM plugins when a new tmux version comes out:

brew upgrade tmux

# to upgrade plugins, within a tmux session tmux: prefix-U

# this will destroy all of your sessions so the latest version of tmux can be launched
tmux kill-server

Now open up a new terminal, prefix+: and then run display-message -p '#{version}' to make sure you are using the latest version.

Unbound Copy-mode Commands

Here’s a list of all copy-mode commands that don’t have any keybindings by default. This was helpful for me to understand what commands are worth investigating.

copy-end-of-line                             
copy-end-of-line-and-cancel                  
copy-pipe-end-of-line                        
copy-line                                    
copy-line-and-cancel                         
copy-pipe-line                               
copy-pipe-line-and-cancel                    
copy-pipe                                    
copy-pipe-no-clear                           
copy-pipe-and-cancel                         
copy-selection                               
copy-selection-no-clear                      

Modifier Keys & Escape Sequences

Understanding modifier keys and how they are described with escape sequences is helpful to not being completely confused when mutating keybindings.

  • Nice guide to tmux modifier keys here
  • ^ is control
  • ^[ is opt
  • cmd does not have an escape sequence in the terminal
  • arrow keys are mapped to specific keys based on your terminal configuration. You’ll need to marry up how tmux + your terminal thinks about arrow keys to make your life easier.
    • od -tx1, cat, and read are all ways to get the raw key bindings that are being sent to the terminal. This is helpful for debugging
    • ^[[ part is actually a single escape character, often represented as \e or \033. Try opening up alacritty , running cat, and pressing the arrow keys to see this in action.
    • Escape codes are often different across terminal emulators. ^[b^[f for meta-left arrow and meta-right arrow and others use ;3D;3C (Extended Keyboard Codes)

Keyboard Copy Mode Navigation

Here’s a cheat sheet to navigating tmux when in copy mode.

Managing Selection

  • Space to enable selection
  • V to enable vertical selection
  • V select line
  • Esc clear selection

Navigating Text

  • {, } next and prev paragraph
  • M jump to the middle of the buffer
  • b jump to prompt start
  • H is top, L is bottom (of buffer)
  • g is history top, G is history bottom
  • u is page up, d is page down
  • U is page up 3x, D is page down 3x (this is custom)

Copying Text

Copy Last Terminal Command Output

One thing I’ve always wanted is an easy way to select + copy the output of the last command. It’s possible, but requires some work:

But with these two things in place opt+shift+up arrow magically selects the last output of my terminal command.

One of the downsides with this is I need to manually keep my fork of the pure repo up to date since I cannot heavily customize the prompt of pure using ENV vars.

This is a bummer, but it’s a quick script and tmux plugins don’t change much. The whole ecosystem is remarkably stable and feels very much like "finished software".

cd /Users/mike/Projects/zsh/pure
g pull --rebase upstream HEAD
g push --force origin HEAD

Copying Text Outside of Copy Mode

As an aside, here’s a great list of macOS emacs-style bindings. Better keyboard-based text navigation on macOS!

Fixing iTerm CSI U (extended mode)


Here’s the iTerm config that worked for me:

  • Use command /opt/homebrew/bin/tmux
  • CSI U enabled
  • Esc+ for left + right option key

And here’s the key mapping that was needed specifically for extended key mode in iTerm. The interesting bit here is M-Right and M-Left is not reported by these keys in non-CSI U mode (i.e. vs code integrated terminal / xterm.js). Instead left/right is reported as ESC-b and ESC-f, and I have no idea why this is—maybe because right/left arrow was not part of the original keybindings?

  • I’m not sure why, but only in iterm (and I have tmux -CC disabled) the M-Left & M-Right arrow keys report \e[1;3C and \e[1;3D. Tmux seems to consider these M-Right and M-Left. This fixes M-{Arrow} word navigation for me. Weirdly enough, iTerm only does this in a tmux session: not in zsh, bash, etc (although these are not consistent either).
  • To get iTerm working properly with tmux I used Esc+ as the option key modifier and "Report modifiers using CSI u". Without CSI U I had weird keybindings errors.

Copying VS Code Environment Variables to Tmux

In my last post, I created a script to open tmux in vscode and nicely name & restore the sessions. I found there were a handful for VSCODE_* variables injected into the terminal that were not copied over. This can effect the terminal integration and other ways the terminal operates, so I wanted to update my script to get them copied over.

Here’s the modification

Conclusion

Although it can be nuanced, tmux is a great tool for your virtual toolbox. While there’s always more to learn, it has consistently been reliable and versatile. If you’re not using tmux yet, I think it is definitely worth diving into—it will help optimize your terminal usage.

Keep in Touch

Subscribe to my email list to keep in touch. I’ll send you new blog posts and other thoughts.