bpt's domtool-mode auto-mode suggestion
[hcoop/domtool2.git] / elisp / domtool-mode.el
CommitLineData
4542571e
AC
1; Created by Brian Templeton (bpt@hcoop.net)
2; Extended by Adam Chlipala (adamc@hcoop.net)
3
4(eval-when-compile (require 'cl))
5
6(defvar domtool-indent 2)
7
8(defvar domtool-mode-syntax-table
9 (let ((table (make-syntax-table)))
10 (loop for i from ?a to ?z
11 do (modify-syntax-entry i "w" table))
12 (loop for i from ?A to ?Z
13 do (modify-syntax-entry i "w" table))
14 (loop for i from ?0 to ?9
15 do (modify-syntax-entry i "w" table))
16 (mapc
17 (lambda (pair)
18 (loop for ch across (if (stringp (car pair))
19 (car pair)
20 (string (car pair)))
21 do (modify-syntax-entry ch (cadr pair) table)))
22 '((" \t\n\14" " ") ; \14 is ^L
23 (?_ "_")
24 (?* ". 23n")
25 (?\( "()1")
26 (?\) ")(4")
27 (?\" "\"")
28 (?\\ "\\")
29 (?\[ "(]")
30 (?\] ")[")
31 ;; We identify single-line comments using
32 ;; font-lock-syntactic-keywords, because it's easier to
33 ;; recognize documentation comments using the syntax table.
34 (?\{ "(}12b")
35 (?\} "){34b")
36 ("->=<,:;^!&" ".") ; -> => <- = , : ; ^ ! &
37 ))
38 table))
39
ef18a741
AC
40(defun domtool-syms-re (&rest syms)
41 (concat "\\<" (regexp-opt syms t) "\\>"))
ef18a741 42
03b14a21 43(require 'domtool-tables)
ef18a741 44
4542571e
AC
45(defvar domtool-font-lock-keywords
46 `(,(concat
47 "\\_<"
48 (regexp-opt '("let" "in" "begin" "end" "with" "where" "extern" "type"
a356587a 49 "val" "context" "Root" "if" "then" "else")
4542571e
AC
50 t)
51 "\\_>")
ef18a741
AC
52
53 (,domtool-actions-regexp . font-lock-builtin-face)
54 (,domtool-vals-regexp . font-lock-variable-name-face)
55 (,domtool-contexts-regexp . font-lock-constant-face)
56 (,domtool-env-vars-regexp . font-lock-constant-face)
57 (,domtool-types-regexp . font-lock-type-face)
58
4542571e
AC
59 ("type[ \t]+\\(\\(\\sw\\|\\s_\\)+\\)" 1 font-lock-type-face)
60 ("val[ \t]+\\(\\(\\sw\\|\\s_\\)+\\)" 1 font-lock-variable-name-face)))
61
62(defvar domtool-font-lock-syntactic-keywords
63 '(("\\(#\\).*\\(\n\\|\\'\\)"
64 (1 "!")
65 (2 "!"))))
66
67(defun domtool-font-lock-syntactic-face-function (state)
68 "Major mode for editing Domtool files."
69 (cond ((nth 3 state) font-lock-string-face)
70 ((eq (nth 7 state) t) font-lock-doc-face)
71 (t font-lock-comment-face)))
72
73(define-derived-mode domtool-mode fundamental-mode "Domtool"
74 ;; For some reason, whatever twiddling `define-derived-mode' does
75 ;; with the syntax table breaks recognition of (*...*) comments. So
76 ;; we need to tell d-d-m to leave our syntax table alone.
77 :syntax-table domtool-mode-syntax-table
78 (set (make-local-variable 'indent-line-function) 'domtool-indent-line)
79 (set (make-local-variable 'font-lock-defaults)
80 '(domtool-font-lock-keywords
81 nil nil nil nil
82 (font-lock-syntactic-keywords
83 . domtool-font-lock-syntactic-keywords)
84 (font-lock-syntactic-face-function
e3989a67 85 . domtool-font-lock-syntactic-face-function)))
ae272cb5
AC
86 (set (make-local-variable 'comment-start-regexp) "(\\*\\|{{")
87 (set (make-local-variable 'comment-end-regexp) "\\*)\\|}}")
25aba3ae
AC
88 (set (make-local-variable 'comment-nested) t)
89
90 (set (make-local-variable 'compile-command)
91 (concat "domtool -tc " (file-relative-name buffer-file-name))))
4542571e
AC
92
93(defun domtool-indent-line ()
94 (let ((savep (> (current-column) (current-indentation)))
95 (indent (domtool-calculate-indent)))
96 (cond
97 ((eq indent 'noindent) indent)
98 (savep (save-excursion (indent-line-to indent)))
99 (t (indent-line-to indent)))))
100
3b37f60f 101(defun until-closed-helper (level)
1cbe9174 102 (if
6f0cf3a6 103 (re-search-backward "\\_<\\(with\\|where\\|begin\\|end\\|let\\|val\\|type\\|if\\)\\_>"
1cbe9174
AC
104 nil t)
105 (cond
106 ((string= (match-string 0) "end")
3b37f60f 107 (until-closed-helper (+ level 1)))
1cbe9174
AC
108 ((= level 0)
109 (current-indentation))
ae272cb5
AC
110 ((and
111 (string= (match-string 0) "with")
112 (save-excursion
113 (backward-char)
114 (looking-at "\\s-")))
115 (until-closed-helper level))
1cbe9174 116 (t
3b37f60f
AC
117 (until-closed-helper (- level 1))))
118
119 0))
1cbe9174 120
e5ea2657 121(defun until-closed ()
3b37f60f 122 (save-excursion
e5ea2657 123 (until-closed-helper 0)))
1cbe9174 124
4542571e
AC
125(defun domtool-calculate-indent ()
126 (save-excursion
127 (back-to-indentation)
128 (multiple-value-bind (previous-keyword base-indent)
129 (save-excursion
ae272cb5 130 (if (re-search-backward "\\_<\\(with\\|where\\|begin\\|end\\|let\\|in\\|val\\|type\\|if\\)\\_>\\|}}\\|{{"
4542571e
AC
131 nil t)
132 (values (match-string 0) (current-indentation))
133 (values nil 0)))
134 (let ((state (syntax-ppss)))
135 (cond
136 ((nth 3 state)
137 'noindent)
138 ((nth 4 state)
139 (domtool-calculate-comment-indent state))
ae272cb5
AC
140 ((looking-at "{{\\|\\_<\\(extern\\|val\\|type\\|context\\)\\_>")
141 0)
6f0cf3a6 142 ((looking-at "\\_<\\(with\\|end\\|in\\|else\\)\\_>")
e5ea2657 143 (until-closed))
4542571e
AC
144 ((not previous-keyword)
145 base-indent)
146 ((string= previous-keyword "end")
147 base-indent)
ae272cb5
AC
148 ((looking-at "\\_<\\(val\\|extern\\|context\\)\\_>")
149 base-indent)
4542571e
AC
150 (t
151 (+ base-indent domtool-indent)))))))
152
153(defun domtool-calculate-comment-indent (state)
154 (ecase (nth 7 state)
155 ((t) 'noindent)
156 ((syntax-table) 'noindent) ; can't happen
157 ((nil) (let ((start (nth 8 state))
158 (depth 0))
159 (while (> (point) start)
160 (re-search-backward "(\\*\\|\\*)" start t)
161 (if (looking-at "(\\*")
162 (incf depth)
163 (decf depth)))
164 (+ (current-indentation) depth)))))
03b14a21
AC
165
166(provide 'domtool-mode)