I am getting quite comfortable with Ubuntu these days. One thing I can’t seem to shake, though, is keyboard shortcuts to select text. I’ve been using macOS for more than 20 years (😱) and two particular shortcuts about selecting text are stuck in my muscle memory. But since I spent most of my time coding and writing, this has been bugging me for quite some time. So today I learned about keybindings in Ubuntu (Linux).
The goal was simple:
| Shortcut | Action |
|---|---|
| Shift + Alt + Left/Right | Select to line start/end |
| Shift + Super + Left/Right | Select word |
Why This Is More Complicated Than Expected
On macOS, this would be a single system-wide setting. That’s because macOS has a unified text system called NSTextInputClient that virtually all apps…
I am getting quite comfortable with Ubuntu these days. One thing I can’t seem to shake, though, is keyboard shortcuts to select text. I’ve been using macOS for more than 20 years (😱) and two particular shortcuts about selecting text are stuck in my muscle memory. But since I spent most of my time coding and writing, this has been bugging me for quite some time. So today I learned about keybindings in Ubuntu (Linux).
The goal was simple:
| Shortcut | Action |
|---|---|
| Shift + Alt + Left/Right | Select to line start/end |
| Shift + Super + Left/Right | Select word |
Why This Is More Complicated Than Expected
On macOS, this would be a single system-wide setting. That’s because macOS has a unified text system called NSTextInputClient that virtually all apps use – even Electron apps like Obsidian (more on that later). When you press a key combo, it goes through this single system layer before reaching the app. Apple defines the keybindings once, and everything inherits them. Very convenient!
Linux doesn’t have that. Instead, each toolkit handles text input independently:
| App type | Text handling |
|---|---|
| GTK apps (Thunderbird, GNOME apps) | GTK’s text input system |
| Qt apps (KDE apps, VLC) | Qt’s text input system |
| Electron apps (Obsidian, VS Code, Slack) | Chromium’s text input (each app can override) |
Each of these is a completely separate implementation with its own keybinding configuration. There’s no shared layer that intercepts keyboard input for text editing. (At least as far as I could find out. Remember I am still pretty new at this.)
So I had to configure this in multiple places.
1: Free Up the Shortcuts
First problem: Ubuntu already uses these key combinations for window management. Shift + Super + Arrow moves windows between monitors, and Super + Arrow tiles windows. (Super btw., is the Option key on my shared Keychron K3, usually it is the “Windows key” or the “Command key” on regular Mac keyboards.)
I use the built-in Tiling Assistant, so I had to check what was grabbing my shortcuts:
gsettings list-recursively | grep -i "super" | grep -E "(Left|Right)"
(This gets all GNOME settings, and filters for the shortcuts I am looking for.)
Then I remapped them to different key combinations:
# Move window tiling to Ctrl+Super+Arrow
gsettings set org.gnome.shell.extensions.tiling-assistant tile-left-half "['<Control><Super>Left', '<Super>KP_4']"
gsettings set org.gnome.shell.extensions.tiling-assistant tile-right-half "['<Control><Super>Right', '<Super>KP_6']"
# Move "send to monitor" to Ctrl+Super+Alt+Arrow
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-left "['<Control><Super><Alt>Left']"
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-right "['<Control><Super><Alt>Right']"
These changes take effect immediately, no restart needed. 🎉
2: GTK Applications
For GTK apps like Thunderbird, create ~/.config/gtk-3.0/gtk.css:1
@binding-set CustomTextBindings {
/* Shift+Alt+Arrow: select to line start/end */
bind "<shift><alt>Left" { "move-cursor" (display-line-ends, -1, 1) };
bind "<shift><alt>Right" { "move-cursor" (display-line-ends, 1, 1) };
/* Shift+Super+Arrow: select word */
bind "<shift><super>Left" { "move-cursor" (words, -1, 1) };
bind "<shift><super>Right" { "move-cursor" (words, 1, 1) };
}
* {
-gtk-key-bindings: CustomTextBindings;
}
Copy the same file to ~/.config/gtk-4.0/gtk.css for GTK 4 apps. Restart your GTK applications to apply.
3: VS Code
VS Code manages its own keybindings. Open the keybindings JSON with Ctrl+Shift+P → “Preferences: Open Keyboard Shortcuts (JSON)” and add the following lines:
[
// Shift+Alt+Arrow: Select to line start/end
{
"key": "shift+alt+left",
"command": "cursorHomeSelect"
},
{
"key": "shift+alt+right",
"command": "cursorEndSelect"
},
// Shift+Super+Arrow: Select word
{
"key": "shift+meta+left",
"command": "cursorWordLeftSelect"
},
{
"key": "shift+meta+right",
"command": "cursorWordRightSelect"
}
]
Note: meta is VS Code’s name for the Super/Win key.
4: Other Electron Apps
This is where it gets a bit tedious. Each Electron app (Obsidian, Slack, Discord, etc.) probably needs its own configuration. Some apps respect system GTK settings, some don’t. Some have configurable hotkeys, some don’t. Welcome to Linux. 👋 I am hopeful that I will find a plugin for Obsidian, but I haven’t had any time to look, yet.
5. Reset to Defaults
If you made a mistake or you do want to undo these changes, this is how:
# Tiling Assistant
gsettings reset org.gnome.shell.extensions.tiling-assistant tile-left-half
gsettings reset org.gnome.shell.extensions.tiling-assistant tile-right-half
# Move to monitor
gsettings reset org.gnome.desktop.wm.keybindings move-to-monitor-left
gsettings reset org.gnome.desktop.wm.keybindings move-to-monitor-right
And delete the gtk.css files you created.
Conclusion
Is this more work than it should be? Absolutely. The lack of a unified text input layer in Linux means you’re configuring the same thing multiple times. But once it’s set up, it works. And I no longer need to cuss and scream when I want to select a line of text!
- If you’re a web developer, you might find it odd that keyboard shortcuts live in a `.css` file. I learned that GTK 3 introduced a theming system that borrows CSS syntax for styling widgets, and since they already had the parser, they reused it for keybindings too. Linux! 🤷♂️ ↩