To improve my iTerm+tmux experience, I’ve set up a whole bunch of key
mappings. Rather than defining these manually, I wrote a Python
script
to generate the corresponding JSON programmatically.
Fixing prefixing
I’ve recently refound my love for
tmux. Having multiple terminal
panes, windows, and even sessions helps me keep my command-line
shenanigans organized. By default, you interact with tmux by pressing a
prefix key (Ctrl-B) followed by another key. For instance,
Ctrl-BC creates a new window and
Ctrl-B" splits the current pane into two. That’s
incredibly powerful, but I don’t particularly enjoy pressing prefixes
all the time.
Fortunately, iTerm, which is the terminal emulator that I use on macOS,
has the ability to define key mappings. This allows you to simulate,
say, Ctrl-BJ by pressing Cmd-J.[1]
Defining these key mappings manually, however, turns out to be a tedious
and error-prone task.
Using Python to generate key mappings
Like any real developer, I wrote a script to automate this task.
Here’s a screenshot showing a portion of the 116(!) key mappings the
script generates:
You can find the complete Python script below or you can download it
from
GitHub.
It generates three types of key mappings:
If you press Cmd-key, iTerm sends Ctrl-Bkey, for A to Z and then some,
except X, C, V, and Q to
keep the default cut, copy, paste, and quit keyboard shortcuts.
If you press Cmd-Ctrl-key, iTerm sends
Ctrl-BShift-key for A to
Z and then some.[2]
If you press Option-key, iTerm sends Ctrl-BCtrl-key, for A to Z.
The output of the script is JSON, which looks[3] like this:
This JSON structure is discussed in more detail below.
I haven’t yet found a way to import these keymappings programmatically,
so you’ll first have to save the output to a file:
$./itermkeymap.py > generated.itermkeymap
And then import that file by clicking “Presets…” and “Import…” at
the bottom of the Key Mappings section of the iTerm preferences.
Key mappings are personal
You’re of course welcome to use this script as it is, but I can imagine
you’ll want to adapt it to your personal preferences. For instance, if
you use Ctrl-A as your tmux prefix, you’d need to change “b”
and “02” to “a” and “01” in the following two lines:
prefix_key ="b"# My prefix key in tmux is Ctrl-B prefix_hex ="02"# The hex code that iTerm sends (corresponds to Ctrl-B)
Or perhaps you’d like to change which keys and modifiers are used:
I couldn’t find any documentation about the JSON structure, but here’s
what I’ve learned so far:
“0x6a-0x100000-0x0”: Refers to the key combination you press to
trigger the key mapping, consisting of three parts:
“0x6a”: is the hex code for “j”. See man ascii for the complete
ASCII table in hex.
“0x100000”: refers to the Cmd key.
“0x0”: I’m not sure what this does. It can be safely left out when
importing a JSON file.
“Version”: Is always 1; not interesting.
“Action”: “11” stands for “Send Hex Codes”.
“Text”: The hex codes being sent. In this case Ctrl-B
followed by J.
“Label”: Doesn’t seem to be used by iTerm. The script puts the key
mapping in plain text here for debugging purposes.
Defining the corresponding tmux key bindings
All these key mappings only make sense when you define the corresponding
key bindings in tmux[4]. Here are tree key bindings to illustrate the
three types of mappings for the H key:
bind h show-message "Received Ctrl-B H"# If you press Cmd-H bind H show-message "Received Ctrl-B Shift-H"# If you press Cmd-Ctrl-H bind ^h show-message "Received Ctrl-B Ctrl-H"# If you press Option-H
My own tmux configuration defines, among many others, the following key
bindings:
bind h select-pane -L# Select pane to the left bind j select-pane -D# Select pane below bind k select-pane -U# Select pane above bind l select-pane -R# Select pane to the right bind t new-window -c"#{pane_current_path}"# New window bind w kill-pane # Close current pane
These key bindings enable me to navigate to other panes using
Cmd-H, Cmd-J, Cmd-K, and
Cmd-L (aka vim style). Moreover, common keyboard shortcuts
such as Cmd-T and Cmd-W are now intercepted; they
no longer create and close iTerm tabs, but create a new tmux window and
close the current tmux pane, respectively. After all, who needs tabs
when you’re running tmux?
That’s all for now. May your sessions live long and prosper.
– Jeroen
Appendix: The complete script
Below is the script I use to generate the JSON containing the key
mappings. Obviously this script can be improved in many ways, but it
gets the job done. You can also download the script from
GitHub.
#!/usr/bin/env python3
import json import string
prefix_key ="b"# My prefix key in tmux is Ctrl-B prefix_hex ="02"# The hex code that iTerm sends (corresponds to Ctrl-B)
mappings[f"0x{press_hex}-0x{press_mod}"]={ "Version":1, "Action":11,# Corresponds to "Send Hex Codes" action "Text":f"0x{prefix_hex} 0x{send_hex}",# Hex codes to send "Label":f"C-{prefix_key}{send_key}"# Nice for debugging }
# Option+<KEY> becomes Prefix Ctrl+<KEY> # Generate Option+A through Option+Z for i, send_key inenumerate(string.ascii_lowercase, start=1): send_hex =hex(i)# returns 0x01 for a, 0x02 for b, etc.
mappings[f"0x{press_hex}-0x{press_mod}"]={ "Version":1, "Action":11,# Corresponds to "Send Hex Codes" action "Text":f"0x{prefix_hex}{send_hex}",# Hex codes to send "Label":f"C-{prefix_key} C-{send_key}"# Nice for debugging }
result ={"Key Mappings": mappings} print(json.dumps(result, indent=4))
Although you cannot bind to the Cmd key in tmux, you
can still use it in an iTerm key mapping to simulate pressing other
key combinations. ↩︎
For some reason, I’m having trouble mapping Cmd-Shift
so I’m currently using Cmd-Ctrl. ↩︎
In case you were wondering about trim, it can be found in my
dsutils repository. ↩︎
The tmux configuration is usually located at ~/.tmux.conf or
~/.config/tmux/tmux.conf. ↩︎
Would you like to receive an email whenever I have a new blog post, organize an event, or have an important announcement to make? Sign up to my newsletter: