Qmk.nvim
Format qmk and zmk keymaps in neovim
Install / Use
/learn @codethread/Qmk.nvimREADME
qmk.nvim
qmk.nvim is a 100% lua plugin for Neovim that formats QMK and ZMK keymaps, used in a large number of mechanical and hobbyist keyboards.

Features
- automatically align your keymaps
- create a comment string of your keymap
- use inline JSON comments to make quick easy changes
- supports QMK and ZMK* (though I highly recommend keymap-editor)
- note any preprocessor macros must start with
_if they are to be identified as the start of a key, e.g
#define _AS(keycode) &as LS(keycode) keycode // ...etc default_layer { &kp TAB _AS(Q) SOME_OTHER }- ZMK is still experimental, please report any bugs
- note any preprocessor macros must start with
For a simple example of the following keymap
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_QWERTY] = LAYOUT_preonic_grid(
KC_1 , K2,
K3 , K4, // notice the white space
KC_5 , HERE_BE_A_LONG_KEY // and how this doesn't line up at all
)
};
Setup your layout:
local qmk = require 'qmk'
qmk.setup {
name = 'LAYOUT_preonic_grid', -- identify your layout name
comment_preview = {
keymap_overrides = {
HERE_BE_A_LONG_KEY = 'Magic', -- replace any long key codes
},
},
layout = { -- create a visual representation of your final layout
'x ^xx', -- including keys that span multiple rows (with alignment left, center or right)
'_ x x', -- pad empty cells
'_ x x',
},
}
Save the file and it will automatically be nicely aligned, with a pretty comment string
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
// ┌───┬────────────┐
// │ 1 │ K2 │
// └───┼────┬───────┤
// │ K3 │ K4 │
// ├────┼───────┤
// │ 5 │ Magic │
// └────┴───────┘
[_QWERTY] = LAYOUT_preonic_grid(
KC_1 , K2 ,
K3 , K4 ,
KC_5 , HERE_BE_A_LONG_KEY
)
};
Requirements
- Neovim >= 0.10
- QMK: Treesitter
cparser available (e.g through nvim-treesitter) - ZMK: Treesitter
devicetreeparser available (e.g through nvim-treesitter)- this can be used for .keymap files with
set ft=dts
- this can be used for .keymap files with
Installation
- install with your favourite package manager, e.g packer
- call
setupwith your layout name and your layout configuration
e.g:
use {
'codethread/qmk.nvim',
config = function()
---@type qmk.UserConfig
local conf = {
name = 'LAYOUT_preonic_grid',
layout = {
'_ x x x x x x _ x x x x x x',
'_ x x x x x x _ x x x x x x',
'_ x x x x x x _ x x x x x x',
'_ x x x x x x _ x x x x x x',
'_ x x x x x x _ x x x x x x',
}
}
require('qmk').setup(conf)
end
}
Configuration
qmk.nvim takes the following configuration (---@type qmk.UserConfig):
| setting | type | json | descritpion |
|------------------------------------|---------------------------------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| name | string required | ✓ | the name of your layout, for example LAYOUT_preonic_grid for the preonic keyboard, for zmk this can just be anything, it won't be used |
| layout | string[] required | ✓ | the keyboard key layout, see Layout for more details |
| variant | qmk,zmk | | (default qmk) chooses the expected hardware target |
| timeout | number | | (default 5000) duration of vim.notify timeout if using nvim-notify |
| auto_format_pattern | string,string[] | | (default { '*keymap.c', '*.keymap' }) the autocommand file pattern to use when applying QMKFormat on save |
| comment_preview | table | ✓ | table of properties for rendering a pretty comment string of each keymap |
| comment_preview.position | top,bottom,inside, none | ✓ | (default top) control the position of the preview, set to none to disable (inside is only valid for variant=qmk) |
| comment_preview.keymap_overrides | table<string, string> | ✓ | a dictionary of key codes to text replacements, any provided value will be merged with the existing dictionary, see key_map.lua for details |
| comment_preview.symbols | table<string, string> | ✓ | a dictionary of symbols used for the preview comment border chars see default.lua for details |
examples
Here are some example configurations:
<details> <summary>Disabling most features</summary>{
name = 'Some_layout',
layout = { { 'x', 'x' } },
auto_format_pattern = nil,
comment_preview = {
position = 'none'
}
}
</details>
<details>
<summary>Using Multiple Configurations</summary>
setup can be called multiple times without issue to apply different configuration options. This means you can use autocommands to apply different configurations for different boards, e.g:
local group = vim.api.nvim_create_augroup('MyQMK', {})
vim.api.nvim_create_autocmd('BufEnter', {
desc = 'Format simple keymap',
group = group,
pattern = '*simple/keymap.c', -- this is a pattern to match the filepath of whatever board you wish to target
callback = function()
require('qmk').setup({
name = 'LAYOUT_preonic_grid',
auto_format_pattern = "*simple/keymap.c",
layout = {
'_ x x x x x x _ x x x x x x',
},
})
end,
})
vim.api.nvim_create_autocmd('BufEnter', {
desc = 'Format overlap keymap',
group = group,
pattern = '*overlap/keymap.c',
callback = function()
require('qmk').setup({
name = 'LAYOUT_preonic_grid',
auto_format_pattern = "*overlap/keymap.c",
layout = {
'x x x x x',
},
})
end,
})
</details>
<details>
<summary>using ZMK</summary>
{
name = 'meh',
layout = { { 'x', 'x' } },
variant = 'zmk'
}
</details>
<details>
<summary>Overriding a long key code</summary>
For the configuration
{
name = 'Some_layout',
layout = { 'x x' },
comment_preview = {
position = 'inside',
keymap_overrides = {
-- key codes are mapped literally against the entire key in your layout
-- longer key codes are checked first, and these will replace the value displayed in the preview
--
-- lua magic patterns used to need escaping with `%` but not anymore
-- old escaped patterns should still work thoug
-- watch out for emojis as they are double width
['LSG%(KC_GRAVE%)'] = 'Next Window',
['LAG(KC_GRAVE)'] = 'Prev Window',
},
},
}
With keymap.c:
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_QWERTY] = Some_layout(
KC_1
,
LSG(KC_GRAVE)
)
}
Becomes:
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_QWERTY] = Some_layout(
// ┌───┬─────────────┐
// │ 1 │ Next Window │
// └───┴─────────────┘
KC_1 , LSG(KC_GRAVE)
)
}
Also if your key codes are quite long, you can define aliases in c
//Aliases for longer keycodes
#define NUMPAD TG(_NUMPAD)
</details>
<details>
<summary>A pretty kinisis layout</summary>
for the configuration
{
name = 'LAYOUT_pretty',
layout = {
