It finally happen, things got out hand. It got to a point where I need variables in my Default.sublime-keymap
file. And just thought wouldn't it be nice if I could write this in python?
I made a thing
VonHeikemen / sublime-pro-key-bindings
Declare your keybinding in sublime text using Python
Sublime Text Programmatic Key Bindings
Use the full power of python to declare your key bindings.
Just imaging writing your bindings with this api:
prefix_origami = "ctrl+w"
def keybinding(bind, **kwargs):
command = kwargs.get('command')
# Move between selections in overlay
bind(["alt+k"], "move", overlay_visible, by="lines", forward=False)
bind(["alt+j"], "move", overlay_visible, by="lines", forward=True)
# Safe quit
bind(["ctrl+q"], [command("close_workspace"), command("exit")])
# Plugin: Origami
bind([prefix_origami], "noop") # Disable the default behavior of ctrl+w
bind([prefix_origami, "q"], "close")
bind([prefix_origami, "c"], "destroy_pane", direction="self")
# Contexts
overlay_visible = {
"key": "overlay_visible",
"operator": "equal",
"operand": True
}
A plugin. A very funny plugin that allows me to write code like this:
prefix_origami = "ctrl+w"
def keybinding(bind, **kwargs):
command = kwargs.get('command')
# Move between selections in overlay
bind(["alt+k"], "move", overlay_visible, by="lines", forward=False)
bind(["alt+j"], "move", overlay_visible, by="lines", forward=True)
# Safe quit
bind(["ctrl+q"], [command("close_workspace"), command("exit")])
# Plugin: Origami
bind([prefix_origami], "noop") # Disable the default behavior of ctrl+w
bind([prefix_origami, "q"], "close")
bind([prefix_origami, "c"], "destroy_pane", direction="self")
# Contexts
overlay_visible = {
"key": "overlay_visible",
"operator": "equal",
"operand": True
}
How does it work?
TL;DR it adds a command that looks for a python file in a specific part of your file system, reads it, runs the function keybinding
and creates a [something].sublime-keymap
file.
The "real" magic is in the bind
helper function. That's the one that collects the data of your keybindings and gives it to the function that creates the keymap file.
bind
has an interesting use. It only needs an array of keys and a command name. So you could use it for simple bindings like this.
bind(["alt+q"], "exit")
But it becomes more useful when context comes into play. The first two positional arguments are require, but the rest of them are considered "context objects" so it allow this type of use.
bind(["alt+k"], "move", overlay_visible, overlay_has_focus)
What is overlay_visible
and overlay_has_focus
? Is that magic to? No... those are just variables. This is where they shine, you can have them in scope, give them descriptive names and reuse them as much as you can. It's a python file, you can do whatever you want.
Let's go back to bind
for a moment. It has more tricks. Some commands need arguments, and all of them need names. Enter python's keyword arguments.
bind(["alt+k"], "move", overlay_visible, by="lines", forward=False)
bind(["alt+j"], "move", overlay_visible, by="lines", forward=True)
In here by
and forward
become the arguments for the move
command. It just brings me joy.
The last trick bind
has it's a good one, it's a gift from me to me. Technically we can't bind multiple commands to a single combination of keys, but there is a workaround for that. Nothing can stop me from creating a command that calls other commands. But of course the signature for such command can be quite awkward so I created another helper called command
. You can access this helper from the kwargs
of the keybinding
function.
def keybinding(bind, **kwargs):
command = kwargs.get('command')
# Your code....
It works in a similar fashion as bind
. You give it a command name and some keyword arguments.
command("insert", characters="hello")
This will create an object that has all the data bind
needs. The idea is to enable things like this.
bind(["alt+q"], [command("close_workspace"), command("exit")])
So nice.
Usage
Let's talk usage. How do you even invoke this thing? You open the command palette and look for this.
Sublime Programatic Key Bindings - Compile Default
Yeah... I need a better name. Sublime can do a fuzzy search, don't worry about it now.
What you do need to worry about is where you need to create that wonderful keybinding
function. I'll you show the command.
[
{
"caption": "Sublime Programatic Key Bindings - Compile Default",
"command": "spk_key_binding",
"args": {
"bindings": "$packages/User/SublimeProKeyBindings/keybindings.py",
"destination": "$packages/User/SublimeProKeyBindings/Default ($platform).sublime-keymap"
}
}
]
So you need to go to your "user folder" and create a folder called SublimeProKeyBindings
and in there create keybindings.py
.
If you are not a fan of those paths I picked, no problem, you can always create another command. In your "user folder" create a Default.sublime-commands
file, then put your command.
[
{
"caption": "My Awesome Key Bindings",
"command": "spk_key_binding",
"args": {
"bindings": "$packages/User/keybindings.py",
"destination": "$packages/User/Default ($platform).sublime-keymap"
}
}
]
You need to have a .sublime-keymap
in a place where sublime text can recognize it, so you don't have many choices. Basically your user folder or the folder of another package. In this example I'm putting all the files in the root of the user folder. For keybinding.py
there will be no problem, but Default ($platform).sublime-keymap
will overwrite the one you already have (be careful if you copy/paste).
What about installation?
It's available on Package Control, just search for Programmatic Key Bindings.
Conclusion
We can have nice things in sublime text to.
Thank you for your time. If you find this article useful and want to support my efforts, consider leaving a tip in ko-fi.com/vonheikemen.
Top comments (2)
I got this plugin NvMode - (Not VIM Mode), which I use to enable basic modal editing features in sublime. And then I use
sublime-pro-key-bindings
to manage my own keymaps for that plugin: keybindings.py (if you notice any weird looking command they come from here).Now the plugin is live in Package Control!