1 ;;; sml-mode.el --- Major mode for editing (Standard) ML
3 ;; Copyright (C) 1999,2000,2004,2007,2010 Stefan Monnier
4 ;; Copyright (C) 1994-1997 Matthew J. Morley
5 ;; Copyright (C) 1989 Lars Bo Nielsen
7 ;; Author: Lars Bo Nielsen
11 ;; Matthew Morley <mjm@scs.leeds.ac.uk> (aka <matthew@verisity.com>)
12 ;; Matthias Blume <blume@cs.princeton.edu> (aka <blume@kurims.kyoto-u.ac.jp>)
13 ;; (Stefan Monnier) <monnier@iro.umontreal.ca>
14 ;; Maintainer: (Stefan Monnier) <monnier@iro.umontreal.ca>
19 ;; This file is not part of GNU Emacs, but it is distributed under the
22 ;; This program is free software; you can redistribute it and/or
23 ;; modify it under the terms of the GNU General Public License as
24 ;; published by the Free Software Foundation; either version 3, or (at
25 ;; your option) any later version.
27 ;; This program is distributed in the hope that it will be useful, but
28 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
29 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 ;; General Public License for more details.
32 ;; You should have received a copy of the GNU General Public License
33 ;; along with GNU Emacs; see the file COPYING. If not, write to the
34 ;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
40 ;; Still under construction: History obscure, needs a biographer as
41 ;; well as a M-x doctor. Change Log on request.
43 ;; Hacked by Olin Shivers for comint from Lars Bo Nielsen's sml.el.
45 ;; Hacked by Matthew Morley to incorporate Fritz Knabe's hilite and
46 ;; font-lock patterns, some of Steven Gilmore's (reduced) easy-menus,
47 ;; and numerous bugs and bug-fixes.
51 ;; See accompanying info file: sml-mode.info
53 ;;; FOR YOUR .EMACS FILE
55 ;; If sml-mode.el lives in some non-standard directory, you must tell
56 ;; emacs where to get it. This may or may not be necessary:
58 ;; (add-to-list 'load-path "~jones/lib/emacs/")
60 ;; Then to access the commands autoload sml-mode with that command:
62 ;; (load "sml-mode-startup")
64 ;; sml-mode-hook is run whenever a new sml-mode buffer is created.
66 ;; Finally, there are inferior-sml-{mode,load}-hooks -- see comments
67 ;; in sml-proc.el. For much more information consult the mode's *info*
72 (eval-when-compile (require 'cl
))
76 (condition-case nil
(require 'skeleton
) (error nil
))
78 ;;; VARIABLES CONTROLLING INDENTATION
80 (defcustom sml-indent-level
4
81 "Indentation of blocks in ML (see also `sml-indent-rule')."
85 (defcustom sml-indent-args sml-indent-level
86 "*Indentation of args placed on a separate line."
90 ;; (defvar sml-indent-align-args t
91 ;; "*Whether the arguments should be aligned.")
93 ;; (defvar sml-case-indent nil
94 ;; "*How to indent case-of expressions.
95 ;; If t: case expr If nil: case expr of
96 ;; of exp1 => ... exp1 => ...
97 ;; | exp2 => ... | exp2 => ...
99 ;; The first seems to be the standard in SML/NJ, but the second
102 (defcustom sml-electric-semi-mode nil
103 "*If non-nil, `\;' will self insert, reindent the line, and do a newline.
104 If nil, just insert a `\;'. (To insert while t, do: \\[quoted-insert] \;)."
108 (defcustom sml-rightalign-and t
109 "If non-nil, right-align `and' with its leader.
111 datatype a = A datatype a = A
116 ;;; OTHER GENERIC MODE VARIABLES
118 (defvar sml-mode-info
"sml-mode"
119 "*Where to find Info file for `sml-mode'.
120 The default assumes the info file \"sml-mode.info\" is on Emacs' info
121 directory path. If it is not, either put the file on the standard path
122 or set the variable `sml-mode-info' to the exact location of this file
124 (setq sml-mode-info \"/usr/me/lib/info/sml-mode\")
126 in your .emacs file. You can always set it interactively with the
127 set-variable command.")
129 (defvar sml-mode-hook nil
130 "*Run upon entering `sml-mode'.
131 This is a good place to put your preferred key bindings.")
133 ;;; CODE FOR SML-MODE
135 (defun sml-mode-info ()
136 "Command to access the TeXinfo documentation for `sml-mode'.
137 See doc for the variable `sml-mode-info'."
143 (describe-variable 'sml-mode-info
)
144 (message "Can't find it... set this variable first!")))))
147 ;;; Autoload functions -- no-doc is another idea cribbed from AucTeX!
150 "This function is part of sml-proc, and has not yet been loaded.
151 Full documentation will be available after autoloading the function."))
153 (autoload 'sml-compile
"sml-proc" sml-no-doc t
)
154 (autoload 'sml-load-file
"sml-proc" sml-no-doc t
)
155 (autoload 'switch-to-sml
"sml-proc" sml-no-doc t
)
156 (autoload 'sml-send-region
"sml-proc" sml-no-doc t
)
157 (autoload 'sml-send-buffer
"sml-proc" sml-no-doc t
))
161 (defconst sml-keywords-regexp
162 (sml-syms-re '("abstraction" "abstype" "and" "andalso" "as" "before" "case"
163 "datatype" "else" "end" "eqtype" "exception" "do" "fn"
164 "fun" "functor" "handle" "if" "in" "include" "infix"
165 "infixr" "let" "local" "nonfix" "of" "op" "open" "orelse"
166 "overload" "raise" "rec" "sharing" "sig" "signature"
167 "struct" "structure" "then" "type" "val" "where" "while"
168 "with" "withtype" "o"))
169 "A regexp that matches any and all keywords of SML.")
171 (defconst sml-tyvarseq-re
172 "\\(\\('+\\(\\sw\\|\\s_\\)+\\|(\\([,']\\|\\sw\\|\\s_\\|\\s-\\)+)\\)\\s-+\\)?")
174 ;;; Font-lock settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
176 (defcustom sml-font-lock-symbols nil
177 "Display \\ and -> and such using symbols in fonts.
178 This may sound like a neat trick, but be extra careful: it changes the
179 alignment and can thus lead to nasty surprises w.r.t layout.
180 If t, try to use whichever font is available. Otherwise you can
181 set it to a particular font of your preference among `japanese-jisx0208'
183 :type
'(choice (const nil
)
186 (const japanese-jisx0208
)))
188 (defconst sml-font-lock-symbols-alist
190 ;; The symbols can come from a JIS0208 font.
191 (and (fboundp 'make-char
) (charsetp 'japanese-jisx0208
)
192 (memq sml-font-lock-symbols
'(t japanese-jisx0208
))
193 (list (cons "fn" (make-char 'japanese-jisx0208
38 75))
194 (cons "andalso" (make-char 'japanese-jisx0208
34 74))
195 (cons "orelse" (make-char 'japanese-jisx0208
34 75))
196 ;; (cons "as" (make-char 'japanese-jisx0208 34 97))
197 (cons "not" (make-char 'japanese-jisx0208
34 76))
198 (cons "div" (make-char 'japanese-jisx0208
33 96))
199 ;; (cons "*" (make-char 'japanese-jisx0208 33 95))
200 (cons "->" (make-char 'japanese-jisx0208
34 42))
201 (cons "=>" (make-char 'japanese-jisx0208
34 77))
202 (cons "<-" (make-char 'japanese-jisx0208
34 43))
203 (cons "<>" (make-char 'japanese-jisx0208
33 98))
204 (cons ">=" (make-char 'japanese-jisx0208
33 102))
205 (cons "<=" (make-char 'japanese-jisx0208
33 101))
206 (cons "..." (make-char 'japanese-jisx0208
33 68))
207 ;; Some greek letters for type parameters.
208 (cons "'a" (make-char 'japanese-jisx0208
38 65))
209 (cons "'b" (make-char 'japanese-jisx0208
38 66))
210 (cons "'c" (make-char 'japanese-jisx0208
38 67))
211 (cons "'d" (make-char 'japanese-jisx0208
38 68))
213 ;; Or a unicode font.
214 (and (fboundp 'decode-char
)
215 (memq sml-font-lock-symbols
'(t unicode
))
216 (list (cons "fn" (decode-char 'ucs
955))
217 (cons "andalso" (decode-char 'ucs
8896))
218 (cons "orelse" (decode-char 'ucs
8897))
219 ;; (cons "as" (decode-char 'ucs 8801))
220 (cons "not" (decode-char 'ucs
172))
221 (cons "div" (decode-char 'ucs
247))
222 (cons "*" (decode-char 'ucs
215))
223 (cons "o" (decode-char 'ucs
9675))
224 (cons "->" (decode-char 'ucs
8594))
225 (cons "=>" (decode-char 'ucs
8658))
226 (cons "<-" (decode-char 'ucs
8592))
227 (cons "<>" (decode-char 'ucs
8800))
228 (cons ">=" (decode-char 'ucs
8805))
229 (cons "<=" (decode-char 'ucs
8804))
230 (cons "..." (decode-char 'ucs
8943))
231 ;; (cons "::" (decode-char 'ucs 8759))
232 ;; Some greek letters for type parameters.
233 (cons "'a" (decode-char 'ucs
945))
234 (cons "'b" (decode-char 'ucs
946))
235 (cons "'c" (decode-char 'ucs
947))
236 (cons "'d" (decode-char 'ucs
948))
239 (defun sml-font-lock-compose-symbol (alist)
240 "Compose a sequence of ascii chars into a symbol.
241 Regexp match data 0 points to the chars."
242 ;; Check that the chars should really be composed into a symbol.
243 (let* ((start (match-beginning 0))
245 (syntaxes (if (eq (char-syntax (char-after start
)) ?w
)
247 (if (or (memq (char-syntax (or (char-before start
) ?\
)) syntaxes
)
248 (memq (char-syntax (or (char-after end
) ?\
)) syntaxes
)
249 (memq (get-text-property start
'face
)
250 '(font-lock-doc-face font-lock-string-face
251 font-lock-comment-face
)))
252 ;; No composition for you. Let's actually remove any composition
253 ;; we may have added earlier and which is now incorrect.
254 (remove-text-properties start end
'(composition))
255 ;; That's a symbol alright, so add the composition.
256 (compose-region start end
(cdr (assoc (match-string 0) alist
)))))
257 ;; Return nil because we're not adding any face property.
260 (defun sml-font-lock-symbols-keywords ()
261 (when (fboundp 'compose-region
)
263 (dolist (x sml-font-lock-symbols-alist
)
264 (when (and (if (fboundp 'char-displayable-p
)
265 (char-displayable-p (cdr x
))
267 (not (assoc (car x
) alist
))) ;Not yet in alist.
270 `((,(regexp-opt (mapcar 'car alist
) t
)
271 (0 (sml-font-lock-compose-symbol ',alist
))))))))
273 ;; The font lock regular expressions.
275 (defconst sml-font-lock-keywords
276 `(;;(sml-font-comments-and-strings)
277 (,(concat "\\<\\(fun\\|and\\)\\s-+" sml-tyvarseq-re
"\\(\\sw+\\)\\s-+[^ \t\n=]")
278 (1 font-lock-keyword-face
)
279 (6 font-lock-function-name-face
))
280 (,(concat "\\<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+" sml-tyvarseq-re
"\\(\\sw+\\)")
281 (1 font-lock-keyword-face
)
282 (7 font-lock-type-def-face
))
283 ("\\<\\(val\\)\\s-+\\(\\sw+\\>\\s-*\\)?\\(\\sw+\\)\\s-*[=:]"
284 (1 font-lock-keyword-face
)
285 ;;(6 font-lock-variable-def-face nil t)
286 (3 font-lock-variable-name-face
))
287 ("\\<\\(structure\\|functor\\|abstraction\\)\\s-+\\(\\sw+\\)"
288 (1 font-lock-keyword-face
)
289 (2 font-lock-module-def-face
))
290 ("\\<\\(signature\\)\\s-+\\(\\sw+\\)"
291 (1 font-lock-keyword-face
)
292 (2 font-lock-interface-def-face
))
294 (,sml-keywords-regexp . font-lock-keyword-face
)
295 ,@(sml-font-lock-symbols-keywords))
296 "Regexps matching standard SML keywords.")
298 (defface font-lock-type-def-face
300 "Font Lock mode face used to highlight type definitions."
301 :group
'font-lock-highlighting-faces
)
302 (defvar font-lock-type-def-face
'font-lock-type-def-face
303 "Face name to use for type definitions.")
305 (defface font-lock-module-def-face
307 "Font Lock mode face used to highlight module definitions."
308 :group
'font-lock-highlighting-faces
)
309 (defvar font-lock-module-def-face
'font-lock-module-def-face
310 "Face name to use for module definitions.")
312 (defface font-lock-interface-def-face
314 "Font Lock mode face used to highlight interface definitions."
315 :group
'font-lock-highlighting-faces
)
316 (defvar font-lock-interface-def-face
'font-lock-interface-def-face
317 "Face name to use for interface definitions.")
320 ;; Code to handle nested comments and unusual string escape sequences
323 (defsyntax sml-syntax-prop-table
324 '((?
\\ .
".") (?
* .
"."))
325 "Syntax table for text-properties")
327 ;; For Emacsen that have no built-in support for nested comments
328 (defun sml-get-depth-st ()
330 (let* ((disp (if (eq (char-before) ?\
)) (progn (backward-char) -
1) nil
))
332 (disp (if (eq (char-before) ?\
() (progn (backward-char) 0) disp
))
337 (if (re-search-backward "\\*)\\|(\\*" nil t
)
338 (+ (or (get-char-property (point) 'comment-depth
) 0)
339 (case (char-after) (?\
( 1) (?
* 0))
342 (depth (if (> depth
0) depth
)))
343 (put-text-property pt
(1+ pt
) 'comment-depth depth
)
344 (when depth sml-syntax-prop-table
))))))
346 (defconst sml-font-lock-syntactic-keywords
347 `(("^\\s-*\\(\\\\\\)" (1 ',sml-syntax-prop-table
))
348 ,@(unless sml-builtin-nested-comments-flag
349 '(("(?\\(\\*\\))?" (1 (sml-get-depth-st)))))))
351 (defconst sml-font-lock-defaults
352 '(sml-font-lock-keywords nil nil
((?_ .
"w") (?
' .
"w")) nil
353 (font-lock-syntactic-keywords . sml-font-lock-syntactic-keywords
)))
356 ;;; Indentation with SMIE
358 (defconst sml-smie-op-levels
359 ;; We have 3 problem areas where SML's syntax can't be handled by an
360 ;; operator precedence grammar:
362 ;; "= A before B" is "= A) before B" if this is the
363 ;; boolean "=" but it is "= (A before B)" if it's the definitional "=".
364 ;; We can work around the problem by tweaking the lexer to return two
365 ;; different tokens for the two different kinds of "=".
366 ;; "of A | B" in a "case" we want "of (A | B, but in a datatype
367 ;; we want "of A) | B".
368 ;; "= A | B" can be "= A ) | B" if the = is from a "fun" definition,
369 ;; but it is "= (A | B" if it is a "datatype" definition (of course, if
370 ;; the previous introducing the = is "and", deciding whether
371 ;; it's a datatype or a function requires looking even further back).
374 (smie-bnf-precedence-table
375 '((exp ("if" exp
"then" exp
"else" exp
)
376 ("case" exp
"of" branches
)
377 ("let" decls
"in" cmds
"end")
378 ("struct" decls
"end")
381 (sexp "handle" branches
)
382 ("fn" sexp
"=>" exp
))
383 (sexp (sexp ":" type
) ("(" exps
")")
386 (sexp "andalso" sexp
))
387 (cmds (cmds ";" cmds
) (exp))
388 (exps (exps "," exps
) (exp)) ; (exps ";" exps)
389 (branches (sexp "=>" exp
) (branches "|" branches
))
390 ;; Operator precedence grammars handle separators much better then
391 ;; starters/terminators, so let's pretend that let/fun are separators.
392 (decls (sexp "d=" exp
)
393 (sexp "d=" databranches
)
394 (sexp "=of" type
) ;After "exception".
395 ("local" decls
"in" decls
"end")
396 (decls "functor" decls
)
397 (decls "signature" decls
)
398 (decls "structure" decls
)
402 (decls "infix" decls
)
403 (decls "infixr" decls
)
404 (decls "nonfix" decls
)
405 (decls "abstype" decls
)
406 (decls "datatype" decls
)
409 (type (type "->" type
)
411 (databranches (sexp "=of" type
) (databranches "d|" databranches
))
413 ;; (mexp ("functor" marg "d=" mexp)
414 ;; ("structure" marg "d=" mexp)
415 ;; ("signature" marg "d=" mexp))
416 (marg (marg ":" type
) (marg ":>" type
)))
417 ;; '((nonassoc "else") (right "handle"))
418 '((nonassoc "of") (assoc "|")) ; "case a of b => case c of d => e | f"
419 '((nonassoc "handle") (assoc "|")) ; Idem for "handle".
420 '((assoc "->") (assoc "*"))
421 '((assoc "val" "fun" "type" "datatype" "abstype" "open" "infix" "infixr"
422 "nonfix" "functor" "signature" "structure")
424 '((assoc "orelse") (assoc "andalso") (nonassoc ":"))
425 '((assoc ";")) '((assoc ",")) '((assoc "d|")))
427 (smie-precs-precedence-table
428 '((nonassoc "andalso") ;To anchor the prec-table.
431 (nonassoc ">" ">=" "<>" "<" "<=" "=") ;4
433 (assoc "+" "-" "^") ;6
434 (assoc "/" "*" "quot" "rem" "div" "mod") ;7
435 (nonassoc " "))) ;Bogus anchor at the end.
438 (defconst sml-smie-indent-rules
445 ;; Shift single-char separators 2 columns left if they appear
446 ;; at the beginning of a line so the content is aligned
447 ;; (assuming exactly one space after the separator is used).
455 ;; FIXME: Maybe it would be handy to be able to specify different
456 ;; indentation after local's "in" than after let's "in", but currently
457 ;; SMIE doesn't allow us to do that.
459 (("datatype" .
"and") .
5)
460 (("datatype" .
"with") .
4)
461 (("datatype" .
"d=") .
3)
462 (("structure" .
"d=") .
0)
463 (("signature" .
"d=") .
0)
468 (defun sml-smie-definitional-equal-p ()
469 "Figure out which kind of \"=\" this is.
470 Assumes point is right before the = sign."
471 ;; The idea is to look backward for the first occurrence of a token that
472 ;; requires a definitional "=" and then see if there's such a definitional
473 ;; equal between that token and ourselves (in which case we're not
474 ;; a definitional = ourselves).
475 ;; The "search for =" is naive and will match "=>" and "<=", but it turns
476 ;; out to be OK in practice because such tokens very rarely (if ever) appear
477 ;; between the =-starter and the corresponding definitional equal.
478 ;; One known problem case is code like:
479 ;; "functor foo (structure s : S) where type t = s.t ="
480 ;; where the "type t = s.t" is mistaken for a type definition.
482 (and (re-search-backward (concat "\\(" sml-
=-starter-re
"\\)\\|=") nil t
)
483 (match-beginning 1))))
485 (defun sml-smie-non-nested-of-p ()
486 ;; FIXME: Maybe datatype-|-p makes this nested-of business unnecessary.
487 "Figure out which kind of \"of\" this is.
488 Assumes point is right before the \"of\" symbol."
490 (and (re-search-backward (concat "\\(" sml-non-nested-of-starter-re
491 "\\)\\|\\<case\\>") nil t
)
492 (match-beginning 1))))
494 (defun sml-smie-datatype-|-p
()
495 "Figure out which kind of \"|\" this is.
496 Assumes point is right before the | symbol."
498 (forward-char 1) ;Skip the |.
499 (sml-smie-forward-token-1) ;Skip the tag.
500 (member (sml-smie-forward-token-1)
501 '("|" "of" "in" "datatype" "and" "exception" "abstype" "infix"
502 "infixr" "nonfix" "local" "val" "fun" "structure" "functor"
505 (defun sml-smie-forward-token-1 ()
506 (forward-comment (point-max))
507 (buffer-substring (point)
509 (or (/= 0 (skip-syntax-forward "'w_"))
510 (/= 0 (skip-syntax-forward ".'")))
513 (defun sml-smie-forward-token ()
514 (let ((sym (sml-smie-forward-token-1)))
517 (concat "op " (sml-smie-forward-token-1)))
518 ((member sym
'("|" "of" "="))
519 (save-excursion (sml-smie-backward-token)))
522 (defun sml-smie-backward-token-1 ()
523 (forward-comment (- (point)))
524 (buffer-substring (point)
526 (or (/= 0 (skip-syntax-backward ".'"))
527 (/= 0 (skip-syntax-backward "'w_")))
530 (defun sml-smie-backward-token ()
531 (let ((sym (sml-smie-backward-token-1)))
532 (unless (zerop (length sym
))
533 ;; FIXME: what should we do if `sym' = "op" ?
534 (let ((point (point)))
535 (if (equal "op" (sml-smie-backward-token-1))
539 ((string= sym
"=") (if (sml-smie-definitional-equal-p) "d=" "="))
540 ((string= sym
"of") (if (sml-smie-non-nested-of-p) "=of" "of"))
541 ((string= sym
"|") (if (sml-smie-datatype-|-p
) "d|" "|"))
548 (defvar sml-imenu-regexp
549 (concat "^[ \t]*\\(let[ \t]+\\)?"
550 (regexp-opt (append sml-module-head-syms
551 '("and" "fun" "datatype" "abstype" "type")) t
)
554 (defun sml-imenu-create-index ()
556 (goto-char (point-max))
557 (while (re-search-backward sml-imenu-regexp nil t
)
559 (let ((kind (match-string 2))
560 (column (progn (goto-char (match-beginning 2)) (current-column)))
562 (progn (goto-char (match-end 0))
564 (when (looking-at sml-tyvarseq-re
)
565 (goto-char (match-end 0)))
567 (name (sml-forward-sym)))
568 ;; Eliminate trivial renamings.
569 (when (or (not (member kind
'("structure" "signature")))
570 (progn (search-forward "=")
572 (looking-at "sig\\|struct")))
573 (push (cons (concat (make-string (/ column
2) ?\
) name
) location
)
577 ;;; MORE CODE FOR SML-MODE
580 (add-to-list 'auto-mode-alist
'("\\.s\\(ml\\|ig\\)\\'" . sml-mode
))
583 (define-derived-mode sml-mode fundamental-mode
"SML"
584 "\\<sml-mode-map>Major mode for editing ML code.
585 This mode runs `sml-mode-hook' just before exiting.
587 (set (make-local-variable 'font-lock-defaults
) sml-font-lock-defaults
)
588 (set (make-local-variable 'outline-regexp
) sml-outline-regexp
)
589 (set (make-local-variable 'imenu-create-index-function
)
590 'sml-imenu-create-index
)
591 (set (make-local-variable 'add-log-current-defun-function
)
592 'sml-current-fun-name
)
593 ;; Treat paragraph-separators in comments as paragraph-separators.
594 (set (make-local-variable 'paragraph-separate
)
595 (concat "\\([ \t]*\\*)?\\)?\\(" paragraph-separate
"\\)"))
596 (set (make-local-variable 'require-final-newline
) t
)
597 (set (make-local-variable 'forward-sexp-function
) 'smie-forward-sexp-command
)
599 (easy-menu-add sml-mode-menu
)
600 ;; Compatibility. FIXME: we should use `-' in Emacs-CVS.
601 (unless (boundp 'skeleton-positions
) (set (make-local-variable '@) nil
))
602 (sml-mode-variables))
604 (defun sml-mode-variables ()
605 (set-syntax-table sml-mode-syntax-table
)
606 (setq local-abbrev-table sml-mode-abbrev-table
)
607 (smie-setup sml-smie-op-levels sml-smie-indent-rules
)
608 (set (make-local-variable 'smie-backward-token-function
)
609 'sml-smie-backward-token
)
610 (set (make-local-variable 'smie-forward-token-function
)
611 'sml-smie-forward-token
)
612 (set (make-local-variable 'comment-start
) "(* ")
613 (set (make-local-variable 'comment-end
) " *)")
614 (set (make-local-variable 'comment-start-skip
) "(\\*+\\s-*")
615 (set (make-local-variable 'comment-end-skip
) "\\s-*\\*+)")
616 ;; No need to quote nested comments markers.
617 (set (make-local-variable 'comment-quote-nested
) nil
))
619 (defun sml-funname-of-and ()
620 "Name of the function this `and' defines, or nil if not a function.
621 Point has to be right after the `and' symbol and is not preserved."
623 (if (looking-at sml-tyvarseq-re
) (goto-char (match-end 0)))
624 (let ((sym (sml-forward-sym)))
626 (unless (or (member sym
'(nil "d="))
627 (member (sml-forward-sym) '("d=")))
630 (defun sml-electric-pipe ()
632 Depending on the context insert the name of function, a \"=>\" etc."
635 (unless (save-excursion (skip-chars-backward "\t ") (bolp)) (insert "\n"))
639 (backward-char 2) ;back over the just inserted "| "
640 (let ((sym (sml-find-matching-starter sml-pipeheads
641 (sml-op-prec "|" 'back
))))
646 (let ((f (sml-forward-sym)))
647 (sml-find-forward "\\(=>\\|=\\||\\)\\S.")
649 ((looking-at "|") "") ;probably a datatype
650 ((looking-at "=>") " => ") ;`case', or `fn' or `handle'
651 ((looking-at "=") (concat f
" = "))))) ;a function
653 ;; could be a datatype or a function
654 (setq sym
(sml-funname-of-and))
655 (if sym
(concat sym
" = ") ""))
658 (while (and (setq sym
(sml-forward-sym))
659 (string-match "^'" sym
))
660 (sml-forward-spaces))
662 ((member sym
'("case" "handle" "fn" "of")) " => ")
663 ;;((member sym '("abstype" "datatype")) "")
667 (indent-according-to-mode)
669 (skip-chars-forward "\t |")
670 (skip-syntax-forward "w")
671 (skip-chars-forward "\t ")
672 (when (eq ?
= (char-after)) (backward-char)))))
674 (defun sml-electric-semi ()
676 If variable `sml-electric-semi-mode' is t, indent the current line, insert
677 a newline, and indent."
680 (if sml-electric-semi-mode
681 (reindent-then-newline-and-indent)))
685 (defun sml-mark-function ()
686 "Synonym for `mark-paragraph' -- sorry.
687 If anyone has a good algorithm for this..."
691 (defun sml-indent-line ()
692 "Indent current line of ML code."
694 (let ((savep (> (current-column) (current-indentation)))
695 (indent (max (or (ignore-errors (sml-calculate-indentation)) 0) 0)))
697 (save-excursion (indent-line-to indent
))
698 (indent-line-to indent
))))
700 (defun sml-back-to-outer-indent ()
701 "Unindents to the next outer level of indentation."
705 (skip-chars-forward "\t ")
706 (let ((start-column (current-column))
707 (indent (current-column)))
708 (if (> start-column
0)
711 (while (>= indent start-column
)
712 (if (re-search-backward "^[^\n]" nil t
)
713 (setq indent
(current-indentation))
715 (backward-delete-char-untabify (- start-column indent
)))))))
717 (defun sml-find-comment-indent ()
721 (if (re-search-backward "(\\*\\|\\*)" nil t
)
723 ;; FIXME: That's just a stop-gap.
724 ((eq (get-text-property (point) 'face
) 'font-lock-string-face
))
725 ((looking-at "*)") (incf depth
))
726 ((looking-at comment-start-skip
) (decf depth
)))
729 (1+ (current-column))
732 (defun sml-calculate-indentation ()
734 (beginning-of-line) (skip-chars-forward "\t ")
736 ;; Indentation for comments alone on a line, matches the
737 ;; proper indentation of the next line.
738 (when (looking-at "(\\*") (sml-forward-spaces))
740 (sym (save-excursion (sml-forward-sym))))
742 ;; Allow the user to override the indentation.
743 (when (looking-at (concat ".*" (regexp-quote comment-start
)
744 "[ \t]*fixindent[ \t]*"
745 (regexp-quote comment-end
)))
746 (current-indentation))
748 ;; Continued comment.
749 (and (looking-at "\\*") (sml-find-comment-indent))
751 ;; Continued string ? (Added 890113 lbn)
752 (and (looking-at "\\\\")
753 (or (save-excursion (forward-line -
1)
754 (if (looking-at "[\t ]*\\\\")
755 (current-indentation)))
757 (if (re-search-backward "[^\\\\]\"" nil t
)
758 (1+ (current-column))
761 ;; Closing parens. Could be handled below with `sml-indent-relative'?
762 (and (looking-at "\\s)")
764 (skip-syntax-forward ")")
766 (if (sml-dangling-sym)
767 (sml-indent-default 'noindent
)
770 (and (setq data
(assoc sym sml-close-paren
))
771 (sml-indent-relative sym data
))
773 (and (member sym sml-starters-syms
)
774 (sml-indent-starter sym
))
776 (and (string= sym
"|") (sml-indent-pipe))
779 (sml-indent-default))))))
781 (defsubst sml-bolp
()
782 (save-excursion (skip-chars-backward " \t|") (bolp)))
784 (defun sml-first-starter-p ()
785 "Non-nil if starter at point is immediately preceded by let/local/in/..."
787 (let ((sym (unless (save-excursion (sml-backward-arg))
788 (sml-backward-spaces)
789 (sml-backward-sym))))
790 (if (member sym
'(";" "d=")) (setq sym nil
))
794 (defun sml-indent-starter (orig-sym)
795 "Return the indentation to use for a symbol in `sml-starters-syms'.
796 Point should be just before the symbol ORIG-SYM and is not preserved."
797 (let ((sym (unless (save-excursion (sml-backward-arg))
798 (sml-backward-spaces)
799 (sml-backward-sym))))
800 (if (member sym
'(";" "d=")) (setq sym nil
))
801 (if sym
(sml-get-sym-indent sym
)
802 ;; FIXME: this can take a *long* time !!
803 (setq sym
(sml-find-matching-starter sml-starters-syms
))
804 (if (or (sml-first-starter-p)
805 ;; Don't align with `and' because it might be specially indented.
806 (and (or (equal orig-sym
"and") (not (equal sym
"and")))
809 (if (and sml-rightalign-and
(equal orig-sym
"and"))
810 (- (length sym
) 3) 0))
811 (sml-indent-starter orig-sym
)))))
813 (defun sml-indent-relative (sym data
)
815 (sml-forward-sym) (sml-backward-sexp nil
)
816 (unless (second data
) (sml-backward-spaces) (sml-backward-sym))
817 (+ (or (cdr (assoc sym sml-symbol-indent
)) 0)
818 (sml-delegated-indent))))
820 (defun sml-indent-pipe ()
821 (let ((sym (sml-find-matching-starter sml-pipeheads
822 (sml-op-prec "|" 'back
))))
824 (if (string= sym
"|")
825 (if (sml-bolp) (current-column) (sml-indent-pipe))
826 (let ((pipe-indent (or (cdr (assoc "|" sml-symbol-indent
)) -
2)))
827 (when (or (member sym
'("datatype" "abstype"))
828 (and (equal sym
"and")
831 (not (sml-funname-of-and)))))
832 (re-search-forward "="))
835 (+ pipe-indent
(current-column)))))))
837 (defun sml-find-forward (re)
839 (while (and (not (looking-at re
))
841 (or (ignore-errors (forward-sexp 1) t
) (forward-char 1))
843 (not (looking-at re
))))))
845 (defun sml-indent-arg ()
846 (and (save-excursion (ignore-errors (sml-forward-arg)))
847 ;;(not (looking-at sml-not-arg-re))
848 ;; looks like a function or an argument
849 (sml-move-if (sml-backward-arg))
851 (if (save-excursion (not (sml-backward-arg)))
853 (+ (current-column) sml-indent-args
)
855 (while (and (/= (current-column) (current-indentation))
856 (sml-move-if (sml-backward-arg))))
857 (unless (save-excursion (sml-backward-arg))
858 ;; all earlier args are on the same line
859 (sml-forward-arg) (sml-forward-spaces))
862 (defun sml-get-indent (data sym
)
865 ((not (listp data
)) data
)
866 ((setq d
(member sym data
)) (cadr d
))
867 ((and (consp data
) (not (stringp (car data
)))) (car data
))
868 (t sml-indent-level
))))
870 (defun sml-dangling-sym ()
871 "Non-nil if the symbol after point is dangling.
872 The symbol can be an SML symbol or an open-paren. \"Dangling\" means that
873 it is not on its own line but is the last element on that line."
875 (and (not (sml-bolp))
876 (< (sml-point-after (end-of-line))
877 (sml-point-after (or (sml-forward-sym) (skip-syntax-forward "("))
878 (sml-forward-spaces))))))
880 (defun sml-delegated-indent ()
881 (if (sml-dangling-sym)
882 (sml-indent-default 'noindent
)
883 (sml-move-if (backward-word 1)
884 (looking-at sml-agglomerate-re
))
887 (defun sml-get-sym-indent (sym &optional style
)
888 "Find the indentation for the SYM we're `looking-at'.
889 If indentation is delegated, point will move to the start of the parent.
890 Optional argument STYLE is currently ignored."
891 (assert (equal sym
(save-excursion (sml-forward-sym))))
893 (let ((delegate (and (not (equal sym
"end")) (assoc sym sml-close-paren
)))
895 (when (and delegate
(not (eval (third delegate
))))
896 ;;(sml-find-match-backward sym delegate)
897 (sml-forward-sym) (sml-backward-sexp nil
)
899 (if (second delegate
)
900 (save-excursion (sml-forward-sym))
901 (sml-backward-spaces) (sml-backward-sym))))
903 (let ((idata (assoc head-sym sml-indent-rule
)))
905 ;;(if (or style (not delegate))
906 ;; normal indentation
907 (let ((indent (sml-get-indent (cdr idata
) sym
)))
908 (when indent
(+ (sml-delegated-indent) indent
)))
909 ;; delgate indentation to the parent
910 ;;(sml-forward-sym) (sml-backward-sexp nil)
911 ;;(let* ((parent-sym (save-excursion (sml-forward-sym)))
912 ;; (parent-indent (cdr (assoc parent-sym sml-indent-starters))))
913 ;; check the special rules
914 ;;(+ (sml-delegated-indent)
915 ;; (or (sml-get-indent (cdr indent-data) 1 'strict)
916 ;; (sml-get-indent (cdr parent-indent) 1 'strict)
917 ;; (sml-get-indent (cdr indent-data) 0)
918 ;; (sml-get-indent (cdr parent-indent) 0))))))))
921 (defun sml-indent-default (&optional noindent
)
922 (let* ((sym-after (save-excursion (sml-forward-sym)))
923 (_ (sml-backward-spaces))
924 (sym-before (sml-backward-sym))
925 (sym-indent (and sym-before
(sml-get-sym-indent sym-before
)))
926 (indent-after (or (cdr (assoc sym-after sml-symbol-indent
)) 0)))
927 (when (equal sym-before
"end")
928 ;; I don't understand what's really happening here, but when
929 ;; it's `end' clearly, we need to do something special.
931 (setq sym-before nil sym-indent nil
))
934 ;; the previous sym is an indentation introducer: follow the rule
938 (+ sym-indent indent-after
)))
939 ;; If we're just after a hanging open paren.
940 ((and (eq (char-syntax (preceding-char)) ?\
()
941 (save-excursion (backward-char) (sml-dangling-sym)))
943 (sml-indent-default))
946 (let* ((prec-after (sml-op-prec sym-after
'back
))
947 (prec (or (sml-op-prec sym-before
'back
) prec-after
100)))
948 ;; go back until you hit a symbol that has a lower prec than the
949 ;; "current one", or until you backed over a sym that has the same prec
950 ;; but is at the beginning of a line.
951 (while (and (not (sml-bolp))
952 (while (sml-move-if (sml-backward-sexp (1- prec
))))
954 (while (sml-move-if (sml-backward-sexp prec
))))
956 ;; the `noindent' case does back over an introductory symbol
957 ;; such as `fun', ...
960 (sml-backward-spaces)
961 (member (sml-backward-sym) sml-starters-syms
))
963 ;; Use `indent-after' for cases such as when , or ; should be
964 ;; outdented so that their following terms are aligned.
966 (if (equal sym-after
";")
968 (sml-backward-spaces)
969 (member (sml-backward-sym) sml-starters-syms
)))
970 (and sym-after
(not (looking-at sym-after
))))
972 (current-column))))))))
975 ;; maybe `|' should be set to word-syntax in our temp syntax table ?
976 (defun sml-current-indentation ()
979 (skip-chars-forward " \t|")
983 (defun sml-find-matching-starter (syms &optional prec
)
987 (progn (sml-backward-sexp prec
)
988 (setq sym
(save-excursion (sml-forward-sym)))
989 (not (or (member sym syms
) (bobp)))))
990 (if (member sym syms
) sym
))))
992 (defun sml-skip-siblings ()
993 (while (and (not (bobp)) (sml-backward-arg))
994 (sml-find-matching-starter sml-starters-syms
))
995 (when (looking-at "in\\>\\|local\\>")
996 ;;skip over `local...in' and continue
998 (sml-backward-sexp nil
)
999 (sml-skip-siblings)))
1001 (defun sml-beginning-of-defun ()
1002 (let ((sym (sml-find-matching-starter sml-starters-syms
)))
1003 (if (member sym
'("fun" "and" "functor" "signature" "structure"
1004 "abstraction" "datatype" "abstype"))
1005 (save-excursion (sml-forward-sym) (sml-forward-spaces)
1007 ;; We're inside a "non function declaration": let's skip all other
1008 ;; declarations that we find at the same level and try again.
1010 ;; Obviously, let's not try again if we're at bobp.
1011 (unless (bobp) (sml-beginning-of-defun)))))
1013 (defcustom sml-max-name-components
3
1014 "Maximum number of components to use for the current function name."
1018 (defun sml-current-fun-name ()
1020 (let ((count sml-max-name-components
)
1023 (while (and (> count
0)
1024 (setq name
(sml-beginning-of-defun)))
1026 (setq fullname
(if fullname
(concat name
"." fullname
) name
))
1027 ;; Skip all other declarations that we find at the same level.
1028 (sml-skip-siblings))
1032 ;;; INSERTING PROFORMAS (COMMON SML-FORMS)
1034 (defvar sml-forms-alist nil
1035 "*Alist of code templates.
1036 You can extend this alist to your heart's content. For each additional
1037 template NAME in the list, declare a keyboard macro or function (or
1038 interactive command) called 'sml-form-NAME'.
1039 If 'sml-form-NAME' is a function it takes no arguments and should
1040 insert the template at point\; if this is a command it may accept any
1041 sensible interactive call arguments\; keyboard macros can't take
1042 arguments at all. Apropos keyboard macros, see `name-last-kbd-macro'
1043 and `sml-addto-forms-alist'.
1044 `sml-forms-alist' understands let, local, case, abstype, datatype,
1045 signature, structure, and functor by default.")
1047 (defmacro sml-def-skeleton
(name interactor
&rest elements
)
1048 (when (fboundp 'define-skeleton
)
1049 (let ((fsym (intern (concat "sml-form-" name
))))
1050 ;; TODO: don't do the expansion in comments and strings.
1052 (add-to-list 'sml-forms-alist
',(cons name fsym
))
1054 ;; Try to use the new `system' flag.
1055 (define-abbrev sml-mode-abbrev-table
,name
"" ',fsym nil
'system
)
1056 (wrong-number-of-arguments
1057 (define-abbrev sml-mode-abbrev-table
,name
"" ',fsym
)))
1058 (when (fboundp 'abbrev-put
)
1059 (let ((abbrev (abbrev-symbol ,name sml-mode-abbrev-table
)))
1060 (abbrev-put abbrev
:case-fixed t
)
1061 (abbrev-put abbrev
:enable-function
1062 (lambda () (not (nth 8 (syntax-ppss)))))))
1063 (define-skeleton ,fsym
1064 ,(format "SML-mode skeleton for `%s..' expressions" name
)
1066 ,(concat name
" ") >
1068 (put 'sml-def-skeleton
'lisp-indent-function
2)
1070 (sml-def-skeleton "let" nil
1071 @ "\nin " > _
"\nend" >)
1073 (sml-def-skeleton "if" nil
1074 @ " then " > _
"\nelse " > _
)
1076 (sml-def-skeleton "local" nil
1077 @ "\nin" > _
"\nend" >)
1079 (sml-def-skeleton "case" "Case expr: "
1080 str
"\nof " > _
" => ")
1082 (sml-def-skeleton "signature" "Signature name: "
1083 str
" =\nsig" > "\n" > _
"\nend" >)
1085 (sml-def-skeleton "structure" "Structure name: "
1086 str
" =\nstruct" > "\n" > _
"\nend" >)
1088 (sml-def-skeleton "functor" "Functor name: "
1089 str
" () : =\nstruct" > "\n" > _
"\nend" >)
1091 (sml-def-skeleton "datatype" "Datatype name and type params: "
1094 (sml-def-skeleton "abstype" "Abstype name and type params: "
1095 str
" =" \n _
"\nwith" > "\nend" >)
1099 (sml-def-skeleton "struct" nil
1102 (sml-def-skeleton "sig" nil
1105 (sml-def-skeleton "val" nil
1108 (sml-def-skeleton "fn" nil
1111 (sml-def-skeleton "fun" nil
1116 (defun sml-forms-menu (menu)
1117 (mapcar (lambda (x) (vector (car x
) (cdr x
) t
))
1120 (defvar sml-last-form
"let")
1122 (defun sml-electric-space ()
1123 "Expand a symbol into an SML form, or just insert a space.
1124 If the point directly precedes a symbol for which an SML form exists,
1125 the corresponding form is inserted."
1127 (let ((abbrev-mode (not abbrev-mode
))
1128 (last-command-char ?\
)
1129 ;; Bind `this-command' to fool skeleton's special abbrev handling.
1130 (this-command 'self-insert-command
))
1131 (call-interactively 'self-insert-command
)))
1133 (defun sml-insert-form (name newline
)
1134 "Interactive short-cut to insert the NAME common ML form.
1135 If a prefix argument is given insert a NEWLINE and indent first, or
1136 just move to the proper indentation if the line is blank\; otherwise
1137 insert at point (which forces indentation to current column).
1139 The default form to insert is 'whatever you inserted last time'
1140 \(just hit return when prompted\)\; otherwise the command reads with
1141 completion from `sml-forms-alist'."
1143 (list (completing-read
1144 (format "Form to insert: (default %s) " sml-last-form
)
1145 sml-forms-alist nil t nil
)
1146 current-prefix-arg
))
1147 ;; default is whatever the last insert was...
1148 (if (string= name
"") (setq name sml-last-form
) (setq sml-last-form name
))
1149 (unless (or (not newline
)
1150 (save-excursion (beginning-of-line) (looking-at "\\s-*$")))
1152 (unless (/= ?w
(char-syntax (preceding-char))) (insert " "))
1153 (let ((f (cdr (assoc name sml-forms-alist
))))
1155 ((commandp f
) (command-execute f
))
1157 (t (error "Undefined form: %s" name
)))))
1159 ;; See also macros.el in emacs lisp dir.
1161 (defun sml-addto-forms-alist (name)
1162 "Assign a name to the last keyboard macro defined.
1163 Argument NAME is transmogrified to sml-form-NAME which is the symbol
1166 The symbol's function definition becomes the keyboard macro string.
1168 If that works, NAME is added to `sml-forms-alist' so you'll be able to
1169 reinvoke the macro through \\[sml-insert-form]. You might want to save
1170 the macro to use in a later editing session -- see `insert-kbd-macro'
1171 and add these macros to your .emacs file.
1173 See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]."
1174 (interactive "sName for last kbd macro (\"sml-form-\" will be added): ")
1175 (when (string= name
"") (error "No command name given"))
1176 (let ((fsym (intern (concat "sml-form-" name
))))
1177 (name-last-kbd-macro fsym
)
1178 (message "Macro bound to %s" fsym
)
1179 (add-to-list 'sml-forms-alist
(cons name fsym
))))
1185 (defvar sml-mlton-command
"mlton"
1186 "Command to run MLton. Can include arguments.")
1188 (defvar sml-mlton-mainfile nil
)
1190 (defconst sml-mlton-error-regexp-alist
1191 ;; I wish they just changed MLton to use one of the standard
1193 `(("^\\(?:Error\\|\\(Warning\\)\\): \\(.+\\) \\([0-9]+\\)\\.\\([0-9]+\\)\\.$"
1195 ;; If subgroup 1 matched, then it's a warning, otherwise it's an error.
1196 ,@(if (fboundp 'compilation-fake-loc
) '((1))))))
1198 (eval-after-load "compile"
1199 '(dolist (x sml-mlton-error-regexp-alist
)
1200 (add-to-list 'compilation-error-regexp-alist x
)))
1202 (defun sml-mlton-typecheck (mainfile)
1203 "typecheck using MLton."
1205 (list (if (and mainfile
(not current-prefix-arg
))
1207 (read-file-name "Main file: "))))
1210 (dolist (x sml-mlton-error-regexp-alist
)
1211 (add-to-list 'compilation-error-regexp-alist x
))
1212 (with-current-buffer (find-file-noselect mainfile
)
1213 (compile (concat sml-mlton-command
1214 " -stop tc " ;Stop right after type checking.
1215 (shell-quote-argument
1216 (file-relative-name buffer-file-name
))))))
1219 ;;; MLton's def-use info.
1222 (defvar sml-defuse-file nil
)
1224 (defun sml-defuse-file ()
1225 (or sml-defuse-file
(sml-defuse-set-file)))
1227 (defun sml-defuse-set-file ()
1228 "Specify the def-use file to use."
1230 (setq sml-defuse-file
(read-file-name "Def-use file: ")))
1232 (defun sml-defuse-symdata-at-point ()
1235 (let ((symname (sml-backward-sym)))
1236 (if (equal symname
"op")
1237 (save-excursion (setq symname
(sml-forward-sym))))
1238 (when (string-match "op " symname
)
1239 (setq symname
(substring symname
(match-end 0)))
1241 (sml-forward-spaces))
1243 ;; Def-use files seem to count chars, not columns.
1244 ;; We hope here that they don't actually count bytes.
1245 ;; Also they seem to start counting at 1.
1246 (1+ (- (point) (progn (beginning-of-line) (point))))
1248 (widen) (1+ (count-lines (point-min) (point))))
1249 buffer-file-name
))))
1251 (defconst sml-defuse-def-regexp
1252 "^[[:alpha:]]+ \\([^ \n]+\\) \\(.+\\) \\([0-9]+\\)\\.\\([0-9]+\\)$")
1253 (defconst sml-defuse-use-regexp-format
"^ %s %d\\.%d $")
1255 (defun sml-defuse-jump-to-def ()
1256 "Jump to the definition corresponding to the symbol at point."
1258 (let ((symdata (sml-defuse-symdata-at-point)))
1259 (if (null (car symdata
))
1260 (error "Not on a symbol")
1261 (with-current-buffer (find-file-noselect (sml-defuse-file))
1262 (goto-char (point-min))
1263 (unless (re-search-forward
1264 (format sml-defuse-use-regexp-format
1266 ;; May be an absolute file name.
1267 (regexp-quote (nth 3 symdata
))
1269 ;; Or a relative file name.
1270 (regexp-quote (file-relative-name
1276 ;; FIXME: This is typically due to editing: any minor editing will
1277 ;; mess everything up. We should try to fail more gracefully.
1278 (error "Def-use info not found"))
1279 (unless (re-search-backward sml-defuse-def-regexp nil t
)
1280 ;; This indicates a bug in this code.
1281 (error "Internal failure while looking up def-use"))
1282 (unless (equal (match-string 1) (nth 0 symdata
))
1283 ;; FIXME: This again is most likely due to editing.
1284 (error "Incoherence in the def-use info found"))
1285 (let ((line (string-to-number (match-string 3)))
1286 (char (string-to-number (match-string 4))))
1287 (pop-to-buffer (find-file-noselect (match-string 2)))
1289 (forward-char (1- char
)))))))
1292 ;;; SML/NJ's Compilation Manager support
1295 (defvar sml-cm-mode-syntax-table sml-mode-syntax-table
)
1296 (defvar sml-cm-font-lock-keywords
1297 `(,(concat "\\<" (regexp-opt '("library" "group" "is" "structure"
1298 "functor" "signature" "funsig") t
)
1301 (add-to-list 'completion-ignored-extensions
".cm/")
1302 ;; This was used with the old compilation manager.
1303 (add-to-list 'completion-ignored-extensions
"CM/")
1305 (add-to-list 'auto-mode-alist
'("\\.cm\\'" . sml-cm-mode
))
1307 (define-derived-mode sml-cm-mode fundamental-mode
"SML-CM"
1308 "Major mode for SML/NJ's Compilation Manager configuration files."
1309 (local-set-key "\C-c\C-c" 'sml-compile
)
1310 (set (make-local-variable 'font-lock-defaults
)
1311 '(sml-cm-font-lock-keywords nil t nil nil
)))
1317 (defvar sml-lex-font-lock-keywords
1319 '(("^%\\sw+" . font-lock-builtin-face
)
1320 ("^%%" . font-lock-module-def-face
))
1321 sml-font-lock-keywords
))
1322 (defconst sml-lex-font-lock-defaults
1323 (cons 'sml-lex-font-lock-keywords
(cdr sml-font-lock-defaults
)))
1326 (define-derived-mode sml-lex-mode sml-mode
"SML-Lex"
1327 "Major Mode for editing ML-Lex files."
1328 (set (make-local-variable 'font-lock-defaults
) sml-lex-font-lock-defaults
))
1334 (defface sml-yacc-bnf-face
1335 '((t (:foreground
"darkgreen")))
1336 "Face used to highlight (non)terminals in `sml-yacc-mode'.")
1337 (defvar sml-yacc-bnf-face
'sml-yacc-bnf-face
)
1339 (defcustom sml-yacc-indent-action
16
1340 "Indentation column of the opening paren of actions."
1344 (defcustom sml-yacc-indent-pipe nil
1345 "Indentation column of the pipe char in the BNF.
1346 If nil, align it with `:' or with previous cases."
1350 (defcustom sml-yacc-indent-term nil
1351 "Indentation column of the (non)term part.
1352 If nil, align it with previous cases."
1356 (defvar sml-yacc-font-lock-keywords
1357 (cons '("^\\(\\sw+\\s-*:\\|\\s-*|\\)\\(\\s-*\\sw+\\)*\\s-*\\(\\(%\\sw+\\)\\s-+\\sw+\\|\\)"
1360 (goto-char (match-beginning 0))
1361 (unless (or (re-search-forward "\\<of\\>" (match-end 0) 'move
)
1362 (progn (sml-forward-spaces)
1363 (not (looking-at "("))))
1364 sml-yacc-bnf-face
))))
1365 (4 font-lock-builtin-face t t
))
1366 sml-lex-font-lock-keywords
))
1367 (defconst sml-yacc-font-lock-defaults
1368 (cons 'sml-yacc-font-lock-keywords
(cdr sml-font-lock-defaults
)))
1370 (defun sml-yacc-indent-line ()
1371 "Indent current line of ML-Yacc code."
1372 (let ((savep (> (current-column) (current-indentation)))
1373 (indent (max (or (ignore-errors (sml-yacc-indentation)) 0) 0)))
1375 (save-excursion (indent-line-to indent
))
1376 (indent-line-to indent
))))
1378 (defun sml-yacc-indentation ()
1380 (back-to-indentation)
1381 (or (and (looking-at "%\\|\\(\\sw\\|\\s_\\)+\\s-*:") 0)
1382 (when (save-excursion
1383 (condition-case nil
(progn (up-list -
1) nil
) (scan-error t
)))
1384 ;; We're outside an action.
1386 ;; Special handling of indentation inside %term and %nonterm
1388 (and (re-search-backward "^%\\(\\sw+\\)" nil t
)
1389 (member (match-string 1) '("term" "nonterm"))))
1390 (if (numberp sml-yacc-indent-term
) sml-yacc-indent-term
1391 (let ((offset (if (looking-at "|") -
2 0)))
1393 (looking-at "\\s-*\\(%\\sw*\\||\\)?\\s-*")
1394 (goto-char (match-end 0))
1395 (+ offset
(current-column)))))
1396 ((looking-at "(") sml-yacc-indent-action
)
1398 (if (numberp sml-yacc-indent-pipe
) sml-yacc-indent-pipe
1400 (while (progn (sml-backward-spaces)
1401 (/= 0 (skip-syntax-backward "w_"))))
1402 (sml-backward-spaces)
1403 (if (not (looking-at "\\s-$"))
1404 (1- (current-column))
1405 (skip-syntax-forward " ")
1406 (- (current-column) 2))))))
1407 ;; default to SML rules
1408 (sml-calculate-indentation))))
1411 (add-to-list 'auto-mode-alist
'("\\.grm\\'" . sml-yacc-mode
))
1413 (define-derived-mode sml-yacc-mode sml-mode
"SML-Yacc"
1414 "Major Mode for editing ML-Yacc files."
1415 (set (make-local-variable 'indent-line-function
) 'sml-yacc-indent-line
)
1416 (set (make-local-variable 'font-lock-defaults
) sml-yacc-font-lock-defaults
))
1420 ;;; sml-mode.el ends here