;;; 9ps-mode.el --- major mode for editing ninepscripts -*- lexical-binding: t; -*- ;; Copyright (C) 2021 Omar Polo ;; Author: Omar Polo ;; Keywords: languages ;; Permission to use, copy, modify, and distribute this software for any ;; purpose with or without fee is hereby granted, provided that the above ;; copyright notice and this permission notice appear in all copies. ;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ;; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ;; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ;; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ;; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ;; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ;; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ;;; Commentary: ;; Major mode for editing ninepscripts, the kamid regression tests. ;;; Code: (eval-when-compile (require 'rx)) (defconst 9ps--font-lock-defaults (let ((keywords '("assert" "const" "dir" "include" "proc" "should-fail" "testing" "vargs")) (types '("str" "u8" "u16" "u32"))) `(((,(rx-to-string `(: (or ,@keywords))) 0 font-lock-keyword-face) ("\\([[:word:]]+\\)\s*(" 1 font-lock-function-name-face) (,(rx-to-string `(: (or ,@types))) 0 font-lock-type-face))))) (defvar 9ps-mode-syntax-table (let ((st (make-syntax-table))) (modify-syntax-entry ?\{ "(}" st) (modify-syntax-entry ?\} "){" st) (modify-syntax-entry ?\( "()" st) ;; - and _ are word constituent (modify-syntax-entry ?_ "w" st) (modify-syntax-entry ?- "w" st) ;; both single and double quotes makes strings (modify-syntax-entry ?\" "\"" st) (modify-syntax-entry ?' "'" st) ;; one day we'll have escaping (maybe) (modify-syntax-entry ?\\ "\\" st) ;; add comments. lua-mode does something similar, so it shouldn't ;; bee *too* wrong. (modify-syntax-entry ?# "<" st) (modify-syntax-entry ?\n ">" st) ;; '==' as punctuation (modify-syntax-entry ?= ".") st)) (defun 9ps-indent-line () "Indent current line." (let (indent boi-p ;begin of indent move-eol-p (point (point))) (save-excursion (back-to-indentation) (setq indent (car (syntax-ppss)) boi-p (= point (point))) ;; don't indent empty lines if they don't have the in it (when (and (eq (char-after) ?\n) (not boi-p)) (setq indent 0)) ;; check whether we want to move to the end of line (when boi-p (setq move-eol-p t)) ;; decrement the indent if the first character on the line is a ;; closer. (when (or (eq (char-after) ?\)) (eq (char-after) ?\})) (setq indent (1- indent))) ;; indent the line (delete-region (line-beginning-position) (point)) (indent-to (* tab-width indent))) (when move-eol-p (move-end-of-line nil)))) (defvar 9ps-mode-abbrev-table nil "Abbreviation table used in `9ps-mode' buffers.") (define-abbrev-table '9ps-mode-abbrev-table '()) ;;;###autoload (define-derived-mode 9ps-mode prog-mode "9ps" "Major mode for ninepscript files." :abbrev-table 9ps-mode-abbrev-table (setq font-lock-defaults 9ps--font-lock-defaults) (setq-local comment-start "#") (setq-local comment-start-skip "#+[\t ]*") (setq-local indent-line-function #'9ps-indent-line) (setq-local indent-tabs-mode t)) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.9ps" . 9ps-mode)) (provide '9ps-mode) ;;; 9ps-mode.el ends here