auto-update-daily-20260202
1;;; kanata-kbd-mode.el --- Major mode for editing Kanata .kbd configuration files -*- lexical-binding: t; -*-
2
3;;; Commentary:
4;; This package provides a major mode for editing Kanata .kbd files,
5;; offering syntax highlighting, basic indentation, and comment support.
6;; Kanata is a software keyboard remapper.
7;;
8;; To use, save this file as kanata-kbd-mode.el in your load-path,
9;; and add (require 'kanata-kbd-mode) to your init.el.
10;; Files ending in .kbd will automatically use this mode.
11
12;;; Code:
13
14(defvar kanata-kbd-mode-syntax-table
15 (let ((table (make-syntax-table)))
16 ;; Treat ; as comment starter
17 (modify-syntax-entry ?\; "<" table)
18 (modify-syntax-entry ?\n ">" table)
19 ;; Parentheses for S-expression like structure
20 (modify-syntax-entry ?\( "()" table)
21 (modify-syntax-entry ?\) ")(" table)
22 ;; Treat symbols as word constituents
23 (modify-syntax-entry ?_ "w" table)
24 (modify-syntax-entry ?- "w" table)
25 (modify-syntax-entry ?@ "w" table) ; For aliases like @mykey
26 (modify-syntax-entry ?& "w" table) ; For macros like &mymacro
27 (modify-syntax-entry ?+ "." table) ; Treat + as punctuation for specific highlighting
28 table)
29 "Syntax table for `kanata-kbd-mode`.")
30
31;; Keywords and constants for font-locking
32(defconst kanata-kbd-top-level-directives
33 '("defcfg" "defsrc" "deflayer" "defalias" "defapp")
34 "Top-level directives in Kanata .kbd files.")
35
36(defconst kanata-kbd-defcfg-keywords
37 '("startup-layer" "tap-hold-timeout" "oneshot-timeout" "fallthrough"
38 "process_unmapped_keys" "remap_win_key_to_lmet_for_macos"
39 "leader-timeout" "leader-global-timeout" "leader-sequences"
40 "compose-timeout" "compose-key" "compose-sequences"
41 "unicode-mode-key" "unicode-timeout" "unicode-default-hex-digits"
42 "experimental_cmd_allow_config_dir_relative_paths")
43 "Keywords used within (defcfg ...).")
44
45(defconst kanata-kbd-defapp-keywords
46 '("exec" "title" "class")
47 "Keywords used within (defapp ...).")
48
49
50(defconst kanata-kbd-action-keywords
51 '(;; Layer actions
52 "layer-toggle" "layer-switch" "layer-clear" "layer-while-held" "layer-switch-when-held"
53 "layer-tap-hold" "layer-tap-dance"
54 ;; Macros & Multi
55 "macro" "macro2" "macro-tap-hold" "multi" "release-key"
56 ;; Modifiers and special keys
57 "oneshot" "sticky" "transparent" "_" "unmapped"
58 ;; Tap-hold and tap-dance
59 "tap-hold" "tap-dance"
60 ;; Commands
61 "cmd" "cmd-async"
62 ;; Unicode
63 "unicode" "unicode-hex"
64 ;; Special actions
65 "leader" "compose" "toggle-unicode-mode" "reset-sticky-keys"
66 "noexplicit" ; Used with tap-hold for example
67 )
68 "Special action keywords in Kanata.")
69
70(defconst kanata-kbd-common-key-names
71 '(;; Standard keys
72 "esc" "f1" "f2" "f3" "f4" "f5" "f6" "f7" "f8" "f9" "f10" "f11" "f12"
73 "grv" "1" "2" "3" "4" "5" "6" "7" "8" "9" "0" "min" "eq" "bspc"
74 "tab" "q" "w" "e" "r" "t" "y" "u" "i" "o" "p" "lbrc" "rbrc" "bsls" "bksl" ; Added bksl
75 "caps" "a" "s" "d" "f" "g" "h" "j" "k" "l" "scln" "quot" "ret"
76 "lsft" "z" "x" "c" "v" "b" "n" "m" "comm" "dot" "slsh" "rsft"
77 "lctl" "lmet" "lalt" "spc" "ralt" "rmet" "rctl" "fn" ; Added fn from image context
78 ;; Navigation and editing
79 "ins" "home" "pgup" "del" "end" "pgdn"
80 "up" "left" "down" "right" "rght" ; Added rght as potential alias/typo for right
81 ;; Numpad
82 "nlck" "kp/" "kp*" "kp-" "kp+" "kp." "kp0" "kp1" "kp2" "kp3" "kp4"
83 "kp5" "kp6" "kp7" "kp8" "kp9" "kpenter"
84 ;; Media keys
85 "mute" "volu" "vold" "prev" "next" "play" "stop" "pp" ; Added pp
86 ;; Special keys from image
87 "nonusbslash" "brdn" "brup"
88 ;; Special
89 "C" "S" "A" "M" "W" ; Modifier shorthands like C-a, S-b
90 "lC" "lS" "lA" "lM" "lW" ; Left specific modifier shorthands
91 "rC" "rS" "rA" "rM" "rW" ; Right specific modifier shorthands
92 )
93 "Common key names in Kanata. This list is not exhaustive.")
94
95(defconst kanata-kbd-font-lock-keywords
96 (let* ((top-level-directives-regexp (regexp-opt kanata-kbd-top-level-directives 'symbols))
97 (defcfg-keywords-regexp (regexp-opt kanata-kbd-defcfg-keywords 'symbols))
98 (defapp-keywords-regexp (regexp-opt kanata-kbd-defapp-keywords 'symbols))
99 (action-keywords-regexp (regexp-opt kanata-kbd-action-keywords 'symbols))
100 (common-keys-regexp (regexp-opt kanata-kbd-common-key-names 'symbols))
101 (symbol-name-regexp "\\(?:\\sw\\|\\s_\\)+")
102 (layer-name-after-directive-regexp (concat "\\(?:" top-level-directives-regexp "\\)\\s-+\\(\\(?:" symbol-name-regexp "\\)\\)"))
103 (alias-name-after-defalias-regexp (concat "\\(?:defalias\\)\\s-+\\(\\(?:" symbol-name-regexp "\\)\\)"))
104 ;; Specific regex for @aliases
105 (at-alias-usage-regexp (concat "@\\(" symbol-name-regexp "\\)"))
106 ;; Specific regex for ¯os (if you use them differently)
107 (amp-macro-usage-regexp (concat "&\\(" symbol-name-regexp "\\)")))
108
109 `(;; Comments (anything after ;)
110 (";.+" . font-lock-comment-face)
111
112 ;; Top-level directives
113 (,top-level-directives-regexp . font-lock-keyword-face)
114
115 ;; Keywords within (defcfg ...)
116 (,defcfg-keywords-regexp . font-lock-type-face)
117
118 ;; Keywords within (defapp ...)
119 (,defapp-keywords-regexp . font-lock-type-face)
120
121 ;; Action keywords
122 (,action-keywords-regexp . font-lock-builtin-face)
123
124 ;; Layer names after deflayer, or first argument to startup-layer etc.
125 (,layer-name-after-directive-regexp 1 font-lock-variable-name-face)
126
127 ;; Alias definition name (after defalias)
128 (,alias-name-after-defalias-regexp 1 font-lock-variable-name-face)
129
130 ;; @alias usage (e.g., @mykey) - highlight as function names
131 (,at-alias-usage-regexp 1 font-lock-function-name-face)
132
133 ;; ¯o usage (e.g., &mymacro) - highlight as constants (or choose another face)
134 (,amp-macro-usage-regexp 1 font-lock-constant-face)
135
136 ;; Common key names
137 (,(concat "\\<" common-keys-regexp "\\>") . font-lock-constant-face)
138
139 ;; The '+' symbol when used between key names, primarily in defsrc
140 (,(concat "\\(?:\\<" common-keys-regexp "\\>\\|\\(?:\\sw\\|\\s_\\)+\\)" ; A key or symbol
141 "\\s-*\\(\\+\\)\\s-*" ; The + operator (captured)
142 "\\(?:\\<" common-keys-regexp "\\>\\|\\(?:\\sw\\|\\s_\\)+\\)") ; Another key or symbol
143 1 font-lock-warning-face) ; Highlight '+' distinctively
144
145 ;; Modifier prefixes like C-, S-, A-, M-, W- when followed by a key
146 ("\\([CSAMW]\\(?:[lrCSAMW]\\)*\\)-\\(\\w+\\)" ; Allow stacked modifiers like C-S-a
147 (1 font-lock-preprocessor-face) ; The modifier prefix
148 (2 font-lock-constant-face)) ; The key itself
149
150 ;; Strings (e.g., for application names in defapp, paths in cmd)
151 ("\"\\(?:\\\\.[^\"]*\\|[^\"]\\)*\"" . font-lock-string-face)
152
153 ;; Numbers (e.g., for timeouts, macro steps)
154 ("\\b[0-9]+\\b" . font-lock-string-face) ; Using string face for visibility
155 ))
156 "Font lock keywords for `kanata-kbd-mode`.")
157
158
159;; Mode definition
160;;;###autoload
161(define-derived-mode kanata-kbd-mode prog-mode "KanataKBD"
162 "Major mode for editing Kanata .kbd configuration files."
163 :syntax-table kanata-kbd-mode-syntax-table
164 (setq-local font-lock-defaults '(kanata-kbd-font-lock-keywords))
165 (setq-local comment-start "; ")
166 (setq-local comment-start-skip ";+\\s-*"))
167
168;; Associate .kbd files with this mode
169;;;###autoload
170(add-to-list 'auto-mode-alist '("\\.kbd\\'" . kanata-kbd-mode))
171
172(provide 'kanata-kbd-mode)
173
174;;; kanata-kbd-mode.el ends here