1 ;;; sml-mode.el --- Major mode for editing (Standard) ML
3 ;; Copyright (C) 1989 Lars Bo Nielsen
4 ;; Copyright (C) 1994-1997 Matthew J. Morley
5 ;; Copyright (C) 1999-2000 Stefan Monnier
10 ;; This file is not part of GNU Emacs, but it is distributed under the
13 ;; ====================================================================
15 ;; This program is free software; you can redistribute it and/or
16 ;; modify it under the terms of the GNU General Public License as
17 ;; published by the Free Software Foundation; either version 2, or (at
18 ;; your option) any later version.
20 ;; This program is distributed in the hope that it will be useful, but
21 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 ;; General Public License for more details.
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with GNU Emacs; see the file COPYING. If not, write to the
27 ;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
29 ;; ====================================================================
37 ;; Still under construction: History obscure, needs a biographer as
38 ;; well as a M-x doctor. Change Log on request.
40 ;; Hacked by Olin Shivers for comint from Lars Bo Nielsen's sml.el.
42 ;; Hacked by Matthew Morley to incorporate Fritz Knabe's hilite and
43 ;; font-lock patterns, some of Steven Gilmore's (reduced) easy-menus,
44 ;; and numerous bugs and bug-fixes.
46 ;; Author: Lars Bo Nielsen
50 ;; Matthew Morley <mjm@scs.leeds.ac.uk> (aka <matthew@verisity.com>)
51 ;; Matthias Blume <blume@cs.princeton.edu> (aka <blume@kurims.kyoto-u.ac.jp>)
52 ;; (Stefan Monnier) monnier@cs.yale.edu
53 ;; Maintainer: (Stefan Monnier) monnier+lists/emacs/sml@tequila.cs.yale.edu
58 ;; See accompanying info file: sml-mode.info
60 ;;; FOR YOUR .EMACS FILE
62 ;; If sml-mode.el lives in some non-standard directory, you must tell
63 ;; emacs where to get it. This may or may not be necessary:
65 ;; (setq load-path (cons (expand-file-name "~jones/lib/emacs") load-path))
67 ;; Then to access the commands autoload sml-mode with that command:
69 ;; (autoload 'sml-mode "sml-mode" "Major mode for editing ML programs." t)
71 ;; If files ending in ".sml" or ".ML" are hereafter considered to contain
72 ;; Standard ML source, put their buffers into sml-mode automatically:
74 ;; (setq auto-mode-alist
75 ;; (cons '(("\\.sml$" . sml-mode)
76 ;; ("\\.ML$" . sml-mode)) auto-mode-alist))
78 ;; Here's an example of setting things up in the sml-mode-hook:
80 ;; (setq sml-mode-hook
81 ;; '(lambda() "ML mode hacks"
82 ;; (setq sml-indent-level 2 ; conserve on horiz. space
83 ;; indent-tabs-mode nil))) ; whatever
85 ;; sml-mode-hook is run whenever a new sml-mode buffer is created.
87 ;; Finally, there are inferior-sml-{mode,load}-hooks -- see comments
88 ;; in sml-proc.el. For much more information consult the mode's *info*
93 (eval-when-compile (require 'cl))
98 ;;; VARIABLES CONTROLLING INDENTATION
100 (defcustom sml-indent-level 4
101 "*Indentation of blocks in ML (see also `sml-structure-indent')."
105 (defcustom sml-indent-args sml-indent-level
106 "*Indentation of args placed on a separate line."
110 ;; (defvar sml-indent-align-args t
111 ;; "*Whether the arguments should be aligned.")
113 ;; (defvar sml-case-indent nil
114 ;; "*How to indent case-of expressions.
115 ;; If t: case expr If nil: case expr of
116 ;; of exp1 => ... exp1 => ...
117 ;; | exp2 => ... | exp2 => ...
119 ;; The first seems to be the standard in SML/NJ, but the second
122 (defcustom sml-electric-semi-mode nil
123 "*If non-nil, `\;' will self insert, reindent the line, and do a newline.
124 If nil, just insert a `\;'. (To insert while t, do: \\[quoted-insert] \;)."
128 ;;; OTHER GENERIC MODE VARIABLES
130 (defvar sml-mode-info "sml-mode"
131 "*Where to find Info file for `sml-mode'.
132 The default assumes the info file \"sml-mode.info\" is on Emacs' info
133 directory path. If it is not, either put the file on the standard path
134 or set the variable `sml-mode-info' to the exact location of this file
136 (setq sml-mode-info \"/usr/me/lib/info/sml-mode\")
138 in your .emacs file. You can always set it interactively with the
139 set-variable command.")
141 (defvar sml-mode-hook nil
142 "*Run upon entering `sml-mode'.
143 This is a good place to put your preferred key bindings.")
145 ;;; CODE FOR SML-MODE
147 (defun sml-mode-info ()
148 "Command to access the TeXinfo documentation for `sml-mode'.
149 See doc for the variable `sml-mode-info'."
155 (describe-variable 'sml-mode-info)
156 (message "Can't find it... set this variable first!")))))
159 ;;; Autoload functions -- no-doc is another idea cribbed from AucTeX!
162 "This function is part of sml-proc, and has not yet been loaded.
163 Full documentation will be available after autoloading the function."))
165 (autoload 'run-sml "sml-proc" sml-no-doc t)
166 (autoload 'sml-compile "sml-proc" sml-no-doc t)
167 (autoload 'sml-load-file "sml-proc" sml-no-doc t)
168 (autoload 'switch-to-sml "sml-proc" sml-no-doc t)
169 (autoload 'sml-send-region "sml-proc" sml-no-doc t)
170 (autoload 'sml-send-buffer "sml-proc" sml-no-doc t))
174 (defconst sml-keywords-regexp
175 (sml-syms-re "abstraction" "abstype" "and" "andalso" "as" "before" "case"
176 "datatype" "else" "end" "eqtype" "exception" "do" "fn"
177 "fun" "functor" "handle" "if" "in" "include" "infix"
178 "infixr" "let" "local" "nonfix" "of" "op" "open" "orelse"
179 "overload" "raise" "rec" "sharing" "sig" "signature"
180 "struct" "structure" "then" "type" "val" "where" "while"
181 "with" "withtype" "o")
182 "A regexp that matches any and all keywords of SML.")
184 (defconst sml-font-lock-keywords
185 `(;;(sml-font-comments-and-strings)
186 ("\\<\\(fun\\|and\\)\\s-+\\('\\sw+\\s-+\\)*\\(\\sw+\\)"
187 (1 font-lock-keyword-face)
188 (3 font-lock-function-name-face))
189 ("\\<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+\\('\\sw+\\s-+\\)*\\(\\sw+\\)"
190 (1 font-lock-keyword-face)
191 (4 font-lock-type-def-face))
192 ("\\<\\(val\\)\\s-+\\(\\sw+\\>\\s-*\\)?\\(\\sw+\\)\\s-*[=:]"
193 (1 font-lock-keyword-face)
194 ;;(6 font-lock-variable-def-face nil t)
195 (3 font-lock-variable-name-face))
196 ("\\<\\(structure\\|functor\\|abstraction\\)\\s-+\\(\\sw+\\)"
197 (1 font-lock-keyword-face)
198 (2 font-lock-module-def-face))
199 ("\\<\\(signature\\)\\s-+\\(\\sw+\\)"
200 (1 font-lock-keyword-face)
201 (2 font-lock-interface-def-face))
203 (,sml-keywords-regexp . font-lock-keyword-face))
204 "Regexps matching standard SML keywords.")
206 (defface font-lock-type-def-face
208 "Font Lock mode face used to highlight type definitions."
209 :group 'font-lock-highlighting-faces)
210 (defvar font-lock-type-def-face 'font-lock-type-def-face
211 "Face name to use for type definitions.")
213 (defface font-lock-module-def-face
215 "Font Lock mode face used to highlight module definitions."
216 :group 'font-lock-highlighting-faces)
217 (defvar font-lock-module-def-face 'font-lock-module-def-face
218 "Face name to use for module definitions.")
220 (defface font-lock-interface-def-face
222 "Font Lock mode face used to highlight interface definitions."
223 :group 'font-lock-highlighting-faces)
224 (defvar font-lock-interface-def-face 'font-lock-interface-def-face
225 "Face name to use for interface definitions.")
228 ;;; Code to handle nested comments and unusual string escape sequences
231 (defsyntax sml-syntax-prop-table
232 '((?\\ . ".") (?* . "."))
233 "Syntax table for text-properties")
235 ;; For Emacsen that have no built-in support for nested comments
236 (defun sml-get-depth-st ()
238 (let* ((disp (if (eq (char-before) ?\)) (progn (backward-char) -1) nil))
239 (foo (backward-char))
240 (disp (if (eq (char-before) ?\() (progn (backward-char) 0) disp))
245 (if (re-search-backward "\\*)\\|(\\*" nil t)
246 (+ (or (get-char-property (point) 'comment-depth) 0)
247 (case (char-after) (?\( 1) (?* 0))
250 (depth (if (> depth 0) depth)))
251 (put-text-property pt (1+ pt) 'comment-depth depth)
252 (when depth sml-syntax-prop-table))))))
254 (defconst sml-font-lock-syntactic-keywords
255 `(("^\\s-*\\(\\\\\\)" (1 ',sml-syntax-prop-table))
256 ,@(unless sml-builtin-nested-comments-flag
257 '(("(?\\(\\*\\))?" (1 (sml-get-depth-st)))))))
259 (defconst sml-font-lock-defaults
260 '(sml-font-lock-keywords nil nil ((?_ . "w") (?' . "w")) nil
261 (font-lock-syntactic-keywords . sml-font-lock-syntactic-keywords)))
264 ;;; MORE CODE FOR SML-MODE
267 (add-to-list 'auto-mode-alist '("\\.s\\(ml\\|ig\\)\\'" . sml-mode))
270 (define-derived-mode sml-mode fundamental-mode "SML"
271 "\\<sml-mode-map>Major mode for editing ML code.
272 This mode runs `sml-mode-hook' just before exiting.
274 (set (make-local-variable 'font-lock-defaults) sml-font-lock-defaults)
275 (set (make-local-variable 'outline-regexp) sml-outline-regexp)
276 (set (make-local-variable 'add-log-current-defun-function)
277 'sml-current-fun-name)
278 ;; forward-sexp-function is an experimental variable in my hacked Emacs.
279 (set (make-local-variable 'forward-sexp-function) 'sml-user-forward-sexp)
280 (sml-mode-variables))
282 (defun sml-mode-variables ()
283 (set-syntax-table sml-mode-syntax-table)
284 (setq local-abbrev-table sml-mode-abbrev-table)
285 ;; A paragraph is separated by blank lines or ^L only.
287 (set (make-local-variable 'paragraph-start)
288 (concat "^[\t ]*$\\|" page-delimiter))
289 (set (make-local-variable 'paragraph-separate) paragraph-start)
290 (set (make-local-variable 'indent-line-function) 'sml-indent-line)
291 (set (make-local-variable 'comment-start) "(* ")
292 (set (make-local-variable 'comment-end) " *)")
293 (set (make-local-variable 'comment-nested) t)
294 ;;(set (make-local-variable 'block-comment-start) "* ")
295 ;;(set (make-local-variable 'block-comment-end) "")
296 (set (make-local-variable 'comment-column) 40)
297 (set (make-local-variable 'comment-start-skip) "(\\*+\\s-*")
298 (set (make-local-variable 'comment-indent-function) 'sml-comment-indent))
300 (defun sml-electric-pipe ()
302 Depending on the context insert the name of function, a \"=>\" etc."
305 (unless (save-excursion (skip-chars-backward "\t ") (bolp)) (insert "\n"))
309 (backward-char 2) ;back over the just inserted "| "
310 (let ((sym (sml-find-matching-starter sml-pipeheads
311 (sml-op-prec "|" 'back))))
316 (let ((f (sml-forward-sym)))
317 (sml-find-forward "\\(=>\\|=\\||\\)\\S.")
319 ((looking-at "|") "") ;probably a datatype
320 ((looking-at "=>") " => ") ;`case', or `fn' or `handle'
321 ((looking-at "=") (concat f " = "))))) ;a function
323 ;; could be a datatype or a function
324 (while (and (setq sym (sml-forward-sym))
325 (string-match "^'" sym))
326 (sml-forward-spaces))
329 (equal (sml-forward-sym) "d="))
334 (while (and (setq sym (sml-forward-sym))
335 (string-match "^'" sym))
336 (sml-forward-spaces))
338 ((member sym '("case" "handle" "fn" "of")) " => ")
339 ;;((member sym '("abstype" "datatype")) "")
343 (indent-according-to-mode)
345 (skip-chars-forward "\t |")
346 (skip-syntax-forward "w")
347 (skip-chars-forward "\t ")
348 (when (= ?= (char-after)) (backward-char)))))
350 (defun sml-electric-semi ()
352 If variable `sml-electric-semi-mode' is t, indent the current line, insert
353 a newline, and indent."
356 (if sml-electric-semi-mode
357 (reindent-then-newline-and-indent)))
361 (defun sml-mark-function ()
362 "Synonym for `mark-paragraph' -- sorry.
363 If anyone has a good algorithm for this..."
367 ;; (defun sml-indent-region (begin end)
368 ;; "Indent region of ML code."
370 ;; (message "Indenting region...")
372 ;; (goto-char end) (setq end (point-marker)) (goto-char begin)
373 ;; (while (< (point) end)
374 ;; (skip-chars-forward "\t\n ")
375 ;; (indent-according-to-mode)
377 ;; (move-marker end nil))
378 ;; (message "Indenting region... done"))
380 (defun sml-indent-line ()
381 "Indent current line of ML code."
383 (let ((savep (> (current-column) (current-indentation)))
384 (indent (max (or (ignore-errors (sml-calculate-indentation)) 0) 0)))
386 (save-excursion (indent-line-to indent))
387 (indent-line-to indent))))
389 (defun sml-back-to-outer-indent ()
390 "Unindents to the next outer level of indentation."
394 (skip-chars-forward "\t ")
395 (let ((start-column (current-column))
396 (indent (current-column)))
397 (if (> start-column 0)
400 (while (>= indent start-column)
401 (if (re-search-backward "^[^\n]" nil t)
402 (setq indent (current-indentation))
404 (backward-delete-char-untabify (- start-column indent)))))))
406 (defun sml-find-comment-indent ()
410 (if (re-search-backward "(\\*\\|\\*)" nil t)
412 ((looking-at "*)") (incf depth))
413 ((looking-at comment-start-skip) (decf depth)))
416 (1+ (current-column))
419 (defun sml-calculate-indentation ()
421 (beginning-of-line) (skip-chars-forward "\t ")
423 ;; Indentation for comments alone on a line, matches the
424 ;; proper indentation of the next line.
425 (when (looking-at "(\\*") (sml-forward-spaces))
428 (sym (save-excursion (sml-forward-sym))))
430 ;; allow the user to override the indentation
431 (when (looking-at (concat ".*" (regexp-quote comment-start)
432 "[ \t]*fixindent[ \t]*"
433 (regexp-quote comment-end)))
434 (current-indentation))
437 (and (looking-at "\\*") (sml-find-comment-indent))
439 ;; Continued string ? (Added 890113 lbn)
440 (and (looking-at "\\\\")
442 (if (save-excursion (previous-line 1)
444 (looking-at "[\t ]*\\\\"))
445 (progn (previous-line 1) (current-indentation))
446 (if (re-search-backward "[^\\\\]\"" nil t)
447 (1+ (current-column))
450 (and (setq data (assoc sym sml-close-paren))
451 (sml-indent-relative sym data))
453 (and (member (save-excursion (sml-forward-sym)) sml-starters-syms)
454 (let ((sym (unless (save-excursion (sml-backward-arg))
455 (sml-backward-spaces)
456 (sml-backward-sym))))
457 (if sym (sml-get-sym-indent sym)
458 ;; FIXME: this can take a *long* time !!
459 (sml-find-matching-starter sml-starters-syms)
462 (and (string= sym "|") (sml-indent-pipe))
465 (sml-indent-default))))))
467 (defun sml-indent-relative (sym data)
469 (sml-forward-sym) (sml-backward-sexp nil)
470 (unless (second data) (sml-backward-spaces) (sml-backward-sym))
471 (+ (or (cdr (assoc sym sml-symbol-indent)) 0)
472 (sml-delegated-indent))))
474 (defun sml-indent-pipe ()
475 (let ((sym (sml-find-matching-starter sml-pipeheads
476 (sml-op-prec "|" 'back))))
478 (if (string= sym "|")
479 (if (sml-bolp) (current-column) (sml-indent-pipe))
480 (let ((pipe-indent (or (cdr (assoc "|" sml-symbol-indent)) -2)))
481 (when (member sym '("datatype" "abstype"))
482 (re-search-forward "="))
485 (+ pipe-indent (current-column)))))))
487 (defun sml-find-forward (re)
489 (while (and (not (looking-at re))
491 (or (ignore-errors (forward-sexp 1) t) (forward-char 1))
493 (not (looking-at re))))))
495 (defun sml-indent-arg ()
496 (and (save-excursion (ignore-errors (sml-forward-arg)))
497 ;;(not (looking-at sml-not-arg-re))
498 ;; looks like a function or an argument
499 (sml-move-if (sml-backward-arg))
501 (if (save-excursion (not (sml-backward-arg)))
503 (+ (current-column) sml-indent-args)
505 (while (and (/= (current-column) (current-indentation))
506 (sml-move-if (sml-backward-arg))))
507 (unless (save-excursion (sml-backward-arg))
508 ;; all earlier args are on the same line
509 (sml-forward-arg) (sml-forward-spaces))
512 (defun sml-get-indent (data sym)
513 (let ((head-sym (pop data)) d)
515 ((not (listp data)) data)
516 ((setq d (member sym data)) (second d))
517 ((and (consp data) (not (stringp (car data)))) (car data))
518 (t sml-indent-level))))
520 (defun sml-dangling-sym ()
522 (and (not (sml-bolp))
523 (< (sml-point-after (end-of-line))
524 (sml-point-after (sml-forward-sym)
525 (sml-forward-spaces))))))
527 (defun sml-delegated-indent ()
528 (if (sml-dangling-sym)
529 (sml-indent-default 'noindent)
530 (sml-move-if (backward-word 1)
531 (looking-at sml-agglomerate-re))
534 (defun sml-get-sym-indent (sym &optional style)
535 "Find the indentation for the SYM we're `looking-at'.
536 If indentation is delegated, the point will be at the start of
537 the parent at the end of this function.
538 Optional argument STYLE is currently ignored"
539 (assert (equal sym (save-excursion (sml-forward-sym))))
541 (let ((delegate (assoc sym sml-close-paren))
543 (when (and delegate (not (eval (third delegate))))
544 ;;(sml-find-match-backward sym delegate)
545 (sml-forward-sym) (sml-backward-sexp nil)
547 (if (second delegate)
548 (save-excursion (sml-forward-sym))
549 (sml-backward-spaces) (sml-backward-sym))))
551 (let ((idata (assoc head-sym sml-indent-rule)))
553 ;;(if (or style (not delegate))
554 ;; normal indentation
555 (let ((indent (sml-get-indent idata sym)))
556 (when indent (+ (sml-delegated-indent) indent)))
557 ;; delgate indentation to the parent
558 ;;(sml-forward-sym) (sml-backward-sexp nil)
559 ;;(let* ((parent-sym (save-excursion (sml-forward-sym)))
560 ;; (parent-indent (cdr (assoc parent-sym sml-indent-starters))))
561 ;; check the special rules
562 ;;(+ (sml-delegated-indent)
563 ;; (or (sml-get-indent indent-data 1 'strict)
564 ;; (sml-get-indent parent-indent 1 'strict)
565 ;; (sml-get-indent indent-data 0)
566 ;; (sml-get-indent parent-indent 0))))))))
569 (defun sml-indent-default (&optional noindent)
570 (let* ((sym-after (save-excursion (sml-forward-sym)))
571 (_ (sml-backward-spaces))
572 (sym-before (sml-backward-sym))
573 (sym-indent (and sym-before (sml-get-sym-indent sym-before))))
575 ;; the previous sym is an indentation introducer: follow the rule
576 (let ((indent-after (or (cdr (assoc sym-after sml-symbol-indent)) 0)))
580 (+ sym-indent indent-after)))
582 (let* ((prec-after (sml-op-prec sym-after 'back))
583 (prec (or (sml-op-prec sym-before 'back) prec-after 100)))
584 ;; go back until you hit a symbol that has a lower prec than the
585 ;; "current one", or until you backed over a sym that has the same prec
586 ;; but is at the beginning of a line.
587 (while (and (not (sml-bolp))
588 (sml-move-if (sml-backward-sexp (1- prec)))
590 (while (sml-move-if (sml-backward-sexp prec))))
591 ;; the `noindent' case does back over an introductory symbol
592 ;; such as `fun', ...
595 (sml-backward-spaces)
596 (member (sml-backward-sym) sml-starters-syms)))
602 (skip-chars-backward " \t|") (bolp)))
605 ;; maybe `|' should be set to word-syntax in our temp syntax table ?
606 (defun sml-current-indentation ()
609 (skip-chars-forward " \t|")
613 (defun sml-find-matching-starter (syms &optional prec)
617 (progn (sml-backward-sexp prec)
618 (setq sym (save-excursion (sml-forward-sym)))
619 (not (or (member sym syms) (bobp)))))
620 (unless (bobp) sym))))
622 (defun sml-skip-siblings ()
623 (while (and (not (bobp)) (sml-backward-arg))
624 (sml-find-matching-starter sml-starters-syms))
625 (when (looking-at "in\\>\\|local\\>")
626 ;;skip over `local...in' and continue
628 (sml-backward-sexp nil)
629 (sml-skip-siblings)))
631 (defun sml-beginning-of-defun ()
632 (let ((sym (sml-find-matching-starter sml-starters-syms)))
633 (if (member sym '("fun" "functor" "signature" "structure"
634 "abstraction" "datatype" "abstype"))
635 (save-excursion (sml-forward-sym) (sml-forward-spaces)
637 ;; We're inside a "non function declaration": let's skip all other
638 ;; declarations that we find at the same level and try again.
640 ;; Obviously, let's not try again if we're at bobp.
641 (unless (bobp) (sml-beginning-of-defun)))))
643 (defcustom sml-max-name-components 3
644 "Maximum number of components to use for the current function name."
648 (defun sml-current-fun-name ()
650 (let ((count sml-max-name-components)
653 (while (and (> count 0)
654 (setq name (sml-beginning-of-defun)))
656 (setq fullname (if fullname (concat name "." fullname) name))
657 ;; Skip all other declarations that we find at the same level.
662 (defun sml-comment-indent ()
663 (if (looking-at "^(\\*") ; Existing comment at beginning
664 0 ; of line stays there.
667 ;;; INSERTING PROFORMAS (COMMON SML-FORMS)
669 (defvar sml-forms-alist nil
670 "*Alist of code templates.
671 You can extend this alist to your heart's content. For each additional
672 template NAME in the list, declare a keyboard macro or function (or
673 interactive command) called 'sml-form-NAME'.
674 If 'sml-form-NAME' is a function it takes no arguments and should
675 insert the template at point\; if this is a command it may accept any
676 sensible interactive call arguments\; keyboard macros can't take
677 arguments at all. Apropos keyboard macros, see `name-last-kbd-macro'
678 and `sml-addto-forms-alist'.
679 `sml-forms-alist' understands let, local, case, abstype, datatype,
680 signature, structure, and functor by default.")
682 (defmacro sml-def-skeleton (name interactor &rest elements)
683 (let ((fsym (intern (concat "sml-form-" name))))
685 (add-to-list 'sml-forms-alist ',(cons name fsym))
686 (define-abbrev sml-mode-abbrev-table ,name "" ',fsym)
687 (define-skeleton ,fsym
688 ,(format "SML-mode skeleton for `%s..' expressions" name)
692 (put 'sml-def-skeleton 'lisp-indent-function 2)
694 (sml-def-skeleton "let" nil
695 _ "\nin" > "\nend" >)
697 (sml-def-skeleton "if" nil
698 _ " then " > "\nelse " >)
700 (sml-def-skeleton "local" nil
701 _ "\nin" > "\nend" >)
703 (sml-def-skeleton "case" "Case expr: "
704 str "\nof " > _ " => ")
706 (sml-def-skeleton "signature" "Signature name: "
707 str " =\nsig" > "\n" > _ "\nend" >)
709 (sml-def-skeleton "structure" "Structure name: "
710 str " =\nstruct" > "\n" > _ "\nend" >)
712 (sml-def-skeleton "functor" "Functor name: "
713 str " () : =\nstruct" > "\n" > _ "\nend" >)
715 (sml-def-skeleton "datatype" "Datatype name and type params: "
718 (sml-def-skeleton "abstype" "Abstype name and type params: "
719 str " =" \n _ "\nwith" > "\nend" >)
723 (sml-def-skeleton "struct" nil
726 (sml-def-skeleton "sig" nil
729 (sml-def-skeleton "val" nil
732 (sml-def-skeleton "fn" nil
735 (sml-def-skeleton "fun" nil
740 (defun sml-forms-menu (menu)
741 (easy-menu-filter-return
742 (easy-menu-create-menu "Forms"
746 (vector name fsym t)))
749 (defvar sml-last-form "let")
751 (defun sml-electric-space ()
752 "Expand a symbol into an SML form, or just insert a space.
753 If the point directly precedes a symbol for which an SML form exists,
754 the corresponding form is inserted."
756 (let ((abbrev-mode (not abbrev-mode))
757 (last-command-char ?\ )
758 ;; Bind `this-command' to fool skeleton's special abbrev handling.
759 (this-command 'self-insert-command))
760 (call-interactively 'self-insert-command)))
762 (defun sml-insert-form (name newline)
763 "Interactive short-cut to insert the NAME common ML form.
764 If a prefix argument is given insert a NEWLINE and indent first, or
765 just move to the proper indentation if the line is blank\; otherwise
766 insert at point (which forces indentation to current column).
768 The default form to insert is 'whatever you inserted last time'
769 \(just hit return when prompted\)\; otherwise the command reads with
770 completion from `sml-forms-alist'."
772 (list (completing-read
773 (format "Form to insert: (default %s) " sml-last-form)
774 sml-forms-alist nil t nil)
776 ;; default is whatever the last insert was...
777 (if (string= name "") (setq name sml-last-form) (setq sml-last-form name))
778 (unless (or (not newline)
779 (save-excursion (beginning-of-line) (looking-at "\\s-*$")))
781 (unless (/= ?w (char-syntax (char-before))) (insert " "))
782 (let ((f (cdr (assoc name sml-forms-alist))))
784 ((commandp f) (command-execute f))
786 (t (error "Undefined form: %s" name)))))
788 ;; See also macros.el in emacs lisp dir.
790 (defun sml-addto-forms-alist (name)
791 "Assign a name to the last keyboard macro defined.
792 Argument NAME is transmogrified to sml-form-NAME which is the symbol
795 The symbol's function definition becomes the keyboard macro string.
797 If that works, NAME is added to `sml-forms-alist' so you'll be able to
798 reinvoke the macro through \\[sml-insert-form]. You might want to save
799 the macro to use in a later editing session -- see `insert-kbd-macro'
800 and add these macros to your .emacs file.
802 See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]."
803 (interactive "sName for last kbd macro (\"sml-form-\" will be added): ")
804 (when (string= name "") (error "No command name given"))
805 (let ((fsym (intern (concat "sml-form-" name))))
806 (name-last-kbd-macro fsym)
807 (message "Macro bound to %s" fsym)
808 (add-to-list 'sml-forms-alist (cons name fsym))))
810 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
811 ;;;; SML/NJ's Compilation Manager support ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
812 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
815 (add-to-list 'completion-ignored-extensions "CM/")
817 (add-to-list 'auto-mode-alist '("\\.cm\\'" . sml-cm-mode))
819 (define-generic-mode 'sml-cm-mode
821 '("library" "Library" "LIBRARY" "group" "Group" "GROUP" "is" "IS"
822 "structure" "functor" "signature" "funsig")
824 (list (lambda () (local-set-key "\C-c\C-c" 'sml-compile)))
825 "Generic mode for SML/NJ's Compilation Manager configuration files.")
830 ;;; sml-mode.el ends here