What if you could declare your key bindings in sublime text using Python?
Tags: python, sublimetext
Published: 2021-06-15 22:57:28

---
title: What if you could declare your key bindings in sublime text using Python?
published: true
description: Seriously, let's just use python to bind our keys
tags: #python #sublimetext
//cover_image: https://direct_url_to_image.jpg
---

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

{% github VonHeikemen/sublime-pro-key-bindings %}

A plugin. A very funny plugin that allows me to write code like this:

```python
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.

```python
bind(["alt+q"], "exit")
``` 

But it becomes more useful when [context](https://www.sublimetext.com/docs/key_bindings.html#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.

```python
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.

```python
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.

```python
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.

```python
command("insert", characters="hello")
```

This will create an object that has all the data `bind` needs. The idea is to enable things like this.

```python
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.

```json
[
  {
    "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.

```json
[
  {
    "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](https://packagecontrol.io/), just search for [Programmatic Key Bindings](https://packagecontrol.io/packages/Programmatic%20Key%20Bindings).

## Conclusion

We can have nice things in sublime text to.