odin-mode.el (9282B)
1 ;;; odin-mode.el --- A minor mode for odin 2 3 ;; Author: Ethan Morgan 4 ;; Keywords: odin, language, languages, mode 5 ;; Package-Requires: ((emacs "24.1")) 6 ;; Homepage: https://github.com/glassofethanol/odin-mode 7 8 ;; This file is NOT part of GNU Emacs. 9 10 ;;; Code: 11 12 (require 'cl-lib) 13 (require 'rx) 14 (require 'js) 15 16 (defgroup odin nil 17 "Odin mode" 18 :group 'languages) 19 20 ;; `compilation-mode' configuration 21 22 (eval-after-load 'compile 23 '(add-to-list 'compilation-error-regexp-alist '("^\\(.*?\\)(\\([0-9]+\\):\\([0-9]+\\).*" 1 2 3))) 24 25 (defconst odin-mode-syntax-table 26 (let ((table (make-syntax-table))) 27 (modify-syntax-entry ?\" "\"" table) 28 (modify-syntax-entry ?\\ "\\" table) 29 30 ;; additional symbols 31 (modify-syntax-entry ?' "\"" table) 32 (modify-syntax-entry ?` "\"" table) 33 (modify-syntax-entry ?: "." table) 34 (modify-syntax-entry ?+ "." table) 35 (modify-syntax-entry ?- "." table) 36 (modify-syntax-entry ?% "." table) 37 (modify-syntax-entry ?& "." table) 38 (modify-syntax-entry ?| "." table) 39 (modify-syntax-entry ?^ "." table) 40 (modify-syntax-entry ?! "." table) 41 (modify-syntax-entry ?$ "." table) 42 (modify-syntax-entry ?= "." table) 43 (modify-syntax-entry ?< "." table) 44 (modify-syntax-entry ?> "." table) 45 (modify-syntax-entry ?? "." table) 46 47 ;; Need this for #directive regexes to work correctly 48 (modify-syntax-entry ?# "_" table) 49 50 ;; Modify some syntax entries to allow nested block comments 51 (modify-syntax-entry ?/ ". 124b" table) 52 (modify-syntax-entry ?* ". 23n" table) 53 (modify-syntax-entry ?\n "> b" table) 54 (modify-syntax-entry ?\^m "> b" table) 55 56 table)) 57 58 (defconst odin-builtins 59 '("len" "cap" 60 "typeid_of" "type_info_of" 61 "swizzle" "complex" "real" "imag" "quaternion" "conj" 62 "jmag" "kmag" 63 "min" "max" "abs" "clamp" 64 "expand_to_tuple" 65 66 "init_global_temporary_allocator" 67 "copy" "pop" "unordered_remove" "ordered_remove" "clear" "reserve" 68 "resize" "new" "new_clone" "free" "free_all" "delete" "make" 69 "clear_map" "reserve_map" "delete_key" "append_elem" "append_elems" 70 "append" "append_string" "clear_dynamic_array" "reserve_dynamic_array" 71 "resize_dynamic_array" "incl_elem" "incl_elems" "incl_bit_set" 72 "excl_elem" "excl_elems" "excl_bit_set" "incl" "excl" "card" 73 "assert" "panic" "unimplemented" "unreachable")) 74 75 (defconst odin-keywords 76 '("import" "foreign" "package" 77 "where" "when" "if" "else" "for" "switch" "in" "notin" "do" "case" 78 "break" "continue" "fallthrough" "defer" "return" "proc" 79 "struct" "union" "enum" "bit_field" "bit_set" "map" "dynamic" 80 "auto_cast" "cast" "transmute" "distinct" "opaque" 81 "using" "inline" "no_inline" 82 "size_of" "align_of" "offset_of" "type_of" 83 84 "context" 85 ;; "_" 86 87 ;; Reserved 88 "macro" "const")) 89 90 (defconst odin-constants 91 '("nil" "true" "false" 92 "ODIN_OS" "ODIN_ARCH" "ODIN_ENDIAN" "ODIN_VENDOR" 93 "ODIN_VERSION" "ODIN_ROOT" "ODIN_DEBUG")) 94 95 (defconst odin-typenames 96 '("bool" "b8" "b16" "b32" "b64" 97 98 "int" "i8" "i16" "i32" "i64" 99 "i16le" "i32le" "i64le" 100 "i16be" "i32be" "i64be" 101 "i128" "u128" 102 "i128le" "u128le" 103 "i128be" "u128be" 104 105 "uint" "u8" "u16" "u32" "u64" 106 "u16le" "u32le" "u64le" 107 "u16be" "u32be" "u64be" 108 109 "f32" "f64" 110 "complex64" "complex128" 111 112 "quaternion128" "quaternion256" 113 114 "rune" 115 "string" "cstring" 116 117 "uintptr" "rawptr" 118 "typeid" "any" 119 "byte")) 120 121 (defconst odin-attributes 122 '("builtin" 123 "export" 124 "static" 125 "deferred_in" "deferred_none" "deferred_out" 126 "require_results" 127 "default_calling_convention" "link_name" "link_prefix" 128 "deprecated" "private" "thread_local")) 129 130 131 (defconst odin-proc-directives 132 '("#force_inline" 133 "#force_no_inline" 134 "#type") 135 "Directives that can appear before a proc declaration") 136 137 (defconst odin-directives 138 (append '("#align" "#packed" 139 "#any_int" 140 "#raw_union" 141 "#no_nil" 142 "#complete" 143 "#no_alias" 144 "#c_vararg" 145 "#assert" 146 "#file" "#line" "#location" "#procedure" "#caller_location" 147 "#load" 148 "#defined" 149 "#bounds_check" "#no_bounds_check" 150 "#partial") odin-proc-directives)) 151 152 (defun odin-wrap-word-rx (s) 153 (concat "\\<" s "\\>")) 154 155 (defun odin-wrap-keyword-rx (s) 156 (concat "\\(?:\\S.\\_<\\|\\`\\)" s "\\_>")) 157 158 (defun odin-wrap-directive-rx (s) 159 (concat "\\_<" s "\\>")) 160 161 (defun odin-wrap-attribute-rx (s) 162 (concat "[[:space:]\n]*@[[:space:]\n]*(?[[:space:]\n]*" s "\\>")) 163 164 (defun odin-keywords-rx (keywords) 165 "build keyword regexp" 166 (odin-wrap-keyword-rx (regexp-opt keywords t))) 167 168 (defun odin-directives-rx (directives) 169 (odin-wrap-directive-rx (regexp-opt directives t))) 170 171 (defun odin-attributes-rx (attributes) 172 (odin-wrap-attribute-rx (regexp-opt attributes t))) 173 174 (defconst odin-identifier-rx "[[:word:][:multibyte:]_]+") 175 (defconst odin-hat-type-rx (rx (group (and "^" (1+ (any word "." "_")))))) 176 (defconst odin-dollar-type-rx (rx (group "$" (or (1+ (any word "_")) (opt "$"))))) 177 (defconst odin-number-rx 178 (rx (and 179 symbol-start 180 (or (and (+ digit) (opt (and (any "eE") (opt (any "-+")) (+ digit)))) 181 (and "0" (any "xX") (+ hex-digit))) 182 (opt (and (any "_" "A-Z" "a-z") (* (any "_" "A-Z" "a-z" "0-9")))) 183 symbol-end))) 184 (defconst odin-proc-rx (concat "\\(\\_<" odin-identifier-rx "\\_>\\)\\s *::\\s *\\(" (odin-directives-rx odin-proc-directives) "\\)?\\s *\\_<proc\\_>")) 185 186 (defconst odin-type-rx (concat "\\_<\\(" odin-identifier-rx "\\)\\s *::\\s *\\(?:struct\\|enum\\|union\\|distinct\\)\\s *\\_>")) 187 188 189 (defconst odin-font-lock-defaults 190 `( 191 ;; Types 192 (,odin-hat-type-rx 1 font-lock-type-face) 193 (,odin-dollar-type-rx 1 font-lock-type-face) 194 (,(odin-keywords-rx odin-typenames) 1 font-lock-type-face) 195 (,odin-type-rx 1 font-lock-type-face) 196 197 ;; Hash directives 198 (,(odin-directives-rx odin-directives) 1 font-lock-preprocessor-face) 199 200 ;; At directives 201 (,(odin-attributes-rx odin-attributes) 1 font-lock-preprocessor-face) 202 203 ;; Keywords 204 (,(odin-keywords-rx odin-keywords) 1 font-lock-keyword-face) 205 206 ;; single quote characters 207 ("'\\(\\\\.\\|[^']\\)'" . font-lock-constant-face) 208 209 ;; Variables 210 (,(odin-keywords-rx odin-builtins) 1 font-lock-builtin-face) 211 212 ;; Constants 213 (,(odin-keywords-rx odin-constants) 1 font-lock-constant-face) 214 215 ;; Strings 216 ;; ("\\\".*\\\"" . font-lock-string-face) 217 218 ;; Numbers 219 (,(odin-wrap-word-rx odin-number-rx) . font-lock-constant-face) 220 221 ;; Procedures 222 (,odin-proc-rx 1 font-lock-function-name-face) 223 224 ("---" . font-lock-constant-face) 225 ("\\.\\.<" . font-lock-constant-face) 226 ("\\.\\." . font-lock-constant-face) 227 )) 228 229 ;; add setq-local for older emacs versions 230 (unless (fboundp 'setq-local) 231 (defmacro setq-local (var val) 232 `(set (make-local-variable ',var) ,val))) 233 234 (defconst odin--defun-rx "\(.*\).*\{") 235 236 (defmacro odin-paren-level () 237 `(car (syntax-ppss))) 238 239 (defun odin-line-is-defun () 240 "return t if current line begins a procedure" 241 (interactive) 242 (save-excursion 243 (beginning-of-line) 244 (let (found) 245 (while (and (not (eolp)) (not found)) 246 (if (looking-at odin--defun-rx) 247 (setq found t) 248 (forward-char 1))) 249 found))) 250 251 (defun odin-beginning-of-defun (&optional count) 252 "Go to line on which current function starts." 253 (interactive) 254 (let ((orig-level (odin-paren-level))) 255 (while (and 256 (not (odin-line-is-defun)) 257 (not (bobp)) 258 (> orig-level 0)) 259 (setq orig-level (odin-paren-level)) 260 (while (>= (odin-paren-level) orig-level) 261 (skip-chars-backward "^{") 262 (backward-char)))) 263 (if (odin-line-is-defun) 264 (beginning-of-line))) 265 266 (defun odin-end-of-defun () 267 "Go to line on which current function ends." 268 (interactive) 269 (let ((orig-level (odin-paren-level))) 270 (when (> orig-level 0) 271 (odin-beginning-of-defun) 272 (end-of-line) 273 (setq orig-level (odin-paren-level)) 274 (skip-chars-forward "^}") 275 (while (>= (odin-paren-level) orig-level) 276 (skip-chars-forward "^}") 277 (forward-char))))) 278 279 (defalias 'odin-parent-mode 280 (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode)) 281 282 ;;;###autoload 283 (define-derived-mode odin-mode odin-parent-mode "Odin" 284 :syntax-table odin-mode-syntax-table 285 :group 'odin 286 (setq bidi-paragraph-direction 'left-to-right) 287 (setq-local require-final-newline mode-require-final-newline) 288 (setq-local parse-sexp-ignore-comments t) 289 (setq-local comment-start-skip "\\(//+\\|/\\*+\\)\\s *") 290 (setq-local comment-start "//") 291 (setq-local comment-end "") 292 (setq-local indent-line-function 'js-indent-line) 293 (setq-local font-lock-defaults '(odin-font-lock-defaults)) 294 (setq-local beginning-of-defun-function 'odin-beginning-of-defun) 295 (setq-local end-of-defun-function 'odin-end-of-defun) 296 (setq-local electric-indent-chars 297 (append "{}():;," electric-indent-chars)) 298 (setq imenu-generic-expression 299 `(("type" ,(concat "^" odin-type-rx) 1) 300 ("proc" ,(concat "^" odin-proc-rx) 1))) 301 302 (font-lock-ensure)) 303 304 ;;;###autoload 305 (add-to-list 'auto-mode-alist '("\\.odin\\'" . odin-mode)) 306 307 (provide 'odin-mode) 308 309 310 ;;; odin-mode.el ends here