patch 9.1.2110: filetype: skhd files are not recognized
Commit:
https://github.com/vim/vim/commit/e5f61842b509acea91c63923e60987ad7ae123dd
Author: Kiyoon Kim <
kiy...@users.noreply.github.com>
Date: Sun Jan 25 17:39:05 2026 +0000
patch 9.1.2110: filetype: skhd files are not recognized
Problem: filetype: skhd files are not recognized
Solution: Detect .skhdrc and skhdrc as skhd filetype,
include a syntax and filetype plugin, add syntax tests
(Kiyoon Kim)
Add syntax highlighting for skhd (simple hotkey daemon for macOS)
configuration files. Includes filetype detection for skhdrc and
.skhdrc files.
Reference:
-
https://github.com/asmvik/skhd
closes: #19235
Signed-off-by: Kiyoon Kim <
kiy...@users.noreply.github.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS
index f5767bf52..e139c449e 100644
--- a/.github/MAINTAINERS
+++ b/.github/MAINTAINERS
@@ -658,6 +658,7 @@ runtime/syntax/scss.vim @tpope
runtime/syntax/sed.vim @dkearns
runtime/syntax/shared/debversions.vim @jamessan
runtime/syntax/shaderslang.vim @mTvare6
+runtime/syntax/skhd.vim @kiyoon
runtime/syntax/solidity.vim @coti-z
runtime/syntax/spajson.vim @dseomn
runtime/syntax/spec.vim @ignatenkobrain
diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
index 9d68c3d21..75a4bb5e6 100644
--- a/runtime/autoload/dist/ft.vim
+++ b/runtime/autoload/dist/ft.vim
@@ -3305,6 +3305,9 @@ const ft_from_name = {
# Screen RC
".screenrc": "screen",
"screenrc": "screen",
+ # skhd (simple hotkey daemon for macOS)
+ ".skhdrc": "skhd",
+ "skhdrc": "skhd",
# SLRN
".slrnrc": "slrnrc",
# Squid
diff --git a/runtime/ftplugin/skhd.vim b/runtime/ftplugin/skhd.vim
new file mode 100644
index 000000000..ecc932f58
--- /dev/null
+++ b/runtime/ftplugin/skhd.vim
@@ -0,0 +1,20 @@
+" Vim filetype plugin file
+" Language: skhd(simple hotkey daemon for macOS) configuration file
+" Maintainer: Kiyoon Kim <
https://github.com/kiyoon>
+" Last Change: 2026 Jan 23
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+setlocal comments=:# commentstring=#\ %s
+setlocal formatoptions-=t formatoptions+=croql
+
+let b:undo_ftplugin = "setl com< cms< fo<"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/skhd.vim b/runtime/syntax/skhd.vim
new file mode 100644
index 000000000..6bda7a60a
--- /dev/null
+++ b/runtime/syntax/skhd.vim
@@ -0,0 +1,137 @@
+" Vim syntax file
+" Language: skhd configuration file
+" Maintainer: Kiyoon Kim <
https://github.com/kiyoon>
+" Last Change: 2025 Jan 22
+
+if exists("b:current_syntax")
+ finish
+endif
+
+" Comments: whole line from '#'
+syn match skhdComment /^\s*#.*/
+
+" Modifiers (shift, ctrl, alt, cmd, fn)
+syn keyword skhdModifier
+ \ alt lalt ralt
+ \ shift lshift rshift
+ \ cmd lcmd rcmd
+ \ ctrl lctrl rctrl
+ \ fn hyper meh
+ \ option super
+" highlight the '+' and '-' and ':' separators
+syn match skhdOperator /->/
+syn match skhdOperator /[+:\-;<>,\[\]@~]/
+
+" Hex keycode form: 0x3C etc
+syn match skhdKeycode / 0x[0-9A-Fa-f]+/
+
+" Keys (a–z, digits, function‐keys, arrows…)
+syn keyword skhdKey
+ \ return tab space backspace escape delete
+ \ home end pageup pagedown insert
+ \ left right up down
+ \ sound_up sound_down mute play previous next rewind fast
+ \ brightness_up brightness_down illumination_up illumination_down
+syn match skhdKey / f([1-9]|1[0-9]|20)\>/
+syn match skhdKey / \<[A-Za-z0-9]\>/
+
+" The yabai command and its subcommands
+syn match skhdCommand /\<yabai\>\|\<open\>/
+syn match skhdSubCmd /\<window\>\|\<space\>\|\<display\>/
+
+" ───────────────────────────────────────────────────────────────────
+" Treat anything after a single “:” (not double‑colon) as bash
+" ───────────────────────────────────────────────────────────────────
+" load Vim’s built‑in shell rules
+syntax include @bash syntax/bash.vim
+
+" After `:` (not `::`) is a bash command, but not when it is preceded by a `\`
+syn region skhdBash
+ \ matchgroup=skhdOperator
+ \ start=/ (^|[^:])\zs:\s*/
+ \ end=/ \s*$\ze/
+ \ skip=/ \\s*$/
+ \ keepend
+ \ contains=@bash
+
+" ────────────────────────────────────────────────────────────────
+" Key‑map group definitions and switches
+" ────────────────────────────────────────────────────────────────
+" In skhd, you can define groups and assign hotkeys to them as follows:
+" 1. Group‑definition lines that start with :: <group>
+" 2. Switch operator (<)
+" 3. Target group names after the ;
+
+" Lines like `:: default` or `:: passthrough`
+" match the whole thing as a GroupDef, but capture the group name
+syn match skhdGroupDef /^::\s*\w\+/
+syn match skhdGroupName /::\s*\zs\w\+/
+
+" The `<` switch token in lines like
+" passthrough < cmd + shift + alt - b ; default
+syn match skhdSwitch /<\s*/
+
+" The target (or “fall‑through”) group after the semicolon
+" ... ; default
+syn match skhdTargetGroup /;\s*\zs\w\+/
+
+
+" ------------------------------------------------------------
+" Application-specific bindings block: <keysym> [ ... ]
+" ------------------------------------------------------------
+
+" The whole block. This avoids grabbing .blacklist by requiring the line be just '[' at end.
+syn region skhdProcMapBlock
+ \ matchgroup=skhdProcMapDelim
+ \ start=/ \[\s*$/
+ \ end=/^\s*\]\s*$/
+ \ keepend
+ \ transparent
+ \ contains=skhdProcMapApp,skhdProcMapWildcard,skhdProcMapUnbind,skhdOperator,skhdComment,skhdBash,skhdString
+
+" App name on the left side: "Google Chrome" :
+syn match skhdProcMapApp /^\s*\zs"[^"]*"\ze\s*:\s*/ contained
+
+" Wildcard entry: * :
+syn match skhdProcMapWildcard /^\s*\zs\*\ze\s*:\s*/ contained
+
+" Unbind operator on the right side: "App" ~ or * ~
+syn match skhdProcMapUnbind / ^\s*(\"[^"]*\"|\*)\s*\zs\~\ze\s*$/ contained
+
+syn keyword skhdDirective .load .blacklist
+syn match skhdLoadLine /^\s*\.load\>\s\+/ contains=skhdDirective
+
+syn region skhdBlacklistBlock
+ \ start=/^\s*\.blacklist\>\s*\[\s*$/
+ \ end=/^\s*\]\s*$/
+ \ keepend
+ \ contains=skhdDirective,skhdComment,skhdString
+
+syn region skhdString start=/"/ skip=/\"/ end=/"/
+
+" ────────────────────────────────────────────────────────────────
+" Linking to standard Vim highlight groups
+" ────────────────────────────────────────────────────────────────
+hi def link skhdComment Comment
+hi def link skhdHeadline Title
+hi def link skhdModifier Keyword
+hi def link skhdOperator Operator
+hi def link skhdWildcard Special
+hi def link skhdKey Identifier
+hi def link skhdKeycode Number
+hi def link skhdCommand Function
+hi def link skhdSubCmd Statement
+hi def link skhdGroupDef Label
+hi def link skhdGroupName Identifier
+hi def link skhdSwitch Operator
+hi def link skhdTargetGroup Type
+hi def link skhdString String
+
+hi def link skhdProcMapDelim Operator
+hi def link skhdProcMapApp Type
+hi def link skhdProcMapWildcard Special
+hi def link skhdProcMapUnbind Special
+
+hi def link skhdDirective PreProc
+
+let b:current_syntax = "skhd"
diff --git a/runtime/syntax/testdir/dumps/skhdrc_00.dump b/runtime/syntax/testdir/dumps/skhdrc_00.dump
new file mode 100644
index 000000000..897b5e33e
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/skhdrc_00.dump
@@ -0,0 +1,20 @@
+>#+0#0000e05#ffffff0| |s|k|h|d| |c|o|n|f|i|g|u|r|a|t|i|o|n| |f|i|l|e| +0#0000000&@49
+|#+0#0000e05&| |T|h|i|s| |i|s| |a| |c|o|m@1|e|n|t| +0#0000000&@55
+@75
+|#+0#0000e05&| |B|a|s|i|c| |k|e|y| |b|i|n|d|i|n|g|s| |w|i|t|h| |m|o|d|i|f|i|e|r|s| +0#0000000&@39
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|h| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|w|e|s|t| @36
+|s+0#af5f00255&|h|i|f|t| +0#0000000&|++0#af5f00255&| +0#0000000&|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|j| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|s|o|u|t|h| @27
+|c+0#af5f00255&|t|r|l| +0#0000000&|++0#af5f00255&| +0#0000000&|a+0#af5f00255&|l|t| +0#0000000&|++0#af5f00255&| +0#0000000&|s+0#af5f00255&|h|i|f|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|k| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|n|o|r|t|h| @20
+|f+0#af5f00255&|n| +0#0000000&|-+0#af5f00255&| +0#0000000&|l| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|e|a|s|t| @37
+@75
+|#+0#0000e05&| |U|s|i|n|g| |h|e|x| |k|e|y|c|o|d|e|s| +0#0000000&@54
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|0+0#e000002&|x|2|A| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|p|e|n| |-+0#e000e06&|a| +0#0000000&|"+0#af5f00255&|F+0#e000002&|i|n|d|e|r|"+0#af5f00255&| +0#0000000&@45
+@75
+|#+0#0000e05&| |F|u|n|c|t|i|o|n| |k|e|y|s| +0#0000000&@59
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|f|1| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|F+0#e000002&|1| |p|r|e|s@1|e|d|"+0#af5f00255&| +0#0000000&@46
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|f|1|2| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|F+0#e000002&|1|2| |p|r|e|s@1|e|d|"+0#af5f00255&| +0#0000000&@44
+@75
+|#+0#0000e05&| |S|p|e|c|i|a|l| |k|e|y|s| +0#0000000&@60
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|r+0#00e0e07&|e|t|u|r|n| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|p|e|n| |-+0#e000e06&|a| +0#0000000&|"+0#af5f00255&|T+0#e000002&|e|r|m|i|n|a|l|"+0#af5f00255&| +0#0000000&@41
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|t+0#00e0e07&|a|b| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|r|e|c|e|n|t| @32
+@57|1|,|1| @10|T|o|p|
diff --git a/runtime/syntax/testdir/dumps/skhdrc_01.dump b/runtime/syntax/testdir/dumps/skhdrc_01.dump
new file mode 100644
index 000000000..f9de95228
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/skhdrc_01.dump
@@ -0,0 +1,20 @@
+|c+0#af5f00255#ffffff0|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|f|1| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|F+0#e000002&|1| |p|r|e|s@1|e|d|"+0#af5f00255&| +0#0000000&@46
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|f|1|2| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|F+0#e000002&|1|2| |p|r|e|s@1|e|d|"+0#af5f00255&| +0#0000000&@44
+@75
+|#+0#0000e05&| |S|p|e|c|i|a|l| |k|e|y|s| +0#0000000&@60
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|r+0#00e0e07&|e|t|u|r|n| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|p|e|n| |-+0#e000e06&|a| +0#0000000&|"+0#af5f00255&|T+0#e000002&|e|r|m|i|n|a|l|"+0#af5f00255&| +0#0000000&@41
+>a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|t+0#00e0e07&|a|b| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|r|e|c|e|n|t| @32
+|c+0#af5f00255&|t|r|l| +0#0000000&|-+0#af5f00255&| +0#0000000&|e+0#00e0e07&|s|c|a|p|e| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|s|p|a|c|e| |-+0#e000e06&@1|b|a|l|a|n|c|e| +0#0000000&@34
+|s+0#af5f00255&|h|i|f|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|d+0#00e0e07&|e|l|e|t|e| +0#0000000&|:+0#af5f00255&| |r|m| +0#0000000&|-+0#e000e06&|r|f| +0#0000000&|~|/|.|T|r|a|s|h|/|*| @40
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|s+0#00e0e07&|p|a|c|e| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|p|e|n| |-+0#e000e06&|a| +0#0000000&|"+0#af5f00255&|S+0#e000002&|p|o|t|l|i|g|h|t|"+0#af5f00255&| +0#0000000&@41
+@75
+|#+0#0000e05&| |A|r@1|o|w| |k|e|y|s| +0#0000000&@62
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|l+0#00e0e07&|e|f|t| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|w|e|s|t| @33
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|r+0#00e0e07&|i|g|h|t| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|e|a|s|t| @32
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|u+0#00e0e07&|p| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|n|o|r|t|h| @34
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|d+0#00e0e07&|o|w|n| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|s|o|u|t|h| @32
+@75
+|#+0#0000e05&| |M|e|d|i|a| |k|e|y|s| +0#0000000&@62
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|s+0#00e0e07&|o|u|n|d|_|u|p| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|s|a|s|c|r|i|p|t| |-+0#e000e06&|e| +0#0000000&|"+0#af5f00255&|s+0#e000002&|e|t| |v|o|l|u|m|e| |o|u|t|p|u|t| |v|o|l|u|m|e| |1|0@1|"+0#af5f00255&| +0#0000000&@14
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|m+0#00e0e07&|u|t|e| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|s|a|s|c|r|i|p|t| |-+0#e000e06&|e| +0#0000000&|"+0#af5f00255&|s+0#e000002&|e|t| |v|o|l|u|m|e| |o|u|t|p|u|t| |m|u|t|e|d| |t|r|u|e|"+0#af5f00255&| +0#0000000&@18
+@57|1|9|,|1| @9|1|5|%|
diff --git a/runtime/syntax/testdir/dumps/skhdrc_02.dump b/runtime/syntax/testdir/dumps/skhdrc_02.dump
new file mode 100644
index 000000000..c34744f71
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/skhdrc_02.dump
@@ -0,0 +1,20 @@
+|c+0#af5f00255#ffffff0|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|m+0#00e0e07&|u|t|e| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|s|a|s|c|r|i|p|t| |-+0#e000e06&|e| +0#0000000&|"+0#af5f00255&|s+0#e000002&|e|t| |v|o|l|u|m|e| |o|u|t|p|u|t| |m|u|t|e|d| |t|r|u|e|"+0#af5f00255&| +0#0000000&@18
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|p+0#00e0e07&|l|a|y| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|s|a|s|c|r|i|p|t| |-+0#e000e06&|e| +0#0000000&|"+0#af5f00255&|t+0#e000002&|e|l@1| |a|p@1| |\+0#e000e06&|"|M+0#e000002&|u|s|i|c|\+0#e000e06&|"| +0#e000002&|t|o| |p|l|a|y|p|a|u|s|e|"+0#af5f00255&| +0#0000000&@15
+|c+0#af5f00255&|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|b+0#00e0e07&|r|i|g|h|t|n|e|s@1|_|u|p| +0#0000000&|:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|b+0#e000002&|r|i|g|h|t|n|e|s@1| |u|p|"+0#af5f00255&| +0#0000000&@32
+@75
+|#+0#0000e05&| |y|a|b|a|i| |s|u|b|c|o|m@1|a|n|d|s| +0#0000000&@55
+>a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|w| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|c|l|o|s|e| +0#0000000&@41
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|s| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|s|p|a|c|e| |-+0#e000e06&@1|c|r|e|a|t|e| +0#0000000&@41
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|d| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|d|i|s|p|l|a|y| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|n|e|x|t| @35
+@75
+|#+0#0000e05&| |M|o|d|e| |d|e|f|i|n|i|t|i|o|n|s| +0#0000000&@56
+|:+0#af5f00255&@1| |d|e|f|a|u|l|t| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|c|o|n|f|i|g| |a|c|t|i|v|e|_|w|i|n|d|o|w|_|b|o|r|d|e|r|_|c|o|l|o|r| |0|x|f@1|7@1|5|7|5|9| @8
+|:+0#af5f00255&@1| |r|e|s|i|z|e| +0#0000000&|@+0#af5f00255&| +0#0000000&|:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|c|o|n|f|i|g| |a|c|t|i|v|e|_|w|i|n|d|o|w|_|b|o|r|d|e|r|_|c|o|l|o|r| |0|x|f@1|d|7|5|f|5|f| @7
+@75
+|#+0#0000e05&| |M|o|d|e| |s|w|i|t|c|h|i|n|g| +0#0000000&@58
+|d|e|f|a|u|l|t| |<+0#af5f00255&| |a|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|r| |;+0#af5f00255&| +0#0000000&|r|e|s|i|z|e| @48
+|r|e|s|i|z|e| |<+0#af5f00255&| |e+0#00e0e07&|s|c|a|p|e| +0#0000000&|;+0#af5f00255&| +0#0000000&|d|e|f|a|u|l|t| @49
+|r|e|s|i|z|e| |<+0#af5f00255&| |a|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|r| |;+0#af5f00255&| +0#0000000&|d|e|f|a|u|l|t| @48
+@75
+|#+0#0000e05&| |M|o|d|e| |w|i|t|h| |p|a|s@1|t|h|r|o|u|g|h| +0#0000000&@51
+@57|3|7|,|1| @9|3|6|%|
diff --git a/runtime/syntax/testdir/dumps/skhdrc_03.dump b/runtime/syntax/testdir/dumps/skhdrc_03.dump
new file mode 100644
index 000000000..6f7ca6263
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/skhdrc_03.dump
@@ -0,0 +1,20 @@
+|#+0#0000e05#ffffff0| |M|o|d|e| |w|i|t|h| |p|a|s@1|t|h|r|o|u|g|h| +0#0000000&@51
+|:+0#af5f00255&@1| |p|a|s@1|t|h|r|o|u|g|h| +0#0000000&@60
+@75
+|p|a|s@1|t|h|r|o|u|g|h| |<+0#af5f00255&| |c|m|d| +0#0000000&|++0#af5f00255&| +0#0000000&|s+0#af5f00255&|h|i|f|t| +0#0000000&|++0#af5f00255&| +0#0000000&|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|p| |;+0#af5f00255&| +0#0000000&|d|e|f|a|u|l|t| @29
+@75
+>#+0#0000e05&| |A|p@1|l|i|c|a|t|i|o|n|-|s|p|e|c|i|f|i|c| |b|i|n|d|i|n|g|s| +0#0000000&@43
+|a+0#af5f00255&|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|t| |[+0#af5f00255&| +0#0000000&@65
+@4|"+0#e000002&|i|T|e|r|m|2|"| +0#0000000&|:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|i+0#e000002&|T|e|r|m|2| |s|p|e|c|i|f|i|c|"+0#af5f00255&| +0#0000000&@37
+@4|"+0#e000002&|G|o@1|g|l|e| |C|h|r|o|m|e|"| +0#0000000&|:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|C+0#e000002&|h|r|o|m|e| |s|p|e|c|i|f|i|c|"+0#af5f00255&| +0#0000000&@30
+@4|*+0#e000e06&| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|p|e|n| |-+0#e000e06&|a| +0#0000000&|"+0#af5f00255&|T+0#e000002&|e|r|m|i|n|a|l|"+0#af5f00255&| +0#0000000&@48
+|]+0#af5f00255&| +0#0000000&@73
+@75
+|c+0#af5f00255&|t|r|l| +0#0000000&|-+0#af5f00255&| +0#0000000&|n| |[+0#af5f00255&| +0#0000000&@64
+@4|"+0#e000002&|F|i|n|d|e|r|"| +0#0000000&|:+0#af5f00255&| |o+0#0000000&|p|e|n| |-+0#e000e06&|a| +0#0000000&|"+0#af5f00255&|N+0#e000002&|o|t|e|s|"+0#af5f00255&| +0#0000000&@44
+@4|*| |~+0#e000e06&| +0#0000000&@67
+|]+0#af5f00255&| +0#0000000&@73
+@75
+|#+0#0000e05&| |.|b|l|a|c|k|l|i|s|t| |d|i|r|e|c|t|i|v|e| +0#0000000&@52
+|.|b|l|a|c|k|l|i|s|t| |[| @62
+@57|5@1|,|1| @9|5|7|%|
diff --git a/runtime/syntax/testdir/dumps/skhdrc_04.dump b/runtime/syntax/testdir/dumps/skhdrc_04.dump
new file mode 100644
index 000000000..d048cac1a
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/skhdrc_04.dump
@@ -0,0 +1,20 @@
+|.+0&#ffffff0|b|l|a|c|k|l|i|s|t| |[| @62
+@4|"+0#e000002&|A|l|f|r|e|d|"| +0#0000000&@62
+@4|"+0#e000002&|1|P|a|s@1|w|o|r|d|"| +0#0000000&@59
+|]| @73
+@75
+>#+0#0000e05&| |.|l|o|a|d| |d|i|r|e|c|t|i|v|e| +0#0000000&@57
+|.|l|o|a|d| |"+0#e000002&|/|p|a|t|h|/|t|o|/|o|t|h|e|r|/|s|k|h|d|r|c|"| +0#0000000&@45
+@75
+|#+0#0000e05&| |M|u|l|t|i|-|l|i|n|e| |c|o|m@1|a|n|d| |w|i|t|h| |b|a|c|k|s|l|a|s|h| |c|o|n|t|i|n|u|a|t|i|o|n| +0#0000000&@26
+|a+0#af5f00255&|l|t| +0#0000000&|++0#af5f00255&| +0#0000000&|s+0#af5f00255&|h|i|f|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|m| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|t|o|g@1|l|e| +0#0000000&|z|o@1|m|-|f|u|l@1|s|c|r|e@1|n|;+0#af5f00255&| +0#0000000&|\+0#af5f00255&| +0#0000000&@13
+@18|y|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|t|o|g@1|l|e| +0#0000000&|b|o|r|d|e|r| @25
+@75
+|#+0#0000e05&| |C|o|m|p|l|e|x| |b|i|n|d|i|n|g| |w|i|t|h| |s|u|b|c|o|m@1|a|n|d|s| +0#0000000&@40
+|h+0#af5f00255&|y|p|e|r| +0#0000000&|-+0#af5f00255&| +0#0000000&|1| |:+0#af5f00255&| |y+0#0000000&|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|w|i|n|d|o|w| |-+0#e000e06&@1|s|p|a|c|e| +0#0000000&|1+0#e000002&|;+0#af5f00255&| +0#0000000&|\+0#af5f00255&| +0#0000000&@34
+@12|y|a|b|a|i| |-+0#e000e06&|m| +0#0000000&|s|p|a|c|e| |-+0#e000e06&@1|f|o|c|u|s| +0#0000000&|1+0#e000002&| +0#0000000&@38
+@75
+|#+0#0000e05&| |U|s|i|n|g| |l|a|l|t|,| |r|a|l|t|,| |l|c|m|d|,| |r|c|m|d|,| |l|s|h|i|f|t|,| |r|s|h|i|f|t|,| |l|c|t|r|l|,| |r|c|t|r|l| +0#0000000&@14
+|l+0#af5f00255&|a|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|a| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|l+0#e000002&|e|f|t| |a|l|t|"+0#af5f00255&| +0#0000000&@48
+|r+0#af5f00255&|a|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|a| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|r+0#e000002&|i|g|h|t| |a|l|t|"+0#af5f00255&| +0#0000000&@47
+@57|7|3|,|1| @9|7|8|%|
diff --git a/runtime/syntax/testdir/dumps/skhdrc_05.dump b/runtime/syntax/testdir/dumps/skhdrc_05.dump
new file mode 100644
index 000000000..14534db06
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/skhdrc_05.dump
@@ -0,0 +1,20 @@
+|r+0#af5f00255#ffffff0|a|l|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|a| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|r+0#e000002&|i|g|h|t| |a|l|t|"+0#af5f00255&| +0#0000000&@47
+|l+0#af5f00255&|c|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|b| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|l+0#e000002&|e|f|t| |c|m|d|"+0#af5f00255&| +0#0000000&@48
+|r+0#af5f00255&|c|m|d| +0#0000000&|-+0#af5f00255&| +0#0000000&|b| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|r+0#e000002&|i|g|h|t| |c|m|d|"+0#af5f00255&| +0#0000000&@47
+|l+0#af5f00255&|s|h|i|f|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|c| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|l+0#e000002&|e|f|t| |s|h|i|f|t|"+0#af5f00255&| +0#0000000&@44
+|r+0#af5f00255&|s|h|i|f|t| +0#0000000&|-+0#af5f00255&| +0#0000000&|c| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|r+0#e000002&|i|g|h|t| |s|h|i|f|t|"+0#af5f00255&| +0#0000000&@43
+>l+0#af5f00255&|c|t|r|l| +0#0000000&|-+0#af5f00255&| +0#0000000&|d| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|l+0#e000002&|e|f|t| |c|t|r|l|"+0#af5f00255&| +0#0000000&@46
+|r+0#af5f00255&|c|t|r|l| +0#0000000&|-+0#af5f00255&| +0#0000000&|d| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|r+0#e000002&|i|g|h|t| |c|t|r|l|"+0#af5f00255&| +0#0000000&@45
+@75
+|#+0#0000e05&| |M|e|h| |a|n|d| |h|y|p|e|r| |m|o|d|i|f|i|e|r|s| +0#0000000&@49
+|m+0#af5f00255&|e|h| +0#0000000&|-+0#af5f00255&| +0#0000000&|x| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|m+0#e000002&|e|h| |(|c|t|r|l|+|a|l|t|+|s|h|i|f|t|)|"+0#af5f00255&| +0#0000000&@37
+|h+0#af5f00255&|y|p|e|r| +0#0000000&|-+0#af5f00255&| +0#0000000&|y| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|h+0#e000002&|y|p|e|r| |(|c|t|r|l|+|a|l|t|+|s|h|i|f|t|+|c|m|d|)|"+0#af5f00255&| +0#0000000&@29
+@75
+|#+0#0000e05&| |A|d@1|i|t|i|o|n|a|l| |m|o|d|i|f|i|e|r|s|:| |o|p|t|i|o|n| |a|n|d| |s|u|p|e|r| |a|r|e| |a|l|i|a|s|e|s| +0#0000000&@22
+|o+0#af5f00255&|p|t|i|o|n| +0#0000000&|-+0#af5f00255&| +0#0000000&|z| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|o+0#e000002&|p|t|i|o|n| |i|s| |a|l|t| |a|l|i|a|s|"+0#af5f00255&| +0#0000000&@35
+|s+0#af5f00255&|u|p|e|r| +0#0000000&|-+0#af5f00255&| +0#0000000&|z| |:+0#af5f00255&| |e|c|h|o| +0#e000002&|"+0#af5f00255&|s+0#e000002&|u|p|e|r| |i|s| |c|m|d| |a|l|i|a|s|"+0#af5f00255&| +0#0000000&@37
+@75
+|#+0#0000e05&| |E|n|d| |o|f| |c|o|n|f|i|g|u|r|a|t|i|o|n| +0#0000000&@52
+@75
+|#+0#0000e05&| |v|i|m|:|f|t|=|s|k|h|d|:| +0#0000000&@60
+@57|9|1|,|1| @9|B|o|t|
diff --git a/runtime/syntax/testdir/input/skhdrc.skhdrc b/runtime/syntax/testdir/input/skhdrc.skhdrc
new file mode 100644
index 000000000..a33e9c3dd
--- /dev/null
+++ b/runtime/syntax/testdir/input/skhdrc.skhdrc
@@ -0,0 +1,104 @@
+# skhd configuration file
+# This is a comment
+
+# Basic key bindings with modifiers
+alt - h : yabai -m window --focus west
+shift + cmd - j : yabai -m window --focus south
+ctrl + alt + shift - k : yabai -m window --focus north
+fn - l : yabai -m window --focus east
+
+# Using hex keycodes
+cmd - 0x2A : open -a "Finder"
+
+# Function keys
+cmd - f1 : echo "F1 pressed"
+alt - f12 : echo "F12 pressed"
+
+# Special keys
+cmd - return : open -a "Terminal"
+alt - tab : yabai -m window --focus recent
+ctrl - escape : yabai -m space --balance
+shift - delete : rm -rf ~/.Trash/*
+cmd - space : open -a "Spotlight"
+
+# Arrow keys
+alt - left : yabai -m window --focus west
+alt - right : yabai -m window --focus east
+alt - up : yabai -m window --focus north
+alt - down : yabai -m window --focus south
+
+# Media keys
+cmd - sound_up : osascript -e "set volume output volume 100"
+cmd - mute : osascript -e "set volume output muted true"
+cmd - play : osascript -e "tell app \"Music\" to playpause"
+cmd - brightness_up : echo "brightness up"
+
+# yabai subcommands
+alt - w : yabai -m window --close
+alt - s : yabai -m space --create
+alt - d : yabai -m display --focus next
+
+# Mode definitions
+:: default : yabai -m config active_window_border_color 0xff775759
+:: resize @ : yabai -m config active_window_border_color 0xffd75f5f
+
+# Mode switching
+default < alt - r ; resize
+resize < escape ; default
+resize < alt - r ; default
+
+# Mode with passthrough
+:: passthrough
+
+passthrough < cmd + shift + alt - p ; default
+
+# Application-specific bindings
+alt - t [
+ "iTerm2" : echo "iTerm2 specific"
+ "Google Chrome" : echo "Chrome specific"
+ * : open -a "Terminal"
+]
+
+ctrl - n [
+ "Finder" : open -a "Notes"
+ * ~
+]
+
+# .blacklist directive
+.blacklist [
+ "Alfred"
+ "1Password"
+]
+
+# .load directive
+.load "/path/to/other/skhdrc"
+
+# Multi-line command with backslash continuation
+alt + shift - m : yabai -m window --toggle zoom-fullscreen; \
+ yabai -m window --toggle border
+
+# Complex binding with subcommands
+hyper - 1 : yabai -m window --space 1; \
+ yabai -m space --focus 1
+
+# Using lalt, ralt, lcmd, rcmd, lshift, rshift, lctrl, rctrl
+lalt - a : echo "left alt"
+ralt - a : echo "right alt"
+lcmd - b : echo "left cmd"
+rcmd - b : echo "right cmd"
+lshift - c : echo "left shift"
+rshift - c : echo "right shift"
+lctrl - d : echo "left ctrl"
+rctrl - d : echo "right ctrl"
+
+# Meh and hyper modifiers
+meh - x : echo "meh (ctrl+alt+shift)"
+hyper - y : echo "hyper (ctrl+alt+shift+cmd)"
+
+# Additional modifiers: option and super are aliases
+option - z : echo "option is alt alias"
+super - z : echo "super is cmd alias"
+
+# End of configuration
+
+# vim:ft=skhd:
diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim
index 078019bc5..4bb92028a 100644
--- a/src/testdir/test_filetype.vim
+++ b/src/testdir/test_filetype.vim
@@ -741,6 +741,7 @@ def s:GetFilenameChecks(): dict<list<string>>
simula: ['file.sim'],
sinda: ['file.sin', 'file.s85'],
sisu: ['file.sst', 'file.ssm', 'file.ssi', 'file.-sst', 'file._sst', 'file.sst.meta', 'file.-sst.meta', 'file._sst.meta'],
+ skhd: ['.skhdrc', 'skhdrc'],
skill: ['
file.il', 'file.ils', 'file.cdf'],
slang: ['
file.sl'],
slice: ['file.ice'],
diff --git a/src/version.c b/src/version.c
index f97ecf4fa..35e3c09a0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 2110,
/**/
2109,
/**/