Skip to content

Latest commit

 

History

History
1072 lines (833 loc) · 31 KB

wks.5.org

File metadata and controls

1072 lines (833 loc) · 31 KB

WKS(5)

NAME

wks - A which-key source file used by the *wk*​(1) program.

DESCRIPTION

wk’s configuration files use the wks syntax to generate key chords.

GRAMMAR RULES

The following are ideally the formal grammar rules for wks. I have tried to note where behavior differs from the expectation. If anything is not behaving as you expect, please see CONTACT below to reach out.

key_chord          -> ( chord | prefix | chord_array ) ;

chord              -> trigger_key description keyword* command ;

prefix             -> trigger_key description keyword* '{' ( key_chord )+ '}' ;

chord_array        -> ( implicit_array | explicit_array ) ;

implicit_array     -> modifier* '...' description keyword* command ;

explicit_array     -> '[' ( trigger_key | chord_expression )+ ']' description keyword* command ;

chord_expression   -> '(' trigger_key description keyword* command? ')' ;

trigger_key        -> modifier* ( normal_key | special_key ) ;

normal_key         -> ( '\\' [\\\[\]{}#":^+()] | [^\s\[\]{}#":^+()] ) ;

special_key        -> ( 'Left'    | 'Right'   | 'Up'     | 'Down'
                      | 'TAB'     | 'SPC'     | 'RET'    | 'DEL'  | 'ESC'
                      | 'Home'    | 'PgUp'    | 'PgDown' | 'End'  | 'Begin'
                      | 'VolDown' | 'VolMute' | 'VolUp'  | 'Play' | 'Stop'
                      | 'Prev'    | 'Next'    | 'F'[1-35] ) \s+ ;

modifier           -> ( 'C' | 'H' | 'M' | 'S' ) '-' ;

description        -> '"' ( '\\"' | [^"] | interpolation )* '"' ;

command            -> '%' delimiter ( . | interpolation )* delimiter ;

delimiter          -> ( open_delim | close_delim | ([^[{(])\1 ) ;

open_delim         -> ( '{{' | '((' | '[[' ) ;

close_delim        -> ( '}}' | '))' | ']]' ) ;

interpolation      -> '%(' identifier ')' ;

identifier         -> ( 'key'
                      | 'index'
                      | 'index+1'
                      | 'desc'
                      | 'desc^'
                      | 'desc^^'
                      | 'desc,'
                      | 'desc,,' );

keyword            -> ( hook | flag ) ;

hook               -> '^' ( 'before'
                          | 'after'
                          | 'sync-before'
                          | 'sync-after' ) command ;

flag               -> '+' ( 'keep'
                          | 'close'
                          | 'inherit'
                          | 'ignore'
                          | 'ignore-sort'
                          | 'unhook'
                          | 'deflag'
                          | 'no-before'
                          | 'no-after'
                          | 'write'
                          | 'execute'
                          | 'sync-command' ) ;

preprocessor_macro -> ':' ( string_macro
                          | switch_macro
                          | integer_macro
                          | unsigned_macro
                          | number_macro ) ;

string_macro       -> ( 'include'
                      | 'fg'
                      | 'fg-key'
                      | 'fg-delimiter'
                      | 'fg-prefix'
                      | 'fg-chord'
                      | 'bg'
                      | 'bd'
                      | 'shell'
                      | 'font' ) '"' ( '\\"' | [^"] )* '"' ;

switch_macro       -> ( 'debug'
                      | 'sort'
                      | 'top'
                      | 'bottom' );

integer_macro      -> ( 'menu-width'
                      | 'menu-gap' ) '-'? [0-9]+ ;

unsigned_macro     -> ( 'max-columns'
                      | 'border-width'
                      | 'width-padding'
                      | 'height-padding'
                      | 'delay' ) [0-9]+ ;

number_macro       -> ( 'border-radius' ) '-'? [0-9]+ ( '.' [0-9]* )? ;

NOTE in wks files, comments can be added using the pound character (#). When a pound character is encountered, it signifies the start of a comment. The comment extends from the pound character until the end of the line. It’s important to note that the pound character is treated as a literal character within descriptions and commands and does not indicate the start of a comment in those contexts.

KEY CHORD

A key chord is the top-level construct in the grammar and represents a complete key chord definition.

key_chord -> ( chord | prefix | chord_array ) ;

It can be either a prefix, a chord, or a chord array.

CHORD

A chord is a key chord that results in wk performing some action, like executing a command, when the trigger key is pressed.

chord -> trigger_key description keyword* command ;

All chords must have a trigger key, description, and a command. Zero or more keywords may be given between the description and command.

TRIGGER KEY

A trigger key represents the specific keypress or key combination that triggers a corresponding action or command. In a wks file, it is the written representation of the physical key(s) pressed by the user on their keyboard.

trigger_key -> modifier* ( normal_key | special_key ) ;

A trigger key is then zero or more modifiers followed by a normal key or a special key.

NORMAL KEY

A normal key is any printable, non-whitespace, utf8 character.

normal_key -> ( '\\' [\\\[\]{}#":^+()] | [^\s\[\]{}#":^+()] ) ;

Certain characters have special meanings in wks files. To use these characters as a normal key, simply precede them with a backslash (\).

[
Begins a chord array.
]
Ends a chord array.
{
Begins a prefix block.
}
Ends a prefix block.
#
Begins a comment.
Begins and ends a description.
:
Begins a preprocessor macro.
^
Begins a hook.
+
Begins a flag.
(
Begins a chord expression.
)
Ends a chord expression.

All other non-whitespace, printable utf8 characters prior to a description will be interpreted as a normal key. Those that are whitespace or non-printable fall into the special key category.

SPECIAL KEY

Special keys like tab, escape, spacebar, and F1 can still be used as trigger keys in wks files via their special forms.

special_key -> ( 'Left'    | 'Right'   | 'Up'     | 'Down'
               | 'TAB'     | 'SPC'     | 'RET'    | 'DEL'  | 'ESC'
               | 'Home'    | 'PgUp'    | 'PgDown' | 'End'  | 'Begin'
               | 'VolDown' | 'VolMute' | 'VolUp'  | 'Play' | 'Stop'
               | 'Prev'    | 'Next'    | 'F'[1-35] ) \s+ ;

Each form should indicate the special key it represents but here is a chart to make things explicit.

Left
Left arrow
Right
Right arrow
Up
Up arrow
Down
Down arrow
TAB
Tab
SPC
Space
RET
Enter/Return
DEL
Delete
ESC
Esc
Home
Home
PgUp
Page up
PgDown
Page down
End
End
Begin
Begin
F[1-35]
Function keys 1 through 35.
VolDown
Volume Down
VolMute
Mute Vol
VolUp
Volume Up
Play
Play Audio
Stop
Stop Audio
Prev
Audio Previous
Next
Audio Next

In wks files, whitespace is generally not significant around individual parts of the syntax, with one notable exception: special keys. When using special keys, it is required to include whitespace between the end of the special key and the start of the next item in the wks file.

If you have any additional special keys that you would like wks files to support, please open an issue or a pull request.

MODIFIER

As mentioned above, zero or more modifiers can be given in a trigger key.

modifier -> ( 'C' | 'H' | 'M' | 'S' ) '-' ;

Modifiers can be used in wks files via their special forms.

C-
Control key
H-
Hyper key
M-
Meta key
S-
Shift key

Modifiers act as one would expect. To match the keypress Control+c use the form C-c in your wks file.

Among the modifiers, the Shift modifier (S-) has a unique behavior when used with normal keys. Due to the way normal keys are interpreted, the S- modifier is not always necessary. To determine whether S- is required, it is recommended to test the character in a wks file by typing it with and without the Shift key pressed.

If the character is non-whitespace, printable, and the shifted and unshifted versions produce different output, then the S- modifier is not needed. For instance, pressing the a key with the Shift key held down produces an uppercase A. This test demonstrates that the key’s output changes based on the Shift key state.

In such cases, using S-a in a wks file would not work as expected because the key will never match when the user presses Shift+a.

I am open to changing it so that S-a and A match the same Shift+a keypress, but I have yet to find a fitting solution. The ones I can think of either involve depending on some utf8 library, writing the code by hand, or permitting this syntax for ASCII but not other character sets. Each has its own drawback, and I find the current solution to be intuitive in practice.

DESCRIPTION

A description provide a hint about the purpose of the chord or prefix.

description -> '"' ( '\\"' | [^"] | interpolation )* '"' ;

A description starts with a double quote (), followed by zero or more of the following:

"
Escaped double quotes.
[^”]
Any non-double quote character.
interpolation
An interpolation.

A description ends with a double quote. Aside from interpolations, a description looks like your typical string in many programming languages.

COMMAND

A command is some action to be executed upon completing a key chord sequence.

command -> '%' delimiter ( . | interpolation )* delimiter ;

A command begins with the percent character (%) followed by a delimiter. After the delimiter zero or more characters, or interpolations may be given. A command is ended with the same delimiter that followed the percent character.

Because the delimiter is user defined, there should be no misinterpretation of anything between the delimiters. This means any command given at the command-line should be right at home in between the delimiters.

DELIMITER

A delimiter acts as a start and stop marker for a command in a wks file.

delimiter   -> ( open_delim | close_delim | ([^[{(])\1 )  ;

open_delim  -> ( '{{' | '((' | '[[' ) ;

close_delim -> ( '}}' | '))' | ']]' ) ;

A delimiter may be one of the following:

open_delim or close_delim
The opening and closing delimiters are special delimiters that that have an inverse match. If an opening delimiter is given then the corresponding closing delimiter is required to end the command (e.g., {{ matches }} and so forth).
([^[{(])\1
Any ASCII character that is not any opening bracket ([), opening brace ({), or any opening parenthesis ((), given twice. NOTE this excludes null bytes (\0) as these will indicate the end of a wks file or script. When an arbitrary delimiter is given the same character is expected to be repeated to indicate the end of a command.

The delimiter from one command to the next may be completely different. This puts the burden on the user to ensure their delimiter is compatible with the content of the command.

Here are some examples of different delimiters for the same command.

# Commands with opening and closing delimiters
%{{echo "hello, world"}}
%((echo "hello, world"))
%[[echo "hello, world"]]

# Valid arbitrary delimiters
%||echo "hello, world"||
%%%echo "hello, world"%%
%zzecho "hello, world"zz

Inspired by *sed*​(1), this should keep wks syntax compatible with shell commands, almost indefinitely. It also makes it possible to nest a wks script within a wks command if you want to get really weird.

PREFIX

A prefix is a special type of key chord that acts as a container for other key chords. It represents an incomplete key combination that does not trigger a command on its own.

prefix -> trigger_key description keyword* '{' ( key_chord )+ '}' ;

A prefix has many of the same components as a chord. It begins with a trigger key, followed by a description, zero or more keywords and then a block of one or more key chords surrounded by an opening and closing brace ({, and }).

Note that a key chord may be a prefix, a chord, or a chord array, meaning many prefixes can be nested one inside another.

Here is a simple example of a prefix:

m "+Music"
{
    n "Next" %{{mpc next}}
    p "Prev" %{{mpc prev}}
}

CHORD ARRAY

Chords and prefixes are standard fare in the realm of key chords, so what the heck is a chord array? Well, mostly syntactic sugar so you do not have to repeat yourself when it comes to chords that are very similar but only differ in slightly different ways.

chord_array -> ( implicit_array | explicit_array ) ;

A chord array comes in two flavors, implicit and explicit.

IMPLICIT ARRAY

An implicit array is the simplest of the two flavors. It utilizes the implicitArrayKeys variable defined in config.def.h to generate chords from these trigger keys.

implicit_array -> modifier* '...' description keyword* command ;

An implicit array is then zero or more modifiers, an ellipsis (), a description, zero or more keywords, and a command. This is practially a chord in terms of its form, but in behavior an implicit array generates any number of chords from this simple syntax.

As an example, say your implicit array keys are set to h, j, k, and l, and you have this wks file:

... "Switch workspace %(index+1)" %{{xdotool set_desktop %(index)}}

This is the equivilant wks file without the use of an implicit array:

h "Switch workspace 1" %{{xdotool set_desktop 0}}
j "Switch workspace 2" %{{xdotool set_desktop 1}}
k "Switch workspace 3" %{{xdotool set_desktop 2}}
l "Switch workspace 4" %{{xdotool set_desktop 3}}

EXPLICIT ARRAY

An explicit array is most useful when the desired chords are less homogeneous.

explicit_array -> '[' ( trigger_key | chord_expression )+ ']' description keyword* command ;

To use an explicit array begin with an open bracket ([) followed by one or more trigger keys or chord expressions. The array portion ends with a closing bracket (]) followed by the standard chord components, a description, zero or more keywords, and a command.

I think an example will make things clear:

# Chord array version
[asdfghjkl] "Switch workspace %(index+1)" %{{xdotool set_desktop %(index)}}

# Individual chords and no interpolation
a "Switch workspace 1" %{{xdotool set_desktop 0}}
s "Switch workspace 2" %{{xdotool set_desktop 1}}
d "Switch workspace 3" %{{xdotool set_desktop 2}}
f "Switch workspace 4" %{{xdotool set_desktop 3}}
g "Switch workspace 5" %{{xdotool set_desktop 4}}
h "Switch workspace 6" %{{xdotool set_desktop 5}}
j "Switch workspace 7" %{{xdotool set_desktop 6}}
k "Switch workspace 8" %{{xdotool set_desktop 7}}
l "Switch workspace 9" %{{xdotool set_desktop 8}}

In this case, explicit arrays are only slightly different than an implicit array. However, explicit arrays support chord expressions which make them far more flexible.

CHORD EXPRESSION

Explicit arrays can be very simple with each chord being only slightly different from one another. However, it may make sense to include chords that mostly fit into the explicit array with some more distinct differences. For this situation, chord expressions may be the answer.

chord_expression -> '(' trigger_key description keyword* command? ')' ;

A chord expression is only valid within a chord array, and it is essentially a chord wrapped in parentheses with some added flexibility. Normally, a chord requires at least a trigger key, a description, and a command. A chord expression, on the other hand, requires only a trigger key and a description. Any other information will be filled in by the surrounding chord array.

Here is an example of a chord expression within a chord array:

# With chord arrays and chord expressions
[
    (b "Brave")
    (c "Mullvad Chrome" %{{mullvad-exclude chrome ~/startpage.html}})
    x
] "XDG-OPEN" %{{%(desc,,) ~/startpage.html}}

# With chords and no interpolation
b "Brave" %{{brave ~/startpage.html}}
c "Mullvad Chrome" %{{mullvad-exclude chrome ~/startpage.html}}
x "XDG-OPEN" %{{xdg-open ~/startpage.html}}

Admittedly, chord expressions may not be that useful but they were easy to implement so they are here for those who want to use them.

INTERPOLATION

An interpolation is a means of accessing some metadata of the current chord from within a description or a command.

interpolation -> '%(' identifier ')' ;

The basic syntax for an interpolation begins with a %( delimiter followed by an identifier and closing parenthesis ()).

IDENTIFIER

The following identifiers are valid within an interpolation:

key
The key identifier corresponds to the trigger key of the current chord. This makes the most sense to use within a chord array or for a chord that may change frequently or is not know ahead of time.
index
The index identifier corresponds to the 0 base index of the current chord or prefix within the current scope. NOTE a prefix starts a new scope.
index+1
The index+1 identifier corresponds to the 1 base index of the current chord or prefix within the current scope. NOTE a prefix starts a new scope.
desc
The desc identifier correspond to the description of the current chord or prefix. The desc identifier may not be given within a description. An error will be thrown in the case where this is attempted.
desc^
The description of the current chord with the first character capitalized.
desc^^
The description of the current chord with the all characters capitalized.
desc,
The description of the current chord with the first character downcased.
desc,,
The description of the current chord with the all characters downcased.

KEYWORD

A keyword is an optional instruction to modify the behavior of a chord or prefix.

keyword -> ( hook | flag ) ;

A keyword is either a hook or a flag. Both have equal precedence, meaning they can be mixed up wherever they are permitted.

HOOK

Hooks provide means of adding additional commands to a chord or prefix.

hook -> '^' ( 'before'
            | 'after'
            | 'sync-before'
            | 'sync-after' ) command ;

A hook begins with the caret character (^), followed by the type of hook, and finally the command the hook will run.

The hook type has to do with the order the command will be run. The before hooks run before the chord’s command, and the after hooks run after the chord’s command.

The sync- hooks relate to how wk runs the commands. By default, all commands are run asynchronously to prevent a command from blocking wk. However, if the hook must complete before wk can proceed you can use the sync-* variant to enforce this behavior.

NOTE that a blocking command may prevent wk from ever resuming execution. In the event that this happens, users may need to restart their system entirely to regain control of their keyboard.

See EXAMPLES for further discussion about hooks.

FLAG

Flags are similar to command-line flags in that they change the behavior of wk.

flag -> '+' ( 'keep'
            | 'close'
            | 'inherit'
            | 'ignore'
            | 'ignore-sort'
            | 'unhook'
            | 'deflag'
            | 'no-before'
            | 'no-after'
            | 'write'
            | 'execute'
            | 'sync-command' ) ;

Flags begin with a plus character (+), followed by the flag itself. Here is how each flag changes the behavior of wk:

keep
Instead of closing after `wk` finds a matching chord, it keeps the `wk` menu open.
close
Forces the `wk` window to close. Useful when `+keep` was given to a surrounding prefix.
inherit
Causes the prefix to inherit flags and hooks from its parent. Has no effect when given to a chord.
ignore
Ignore all hooks and flags from the surrounding prefix. Has no effect when given to a prefix.
ignore-sort
Chord is ignored during sorting leaving it in it in the same position it was parsed in.
unhook
Ignore all hooks from the surrounding prefix.
deflag
Ignore all flags from the surrounding prefix.
no-before
Ignore `before` and `sync-before` hooks from the surrounding prefix.
no-after
Ignore `after` and `sync-after` hooks from the surrounding prefix.
write
Write commands to stdout rather than executing them.
execute
Execute the command rather than writing them to stdout. Useful when `+write` was given to a surrounding prefix.
sync-command
Execute the command in a blocking fashion. See the note in HOOK regarding potential issues with blocking commands.

See EXAMPLES for further discussion about flags.

PREPROCESSOR MACROS

There are a number of preprocessor macros that can be used in wks files. These have a number of uses from making wks files more modular to controlling the look and feel of *wk*​(1).

preprocessor_macro -> ':' ( string_macro
                          | switch_macro
                          | integer_macro
                          | unsigned_macro
                          | number_macro ) ;

A preprocessor macro begins with the colon character (:) followed by a specific macro form.

The majority of macros correspond to the command-line arguments that *wk*​(1) supports. When given, these override anything given at the command-line. They are here to provide a baked-in alternative to the command-line versions making it easy to simply run the wks file and get the desired look and feel without having to give the same arguments each time. It can also help distinguish the purpose of the key chords if it is intended to be used as part of a script by making the *wk*​(1) popup window different from the builtin settings.

STRING MACROS

String macros require a string argument.

string_macro -> ( 'include'
                | 'fg-color'
                | 'bg-color'
                | 'bd-color'
                | 'shell'
                | 'font' ) '"' ( '\\"' | [^"] )* '"' ;

Many of the macros here work the same as their command-line counterparts. Simply use :MACRO “ARGUMENT” to make use of any string macro, (e.g. :shell “/usr/bin/env zsh”).

INCLUDE MACRO

Out of the string macros, the :include macro is not present as a command-line argument to *wk*​(1). This is because this macro has more to do with wks files than the look and feel of *wk*​(1). The :include macro works similarly to the #include macro found in C/C++. It allows users to bring other wks files into a single file. NOTE, self includes and recursive includes are not permitted and will cause an error. NOTE, the same file may be included multiple times. This is not an error, and may even be desirable for some users. NOTE, while the #include macro in C/C++ has restrictions on where it can go in a file, the :include macro in a wks file may go literally anywhere. As for file resolution, it’s pretty simple. A relative path is assumed to be in the same directory as the file being processed, and absolute paths are just that, absolute.

See EXAMPLES for a full demonstration of the :include macro.

SWITCH MACROS

Switch macros are the simplest of the bunch. They are essentially an on switch for the corresponding menu settings.

switch_macro -> ( 'debug'
                | 'sort'
                | 'top'
                | 'bottom' );

All the switch macros correspond to their cli flags for *wk*​(1).

INTEGER MACROS

The integer macros require a positive or negative integer argument to the macro.

integer_macro -> ( 'menu-width'
                 | 'menu-gap' ) '-'? [0-9]+ ;

All the integer macros correspond to their cli flags for *wk*​(1).

UNSIGNED MACROS

The unsigned macros require a positive integer argument to the macro.

unsigned_macro -> ( 'max-columns'
                  | 'border-width'
                  | 'width-padding'
                  | 'height-padding'
                  | 'delay' ) [0-9]+ ;

All the unsigned macros correspond to their cli flags for *wk*​(1).

NUMBER MACROS

The number macros require a positive number argument to the macro.

number_macro -> ( 'border-radius' ) '-'? [0-9]+ ( '.' [0-9]* )? ;

All the number macros correspond to their cli flags for *wk*​(1).

EXAMPLES

HOOKS

Users can certainly chain commands together the same way one would chain commands in a regular shell, but hooks help to reduce repetition. They also make more sense in the context of prefixes.

# With hooked prefix
e "+Emacs" ^before %{{xdotool set_desktop 1}}
{
    o "Open" %{{emacsclient -c -a ""}}
    r "Roam" %{{emacsclient -c -a "" ~/20240101080032-startpage.org}}
}

# Without hooks
e "+Emacs"
{
    o "Open" %{{xdotool set_desktop 1 ; emacsclient -c -a ""}}
    r "Roam" %{{xdotool set_desktop 1 ; emacsclient -c -a "" ~/20240101080032-startpage.org}}
}

As you can see, this helps to cut down on repetition, but it also helps enforce a workflow rule without the need to setup desktop environment rules and such.

This example also hints at the idea of inheritance as the hook was given to a prefix and not to individual chords. This topic is covered after introducing flags as these also factor into the discussion.

FLAGS

Each flag has a time and a place but I find +keep, and +write to be the most useful out of the bunch.

The +keep flag can turn wk into a hydra of sorts. I use this to control music playback on my system like this:

m "+Music" +keep
{
    c "Clear mpc" %{{mpc clear}}
    d "Display Song" %{{songinfo}}
    h "Seek -5" %{{mpc seek "-5"}}
    l "Seek +5" %{{mpc seek "+5"}}
    n "Next song" %{{mpc next}}
    p "Prev song" %{{mpc prev}}
    o "Open mpc" +close %{{st -e ncmpcpp}}
    y "Playlist" +close %{{st -e ncmpcpp --screen playlist}}
}

The +write flag is useful for scripting purposes. In the same way that *dmenu*​(1) and co print selections to stdout, this turns *wk*​(1) into a prompt for users to choose from some list of options with less typing.

THE INCLUDE MACRO

Here is an example of the :include macro:

# File main.wks
---------------
# Browser prefix
b "+Browser" { :include "browser_key_chords.wks" }
# Emacs prefix
e "+Emacs" ^before %{{xdotool set_desktop 1}} { :include "emacs_key_chords.wks" }
# Music prefix
m "+Music" +keep { :include "music_key_chords.wks" }

# File browser_key_chords.wks
-----------------------------
[
    (b "Brave")
    (c "Chrome")
    (f "Firefox")
] "null" %{{%(desc,,)}}

# Mullvad-exclude prefix
m "+Mullvad Exclude"
{
    [
        (b "Brave")
        (c "Chrome")
        (f "Firefox")
    ] "null" %{{mullvad-exclude %(desc_)}}
}

# File emacs_key_chords.wks
---------------------------
b "Open blank" %{{emacsclient -c -a ""}}
p "+Projects"
{
    w "wk" %{{emacs "~/Projects/wk"}}
}

# File music_key_chords.wks
---------------------------
c "Clear mpc" %{{mpc clear}}
d "Display song" %{{songinfo}}
h "Seek -5s" %{{mpc seek "-5"}}
l "Seek +5s" %{{mpc seek "+5"}}
n "Next song" %{{mpc next}}
p "Prev song" %{{mpc prev}}
o "Open mpc" +close %{{st -e ncmpcpp}}

This allows users to create key chords in a more modular manner. This can be beneficial when you may want to reuse a wks file in a different context than your main key chords.

You can even do silly things like this:

# File part_one.wks
-------------------
A "silly :include "part_two.wks"

# File part_two.wks
-------------------
example" %{{echo "You wouldn't do this right??"}}

# Resulting wks file
--------------------
A "silly example" %{{echo "You wouldn't do this right??"}}

NOTES

INHERITANCE

Inheritance relates to hooks and flags given to prefixes. The idea is fairly simple. A hook or flag given to a prefix is inherited by any chord within the prefix. Nested prefixes do not inherit the hooks and flags given to their parent.

a "+Prefix" +write
{
    w "Write it!" %{{I get written!}}
    n "+Nested Prefix"
    {
        r "Run it!" %{{echo "I get run!"}}
    }
}

In the above example, the key chord a w causes I get written! to be printed to stdout. The key chord a n r runs the command echo “I get run!”.

To force a nested prefix to inherit from its parent the +inherit flag must be given. Additionally, if the prefix only wishes to inherit certain hooks or flags additional flags may be given to ignore unwanted behavior.

SORTING

Key chords will be sorted when processing a wks file if the –sort flag is passed to wk. This has knock-on effects with index interpolations (often for chord arrays). A wks file like this will produce different results sorted vs unsorted (the default).

# Base file
[neio] "Switch %(index+1)" %{{xdotool set_desktop %(index)}}
b "Second?" +write %{{%(index)}}
a "First?" +write %{{%(index)}}

# Unsorted result
n "Switch 1" %{{xdotool set_desktop 0}}
e "Switch 2" %{{xdotool set_desktop 1}}
i "Switch 3" %{{xdotool set_desktop 2}}
o "Switch 4" %{{xdotool set_desktop 3}}
b "Second?" +write %{{4}}
a "First?" +write %{{5}}

# Sorted result
a "First?" +write %{{0}}
b "Second?" +write %{{1}}
e "Switch 3" %{{xdotool set_desktop 2}}
i "Switch 4" %{{xdotool set_desktop 3}}
n "Switch 5" %{{xdotool set_desktop 4}}
o "Switch 6" %{{xdotool set_desktop 5}}

To avoid this you can add the +ignore-sort flag to any key chord to ensure the value of the index interpolations.

# Base file
[neio] "Switch %(index+1)" +ignore-sort %{{xdotool set_desktop %(index)}}
b "Second?" +write %{{%(index)}}
a "First?" +write %{{%(index)}}

# Sorted with `+ignore-sort` result
e "Switch 1" %{{xdotool set_desktop 0}}
i "Switch 2" %{{xdotool set_desktop 1}}
n "Switch 3" %{{xdotool set_desktop 2}}
o "Switch 4" %{{xdotool set_desktop 3}}
a "First?" +write %{{4}}
b "Second?" +write %{{5}}

BUG REPORTS

Please see wk*​(1) *BUG REPORTS for info on reporting bugs.