How I manage my terminals

, .

Inspired by Diomedis Spinellis’ not so recent blog post, I wanted to share how I (currently) manage my terminals. The key component is tmux, plus a useful little function of my own devising which you might find useful as well. (Source code is at the bottom.)

My first boost in terminal use effectiveness came when I started to use tmux (terminal multiplexer) for all my local terminals, with no exceptions: previously I’d sometimes fire up tmux when I thought that I was embarking on some task where I would want a terminal multiplexer, but more often than not I found myself knee deep into a task already by the time I’d realize that it would be great to be in a tmux session right now – so one day, I configured GNOME Terminal to use tmux as command instead of running my shell directly, and I haven’t looked back since. Soon after, I stopped using GNOME Terminal’s native tabs in favor of tmux windows.

The second, much more significant boost came from the realization that the default directory for new windows and panes in a tmux session isn’t necessarily your home directory – it’s a property of each session, by default the working directory of the initial process creating the session (for new sessions launched by GNOME Terminal, $HOME) but configurable with tmux new-session -c DIRECTORY. And if I’m working in some Git repository, setting the start directory of my tmux session to that repository makes creating new windows and panes significantly less painful, since they immediately start out in the right directory.

To facilitate this, I created a shell function which could launch a new tmux session in a certain directory in a new terminal window, to be used like this:

lterm formatter ~/git/ceylon/ceylon.formatter

And I quickly realized that I usually needed the same directories fairly often, so I added the capability to read the location from symlinks in ~/.config/lterm/ (respecting XDG variables and falling back to /etc and all that, of course):

lterm formatter
lterm ceylon
lterm systemd
lterm mw # MediaWiki
lterm wbqc # Wikibase Quality constraints

With this setup, it’s extremely painless to just launch a quick auxiliary shell for a command when your main shell is currently occupied by a less, emacs, etc., since any new window in the session will start out in the right directory automatically.

Eventually, however, I started using so many terminal windows that I felt myself almost drowning in them, and just navigating to the right one could be a nuisance. GNOME’s Alt+Key above Tab (e. g. Alt+` on US keyboards or Alt+^ on German keyboards) shortcut helped with this: it cycles between all the windows of the same application (while Alt+Tab cycles between different applications), so I could use that shortcut to switch between the most recent terminals, no matter how many other windows (Firefox, IDE, etc.) were also opened in between. However, I eventually added another feature to lterm to solve this problem properly:

If a certain tmux session already existed, lterm would fail to create it – but what if instead it switched to that window? Fortunately, my tmux was already set up to set the GNOME Terminal window title according to the tmux session name inside it (e. g. systemd:1:bash - "hostname" – session name, window number, window name (usually process), and hostname), so I could just activate the window with a title matching the expected session name from lterm and be set. I implemented that using xdotool (and then later had to reimplement it using GNOME Shell’s dbus / JavaScript bindings due to xdotool not working in Wayland sessions), and now I could bring up any terminal using lterm foo, regardless of whether that terminal had already been running or not.

2022-04-23 edit: The reimplementation based on org.gnome.Shell.Eval eventually broke, so I created a custom GNOME Shell extension, Activate Window By Title (source code), to offer a custom D-Bus interface for just this purpose.

As a minor bonus, lterm exits if it’s the first command in an interactive shell – so if you start a new terminal window using Ctrl+Alt+T only to launch a “real” terminal with lterm, then the temporary terminal window will quietly close itself behind your back automatically, since you don’t need it anymore. Another bonus: lterm has automatic completion for all pre-configured locations.

If you want this functionality for yourself, feel free to copy the source code from my GitHub home repository: the lterm function itself and the underlying term function it uses (which you might want to tweak if you prefer a different terminal emulator, like Konsole ☺). There is also a similar rterm function for remote terminals. Place those functions somewhere in your shell initialization files, and make sure the set-titles option is enabled in your tmux.conf, and everything should work! (2022-04-23 edit: you’ll also want to copy activate-window, and install Activate Window By Title.)