Skip to content

A concise and expressive extension language for configuring KMonad

License

Notifications You must be signed in to change notification settings

srithon/kmonadx

Repository files navigation

KMonadX

An extension language for kbd: the keyboard programming manager KMonad’s configuration language.

Introduction

KMonad is an incredibly powerful tool, but its configuration can be quite verbose, and the base language lacks several features which could drasticly reduce boilerplate. Layer inheritance and declarative bindings (i -> j) are 2 of those features which could easily be built on top of the language; rather than attempting to modify the core language, potentially adding unnecessary complexity, creating a language that transpiled to kbd would be much simpler, much more maintainable and would allow for a lot more syntactic flexibility. From this idea came kbdx, a TOML-like language which aims to be expressive, readable and concise, and the kmonadx CLI tool for transpiling it to kbd.

Feature Overview

Here’s an overview of the main features of the kbdx language. If the following features interest you, please proceed to the Documentation section where you can find a complete example configuration (with much more detail), as well as a link to my personal configuration for further inspiration.

Declarative bindings

Rather than having to define a layer with every key from the source layer, you only need to define the keys you want to override! The rest will be inherited from the parent layers; more on that later.

[layer]
[[keys]]
a = 'b'
b = 'c'

Layer inheritance

Layers can include keys from other layers!

[layer1]
[[keys]]
a = 'b'

[layer2]
[[keys]]
b = 'c'

[layer3]
parent = { layer1, layer2 }
[[keys]]
# a = 'b'
# b = 'c'

If layer2 defined a key for a, it would override the binding from layer1, since multi-inheritance is sequential.

Access modifiers for aliases

Buttons defined in layers can be private or public; private buttons are only accessible from within the layer, while public buttons can be accessed from outside the layer!

[layer1]
[[private]]
# this button cannot be accessed from outside the layer
private-button = (tap-hold 5 'a' 'b')
[[public]]
public-button = (tap-hold 5 'b' 'c')
[[keys]]
a = private-button
b = public-button

[layer2]
[[keys]]
# this would be a compilation error, since we cannot access `private-button` outside of `layer1`!
# a = layer1:private-button

b = layer1:public-button

You can also define global buttons in the special aliases table.

[aliases]
global-button = (tap-hold-next-release 100 'a' 'lshift')

[layer1]
[[keys]]
a = global-button

Layer-namespaced aliases

You can reuse names in different layers, since they are namespaced to the layer!

[layer1]
[[public]]
some-button = 'a'
[[keys]]
a = some-button

[layer2]
[[public]]
some-button = 'b'
[[keys]]
# because of the context, this binding refers to `layer2:some-button`!
b = some-button

[layer3]
[[keys]]
a = layer1:some-button
b = layer2:some-button

Topographical sorting of aliases

Unlike in KBD, you don’t have to worry about the order of your bindings, since the compiler will automatically sort them in the output based on which aliases require others!

[aliases]
button1 = (tap-hold 100 @button2 @button3)
button2 = (tap-hold 100 @button3 @button4)
button3 = 'a'
button4 = 'b'

Note that we still cannot have circular dependencies (KMonad wouldn’t be able to parse the configuration); if you do you will get a compilation error. Thus, you cannot do anything like this:

[aliases]
button1 = (tap-hold 100 @button2)
button2 = (tap-hold 100 @button1)

Numeric and string constants with interpolation

You can define constants and interpolate them into Lisp buttons!

[layer1]
[[private]]
tap-hold-delay = 50
github = "srithon"
[[public]]
goto-github = (cmd-button "firefox github.com/$github")
tap-hold-button = (tap-hold $tap-hold-delay 'a' 'b')
[[keys]]
g = goto-github
t = tap-hold-button

Source layer derived from bindings

Unlike in KBD, where you must explicitly define a src layer which contains all of the keys on your keyboard, KMonadX will determine the keys that you use in your configuration, and will generate the source layer based on that! Note that this may have some consequences with certain KMonad buttons that react to “any” key events, since they may not work with keys that are not present in the source layer.

If you have any issues that arise from this, consider defining your own source layer like this:

[src-layer]
[[keys]]
a = 'a'
b = 'b'
c = 'c'
...

Note that this layer can have any name; the point is that it tells the compiler that the keys are used. Also, please file an issue so that I know when it’s breaking, and we can figure out the best way to fix it.

Documentation

See the Functional Documentation for an example configuration which contains the functionality KMonadX currently has, meaning that it compiles correctly on HEAD. See Planned Features for features which may be implemented in the future.

To see the transpiled version of the functional documentation, see compiled_functional_tutorial.kbd

For a real-life example of how KMonadX can be used, see my personal configuration, which is a literate config written in the Org format.