tmux Window Juggling

I keep finding more and more depth to tmux. It really is a remarkable piece of software. In this article I will show you how to turn a tmux Window into a Pane and vice-versa.

Why would you want to move Panes and Windows around in a tmux session? I'll explain my scenario, and maybe that will give you some ideas of your own.

Debugging Sessions

I create tmux scripts for all of my projects. This allows me to simply run the script to have the development environment for the given project up and running in a tmux session.

For a Rails project I generally have a tmux window named server that is running the Rails server. I make it the last window in the list because I don't need to look at it too often.

However, I also use Byebug for debugging Ruby. If you haven't used Byebug or a similar debugger before the way it works is you add a byebug statement in your code where you want a breakpoint. When you run the code the byebug statement will halt the process and transfer control to the debugger.

In a Rails project the server is running the code, so when Byebug hits a breakpoint the debugger stops in the server window. Then you can interact with the debugger as you normally would, inspecting variables, stepping through code, etc.

Now you can perhaps see where this is going. When debugging Rails it would be very handy to have the server window in a split pane next to my editor. That way I can see the relevant code and the debugger in the same tmux window.

Window Becomes Pane

The first tmux command we need is join-pane. The join-pane command gives you the ability to attach a window or pane somewhere else in tmux to your current window. The join-pane command has the following form.

join-pane [-bdhv] [-l size | -p percent] [-s src-pane] [-t dest-pane]

We wont need all of those options thankfully. The -s option is used to specify the source pane or window to join into the current window.

A quick refresher for how to reference things in tmux may be in order.

[session]:[window].[pane]

For example, if your current tmux session is named giggles then a reference to the third pane in the second window would be giggles:2.3. You can omit the session name if you are currently in the session with the window you want to reference. And yes, that means you can use join-pane to move a window or pane from a different tmux session to your current session.

For my case I want to move a whole window, so I can just use -s N, where N is the window number I'm interested in. You can also move a window by its name, so -s server would work just as well.

The -l and -p arguments can be used to size the new pane appropriately. I'm fine with the default 50/50 split so there is no need to use either.

The -t argument can be used to move the source pane to someplace other than the current window. Also not an option I will need. The -b option works with -t to specify that the new pane should be placed to the left or above the target pane.

The joined pane will be active when you complete the join-pane command. If you don't want it to be you can use the -d option.

The -h and -v arguments make new pane a horizontal or vertical pane respectively.

Now I can put together the first command I need. I want to move my server window into my editor window for this debugging session. First I enter command mode with <Prefix>:. Now the join-pane command:

join-pane -h -s server

And there it is, my server window next to my editor window. One weird thing about tmux is how it deals with horizontal and vertical splits. The -h argument specified above produces what I would call a vertical split. There is a pane on the left and a pane on the right. The -v argument does the opposite, putting one pane on top and the other on the bottom.

Pane Becomes Window

To reverse the join-pane operation and make a pane a window use the break-pane command. The break-pane command has the following format.

break-pane [-dP] [-F format] [-n window-name] [-s src-pane] [-t dest-window]

Again, we won't need all of these options. Just like with the join-pane command, if you specify the -d option the window will not become active after the move. The -P argument tells tmux to print information about the new window, and the -F argument provides the ability to format that information.

If you don't specify a source pane with -s then the currently active pane will be broken out to its own window. Otherwise you can specify the pane to move the same way you did for the join-pane.

Panes are not named like windows are, so when you move a pane to its own window tmux will use the default name for the window, which is the current command. If you would like to name the window use the -n argument and the window name.

The other option, if you have an existing window that you would like to move the pane to you can use the -t argument with the reference to the desired window.

For my case when I'm done with the debugging session I just want to move the pane back to its own window named server, and I don't need it to be active. I'd rather the editor window remain active. I can achieve this with the following command (remembering to activate command mode first with <Prefix>:).

break-pane -d -n server

That tells tmux to move the currently active pane to a new window at the end of the window list, do not make it active, and name it server.

Create Key Bindings

The join-pane and break-pane commands aren't too hard to use in an ad-hoc manner to move panes around as necessary. It is a little bit of typing, but if the window to move is not named, or you need to move different windows around you are stuck having to use the commands themselves.

In my case I know the server window will always be available because it is in the tmux script that sets up the environment. Since I know the window name will not change I can create key bindings for those commands. As an added benefit, these key bindings are only available for this session because they are not in the main .tmux.conf file.

tmux bind-key u join-pane -h -s server
tmux bind-key U break-pane -d -n server

Now, whenever I want to see the server window next to my code I can just type <Prefix>u and to return it to its own window I can type <Prefix>U.