flake-update-20260505
  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 &macros (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      ;; &macro 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