1 ;;; sml-mode.el. Major mode for editing (Standard) ML. Version 3.3(beta)
3 (defconst rcsid-sml-mode "@(#)$Name$:$Id$")
5 ;; Copyright (C) 1989-1999, Lars Bo Nielsen; 1994,1997, Matthew J. Morley
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 ;; ====================================================================
33 ;; Still under construction: History obscure, needs a biographer as
34 ;; well as a M-x doctor. Change Log on request.
36 ;; Hacked by Olin Shivers for comint from Lars Bo Nielsen's sml.el.
38 ;; Hacked by Matthew Morley to incorporate Fritz Knabe's hilite and
39 ;; font-lock patterns, some of Steven Gilmore's (reduced) easy-menus,
40 ;; and numerous bugs and bug-fixes.
42 ;; Author: Lars Bo Nielsen
46 ;; Matthew Morley <mjm@scs.leeds.ac.uk> (aka <matthew@verisity.com>)
47 ;; Matthias Blume <blume@cs.princeton.edu> (aka <blume@kurims.kyoto-u.ac.jp>)
48 ;; (Stefan Monnier) monnier@cs.yale.edu
49 ;; Maintainer: (Stefan Monnier) monnier+lists/emacs/sml@tequila.cs.yale.edu
54 ;; See accompanying info file: sml-mode.info
56 ;;; FOR YOUR .EMACS FILE
58 ;; If sml-mode.el lives in some non-standard directory, you must tell
59 ;; emacs where to get it. This may or may not be necessary:
61 ;; (setq load-path (cons (expand-file-name "~jones/lib/emacs") load-path))
63 ;; Then to access the commands autoload sml-mode with that command:
65 ;; (autoload 'sml-mode "sml-mode" "Major mode for editing ML programs." t)
67 ;; If files ending in ".sml" or ".ML" are hereafter considered to contain
68 ;; Standard ML source, put their buffers into sml-mode automatically:
70 ;; (setq auto-mode-alist
71 ;; (cons '(("\\.sml$" . sml-mode)
72 ;; ("\\.ML$" . sml-mode)) auto-mode-alist))
74 ;; Here's an example of setting things up in the sml-mode-hook:
76 ;; (setq sml-mode-hook
77 ;; '(lambda() "ML mode hacks"
78 ;; (setq sml-indent-level 2 ; conserve on horiz. space
79 ;; indent-tabs-mode nil))) ; whatever
81 ;; sml-mode-hook is run whenever a new sml-mode buffer is created.
82 ;; There is an sml-load-hook too, which is only run when this file is
83 ;; loaded. One use for this hook is to select your preferred
84 ;; highlighting scheme, like this:
86 ;; (setq sml-load-hook
87 ;; '(lambda() "Highlights." (require 'sml-hilite)))
89 ;; hilit19 is the magic that actually does the highlighting. My set up
90 ;; for hilit19 runs something like this:
93 ;; (setq hilit-background-mode t ; monochrome (alt: 'dark or 'light)
94 ;; hilit-inhibit-hooks nil
95 ;; hilit-inhibit-rebinding nil
98 ;; Alternatively, you can (require 'sml-font) which uses the font-lock
101 ;; Finally, there are inferior-sml-{mode,load}-hooks -- see comments
102 ;; in sml-proc.el. For much more information consult the mode's *info*
107 (defconst sml-mode-version-string "sml-mode, version 3.9.2")
114 ;;; VARIABLES CONTROLLING INDENTATION
116 (defvar sml-indent-level 4
117 "*Indentation of blocks in ML (see also `sml-structure-indent').")
119 (defvar sml-indent-args sml-indent-level
120 "*Indentation of args placed on a separate line.")
122 ;; (defvar sml-indent-align-args t
123 ;; "*Whether the arguments should be aligned.")
125 ;; (defvar sml-case-indent nil
126 ;; "*How to indent case-of expressions.
127 ;; If t: case expr If nil: case expr of
128 ;; of exp1 => ... exp1 => ...
129 ;; | exp2 => ... | exp2 => ...
131 ;; The first seems to be the standard in SML/NJ, but the second
134 (defvar sml-electric-semi-mode nil
135 "*If t, `\;' will self insert, reindent the line, and do a newline.
136 If nil, just insert a `\;'. (To insert while t, do: C-q \;).")
138 ;;; OTHER GENERIC MODE VARIABLES
140 (defvar sml-mode-info "sml-mode"
141 "*Where to find Info file for sml-mode.
142 The default assumes the info file \"sml-mode.info\" is on Emacs' info
143 directory path. If it is not, either put the file on the standard path
144 or set the variable sml-mode-info to the exact location of this file
145 which is part of the sml-mode 3.2 (and later) distribution. E.g:
147 (setq sml-mode-info \"/usr/me/lib/info/sml-mode\")
149 in your .emacs file. You can always set it interactively with the
150 set-variable command.")
152 (defvar sml-mode-hook nil
153 "*This hook is run when sml-mode is loaded, or a new sml-mode buffer created.
154 This is a good place to put your preferred key bindings.")
156 (defvar sml-load-hook nil
157 "*This hook is run when sml-mode (sml-mode.el) is loaded into Emacs.")
159 (defvar sml-mode-abbrev-table nil "*SML mode abbrev table (default nil)")
161 ;;; CODE FOR SML-MODE
163 (defun sml-mode-info ()
164 "Command to access the TeXinfo documentation for sml-mode.
165 See doc for the variable sml-mode-info."
169 (Info-goto-node (concat "(" sml-mode-info ")"))
171 (describe-variable 'sml-mode-info)
172 (message "Can't find it... set this variable first!")))))
175 ;;; Autoload functions -- no-doc is another idea cribbed from AucTeX!
178 "This function is part of sml-proc, and has not yet been loaded.
179 Full documentation will be available after autoloading the function."))
181 (autoload 'run-sml "sml-proc" sml-no-doc t)
182 (autoload 'sml-compile "sml-proc" sml-no-doc t)
183 (autoload 'sml-load-file "sml-proc" sml-no-doc t)
184 (autoload 'switch-to-sml "sml-proc" sml-no-doc t)
185 (autoload 'sml-send-region "sml-proc" sml-no-doc t)
186 (autoload 'sml-send-buffer "sml-proc" sml-no-doc t))
190 (defconst sml-keywords-regexp
191 (sml-syms-re "abstraction" "abstype" "and" "andalso" "as" "before" "case"
192 "datatype" "else" "end" "eqtype" "exception" "do" "fn"
193 "fun" "functor" "handle" "if" "in" "include" "infix"
194 "infixr" "let" "local" "nonfix" "of" "op" "open" "orelse"
195 "overload" "raise" "rec" "sharing" "sig" "signature"
196 "struct" "structure" "then" "type" "val" "where" "while"
198 "A regexp that matches any and all keywords of SML.")
200 (defconst sml-font-lock-keywords
201 `(;;(sml-font-comments-and-strings)
202 ("\\<\\(fun\\|and\\)\\s-+\\('\\sw+\\s-+\\)*\\(\\sw+\\)"
203 (1 font-lock-keyword-face)
204 (3 font-lock-function-def-face))
205 ("\\<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+\\('\\sw+\\s-+\\)*\\(\\sw+\\)"
206 (1 font-lock-keyword-face)
207 (4 font-lock-type-def-face))
208 ("\\<\\(val\\)\\s-+\\(\\sw+\\>\\s-*\\)?\\(\\sw+\\)\\s-*="
209 (1 font-lock-keyword-face)
210 ;;(6 font-lock-variable-def-face nil t)
211 (3 font-lock-variable-def-face))
212 ("\\<\\(structure\\|functor\\|abstraction\\)\\s-+\\(\\sw+\\)"
213 (1 font-lock-keyword-face)
214 (2 font-lock-module-def-face))
215 ("\\<\\(signature\\)\\s-+\\(\\sw+\\)"
216 (1 font-lock-keyword-face)
217 (2 font-lock-interface-def-face))
219 (,sml-keywords-regexp . font-lock-keyword-face))
220 "Regexps matching standard SML keywords.")
222 ;; default faces values
223 (flet ((def-face (face def)
224 "Define a face for font-lock."
225 (unless (boundp face)
228 ((facep def) (copy-face def face))
230 (def-face 'font-lock-function-def-face 'font-lock-function-name-face)
231 (def-face 'font-lock-type-def-face 'font-lock-type-face)
232 (def-face 'font-lock-module-def-face 'font-lock-function-name-face)
233 (def-face 'font-lock-interface-def-face 'font-lock-type-face)
234 (def-face 'font-lock-variable-def-face 'font-lock-variable-name-face))
236 (defvar sml-syntax-prop-table
237 (let ((st (make-syntax-table)))
238 (modify-syntax-entry ?l "(d" st)
239 (modify-syntax-entry ?s "(d" st)
240 (modify-syntax-entry ?d ")l" st)
241 (modify-syntax-entry ?\\ "." st)
242 (modify-syntax-entry ?* "." st)
245 (defun sml-get-depth-st ()
247 (let* ((disp (if (eq (char-before) ?\)) (progn (backward-char) -1) nil))
248 (foo (backward-char))
249 (disp (if (eq (char-before) ?\() (progn (backward-char) 0) disp))
254 (if (re-search-backward "\\*)\\|(\\*" nil t)
255 (+ (or (get-char-property (point) 'comment-depth) 0)
256 (case (char-after) (?\( 1) (?* 0))
259 (depth (if (> depth 0) depth)))
260 (put-text-property pt (1+ pt) 'comment-depth depth)
261 (when depth sml-syntax-prop-table))))))
263 (defconst sml-font-lock-syntactic-keywords
264 `(;;("\\<\\(l\\)\\(et\\|ocal\\)\\>" (1 ',sml-syntax-prop-table))
265 ;;("\\<\\(s\\)\\(ig\\truct\\)\\>" (1 ',sml-syntax-prop-table))
266 ;;("\\<en\\(d\\)\\>" (1 ',sml-syntax-prop-table))
267 ("^\\s-*\\(\\\\\\)" (1 ',sml-syntax-prop-table))
268 ("(?\\(\\*\\))?" (1 (sml-get-depth-st)))))
270 (defconst sml-font-lock-defaults
271 '(sml-font-lock-keywords nil nil ((?_ . "w") (?' . "w")) nil
272 (font-lock-syntactic-keywords . sml-font-lock-syntactic-keywords)))
275 ;;; MORE CODE FOR SML-MODE
277 (defun sml-mode-version ()
278 "This file's version number (sml-mode)."
280 (message sml-mode-version-string))
284 "Major mode for editing ML code.
285 Entry to this mode runs the hooks on sml-mode-hook.
289 (kill-all-local-variables)
291 (use-local-map sml-mode-map)
292 (setq major-mode 'sml-mode)
293 (setq mode-name "SML")
294 (set (make-local-variable 'outline-regexp) sml-outline-regexp)
295 (run-hooks 'sml-mode-hook)) ; Run the hook last
297 (defun sml-mode-variables ()
298 (set-syntax-table sml-mode-syntax-table)
299 (setq local-abbrev-table sml-mode-abbrev-table)
300 ;; A paragraph is separated by blank lines or ^L only.
302 (set (make-local-variable 'paragraph-start)
303 (concat "^[\t ]*$\\|" page-delimiter))
304 (set (make-local-variable 'paragraph-separate) paragraph-start)
305 (set (make-local-variable 'indent-line-function) 'sml-indent-line)
306 (set (make-local-variable 'comment-start) "(* ")
307 (set (make-local-variable 'comment-end) " *)")
308 (set (make-local-variable 'comment-column) 40)
309 (set (make-local-variable 'comment-start-skip) "(\\*+[ \t]?")
310 (set (make-local-variable 'comment-indent-function) 'sml-comment-indent)
311 (set (make-local-variable 'font-lock-defaults) sml-font-lock-defaults))
313 (defun sml-electric-pipe ()
315 Depending on the context insert the name of function, a \"=>\" etc."
318 (unless (save-excursion (skip-chars-backward "\t ") (bolp)) (insert "\n"))
322 (backward-char 2) ;back over the just inserted "| "
323 (sml-find-matching-starter sml-pipehead-re
324 (sml-op-prec "|" 'back))
325 (let ((sym (sml-forward-sym)))
329 (let ((f (sml-forward-sym)))
330 (sml-find-forward "\\(=>\\|=\\||\\)\\S.")
332 ((looking-at "|") "") ;probably a datatype
333 ((looking-at "=>") " => ") ;`case', or `fn' or `handle'
334 ((looking-at "=") (concat f " = "))))) ;a function
336 ;; could be a datatype or a function
337 (while (and (setq sym (sml-forward-sym))
338 (string-match "^'" sym))
339 (sml-forward-spaces))
342 (equal (sml-forward-sym) "d="))
347 (while (and (setq sym (sml-forward-sym))
348 (string-match "^'" sym))
349 (sml-forward-spaces))
351 ((member sym '("case" "handle" "fn")) " => ")
352 ((member sym '("abstype" "datatype")) "")
353 (t (error "Wow, now, there's a bug")))))))
358 (skip-chars-forward "\t |")
359 (skip-syntax-forward "w")
360 (skip-chars-forward "\t ")
361 (when (= ?= (char-after)) (backward-char)))))
363 (defun sml-electric-semi ()
365 If variable sml-electric-semi-mode is t, indent the current line, insert
366 a newline, and indent."
369 (if sml-electric-semi-mode
370 (reindent-then-newline-and-indent)))
374 (defun sml-mark-function ()
375 "Synonym for mark-paragraph -- sorry.
376 If anyone has a good algorithm for this..."
380 ;; (defun sml-indent-region (begin end)
381 ;; "Indent region of ML code."
383 ;; (message "Indenting region...")
385 ;; (goto-char end) (setq end (point-marker)) (goto-char begin)
386 ;; (while (< (point) end)
387 ;; (skip-chars-forward "\t\n ")
390 ;; (move-marker end nil))
391 ;; (message "Indenting region... done"))
393 (defun sml-indent-line ()
394 "Indent current line of ML code."
396 (let ((indent (sml-calculate-indentation)))
397 (if (/= (current-indentation) indent)
398 (save-excursion ;; Added 890601 (point now stays)
399 (let ((beg (progn (beginning-of-line) (point))))
400 (skip-chars-forward "\t ")
401 (delete-region beg (point))
402 (indent-to indent))))
403 ;; If point is before indentation, move point to indentation
404 (if (< (current-column) (current-indentation))
405 (skip-chars-forward "\t "))))
407 (defun sml-back-to-outer-indent ()
408 "Unindents to the next outer level of indentation."
412 (skip-chars-forward "\t ")
413 (let ((start-column (current-column))
414 (indent (current-column)))
415 (if (> start-column 0)
418 (while (>= indent start-column)
419 (if (re-search-backward "^[^\n]" nil t)
420 (setq indent (current-indentation))
422 (backward-delete-char-untabify (- start-column indent)))))))
424 (defun sml-find-comment-indent ()
428 (if (re-search-backward "(\\*\\|\\*)" nil t)
430 ((looking-at "*)") (incf depth))
431 ((looking-at comment-start-skip) (decf depth)))
434 (1+ (current-column))
437 (defun sml-calculate-indentation ()
439 (beginning-of-line) (skip-chars-forward "\t ")
441 ;; Indentation for comments alone on a line, matches the
442 ;; proper indentation of the next line.
443 (when (looking-at comment-start-skip) (sml-forward-spaces))
446 (sym (save-excursion (sml-forward-sym))))
448 ;; allow the user to override the indentation
449 (when (looking-at sml-fixindent-re) (current-indentation))
452 (and (looking-at "\\*") (sml-find-comment-indent))
454 ;; Continued string ? (Added 890113 lbn)
455 (and (looking-at "\\\\")
457 (if (save-excursion (previous-line 1)
459 (looking-at "[\t ]*\\\\"))
460 (progn (previous-line 1) (current-indentation))
461 (if (re-search-backward "[^\\\\]\"" nil t)
462 (1+ (current-column))
465 (and (setq data (assoc sym sml-close-paren))
466 (sml-indent-relative sym data))
468 (and (looking-at sml-starters-re)
469 (let ((sym (unless (save-excursion (sml-backward-arg))
470 (sml-backward-spaces)
471 (sml-backward-sym))))
472 (if sym (sml-get-sym-indent sym)
473 ;; FIXME: this can take a *long* time !!
474 (sml-find-matching-starter sml-starters-re)
477 (and (string= sym "|") (sml-indent-pipe))
480 (sml-indent-default))))))
482 (defun sml-indent-relative (sym data)
484 (sml-forward-sym) (sml-backward-sexp nil)
485 (unless (second data) (sml-backward-spaces) (sml-backward-sym))
486 (+ (or (cdr (assoc sym sml-symbol-indent)) 0)
487 (sml-delegated-indent))))
489 (defun sml-indent-pipe ()
490 (when (sml-find-matching-starter sml-pipehead-re
491 (sml-op-prec "|" 'back))
493 (if (sml-bolp) (current-column) (sml-indent-pipe))
494 (let ((pipe-indent (or (cdr (assoc "|" sml-symbol-indent)) -2)))
495 (when (looking-at "\\(data\\|abs\\)type\\>")
496 (re-search-forward "="))
499 (+ pipe-indent (current-column))))))
501 (defun sml-find-forward (re)
503 (while (and (not (looking-at re))
505 (or (ignore-errors (forward-sexp 1) t) (forward-char 1))
507 (not (looking-at re))))))
509 (defun sml-indent-arg ()
510 (and (save-excursion (ignore-errors (sml-forward-arg)))
511 ;;(not (looking-at sml-not-arg-re))
512 ;; looks like a function or an argument
513 (sml-move-if (sml-backward-arg))
515 (if (save-excursion (not (sml-backward-arg)))
517 (+ (current-column) sml-indent-args)
519 (while (and (/= (current-column) (current-indentation))
520 (sml-move-if (sml-backward-arg))))
521 (unless (save-excursion (sml-backward-arg))
522 ;; all earlier args are on the same line
523 (sml-forward-arg) (sml-forward-spaces))
526 (defun sml-get-indent (data sym)
527 (let ((head-sym (pop data)) d)
529 ((not (listp data)) data)
530 ((setq d (member sym data)) (second d))
531 ((and (consp data) (not (stringp (car data)))) (car data))
532 (t sml-indent-level))))
534 (defun sml-dangling-sym ()
536 (and (not (sml-bolp))
537 (< (sml-point-after (end-of-line))
538 (sml-point-after (sml-forward-sym)
539 (sml-forward-spaces))))))
541 (defun sml-delegated-indent ()
542 (if (sml-dangling-sym)
543 (sml-indent-default 'noindent)
544 (sml-move-if (backward-word 1)
545 (looking-at sml-agglomerate-re))
548 (defun sml-get-sym-indent (sym &optional style)
549 "expects to be looking-at SYM.
550 If indentation is delegated, the point will be at the start of
551 the parent at the end of this function."
552 (assert (equal sym (save-excursion (sml-forward-sym))))
554 (let ((delegate (assoc sym sml-close-paren))
556 (when (and delegate (not (eval (third delegate))))
557 ;;(sml-find-match-backward sym delegate)
558 (sml-forward-sym) (sml-backward-sexp nil)
560 (if (second delegate)
561 (save-excursion (sml-forward-sym))
562 (sml-backward-spaces) (sml-backward-sym))))
564 (let ((idata (assoc head-sym sml-indent-rule)))
566 ;;(if (or style (not delegate))
567 ;; normal indentation
568 (let ((indent (sml-get-indent idata sym)))
569 (when indent (+ (sml-delegated-indent) indent)))
570 ;; delgate indentation to the parent
571 ;;(sml-forward-sym) (sml-backward-sexp nil)
572 ;;(let* ((parent-sym (save-excursion (sml-forward-sym)))
573 ;; (parent-indent (cdr (assoc parent-sym sml-indent-starters))))
574 ;; check the special rules
575 ;;(+ (sml-delegated-indent)
576 ;; (or (sml-get-indent indent-data 1 'strict)
577 ;; (sml-get-indent parent-indent 1 'strict)
578 ;; (sml-get-indent indent-data 0)
579 ;; (sml-get-indent parent-indent 0))))))))
582 (defun sml-indent-default (&optional noindent)
583 (let* ((sym-after (save-excursion (sml-forward-sym)))
584 (_ (sml-backward-spaces))
585 (sym-before (sml-backward-sym))
586 (sym-indent (and sym-before (sml-get-sym-indent sym-before))))
588 ;; the previous sym is an indentation introducer: follow the rule
589 (let ((indent-after (or (cdr (assoc sym-after sml-symbol-indent)) 0)))
590 (if noindent (current-column) (+ sym-indent indent-after)))
592 (let* ((prec-after (sml-op-prec sym-after 'back))
593 (prec (or (sml-op-prec sym-before 'back) prec-after 100)))
594 ;; go back until you hit a symbol that has a lower prec than the
595 ;; "current one", or until you backed over a sym that has the same prec
596 ;; but is at the beginning of a line.
597 (while (and (not (sml-bolp))
598 (sml-move-if (sml-backward-sexp (1- prec)))
600 (while (sml-move-if (sml-backward-sexp prec))))
601 ;; the `noindent' case does back over an introductory symbol
602 ;; such as `fun', ...
605 (sml-backward-spaces)
606 (string-match sml-starters-re (or (sml-backward-sym) ""))))
612 (skip-chars-backward " \t|") (bolp)))
615 ;; maybe `|' should be set to word-syntax in our temp syntax table ?
616 (defun sml-current-indentation ()
619 (skip-chars-forward " \t|")
623 (defun sml-find-matching-starter (regexp &optional prec)
625 (sml-backward-sexp prec)
626 (while (not (or (looking-at regexp) (bobp)))
627 (sml-backward-sexp prec))
630 (defun sml-comment-indent ()
631 (if (looking-at "^(\\*") ; Existing comment at beginning
632 0 ; of line stays there.
634 (skip-chars-backward " \t")
635 (max (1+ (current-column)) ; Else indent at comment column
636 comment-column)))) ; except leave at least one space.
638 ;;; INSERTING PROFORMAS (COMMON SML-FORMS)
640 (defvar sml-forms-alist nil
641 "*The alist of templates to auto-insert.
643 You can extend this alist to your heart's content. For each additional
644 template NAME in the list, declare a keyboard macro or function (or
645 interactive command) called 'sml-form-NAME'.
647 If 'sml-form-NAME' is a function it takes no arguments and should
648 insert the template at point\; if this is a command it may accept any
649 sensible interactive call arguments\; keyboard macros can't take
650 arguments at all. Apropos keyboard macros, see `name-last-kbd-macro'
651 and `sml-addto-forms-alist'.
653 `sml-forms-alist' understands let, local, case, abstype, datatype,
654 signature, structure, and functor by default.")
656 (defmacro sml-def-skeleton (name interactor &rest elements)
657 (let ((fsym (intern (concat "sml-form-" name))))
659 (add-to-list 'sml-forms-alist ',(cons name fsym))
660 (define-skeleton ,fsym
661 ,(format "SML-mode skeleton for `%s..' expressions" name)
665 (put 'sml-def-skeleton 'lisp-indent-function 2)
667 (sml-def-skeleton "let" nil
668 _ "\nin" > "\nend" >)
670 (sml-def-skeleton "if" nil
671 _ " then " > "\nelse " >)
673 (sml-def-skeleton "local" nil
674 _ "\nin" > "\nend" >)
676 (sml-def-skeleton "case" "Case expr: "
677 str "\nof " > _ " => ")
679 (sml-def-skeleton "signature" "Signature name: "
680 str " =\nsig" > "\n" > _ "\nend" >)
682 (sml-def-skeleton "structure" "Structure name: "
683 str " =\nstruct" > "\n" > _ "\nend" >)
685 (sml-def-skeleton "functor" "Functor name: "
686 str " () : =\nstruct" > "\n" > _ "\nend" >)
688 (sml-def-skeleton "datatype" "Datatype name and type parameters: "
691 (sml-def-skeleton "abstype" "Abstype name and type parameters: "
692 str " =" \n _ "\nwith" > "\nend" >)
696 (defun sml-forms-menu (menu)
697 (easy-menu-filter-return
698 (easy-menu-create-menu "Forms"
702 (vector name fsym t)))
705 (defvar sml-last-form "let")
707 (defun sml-electric-space ()
708 "Expand a symbol into an SML form, or just insert a space.
709 If the point directly precedes a symbol for which an SML form exists,
710 the corresponding form is inserted."
712 (let* ((point (point))
713 (sym (sml-backward-sym)))
714 (if (not (and sym (assoc sym sml-forms-alist)))
715 (progn (goto-char point) (insert " "))
716 (delete-region (point) point)
717 (sml-insert-form sym nil))))
719 (defun sml-insert-form (name newline)
720 "Interactive short-cut to insert a common ML form.
721 If a perfix argument is given insert a newline and indent first, or
722 just move to the proper indentation if the line is blank\; otherwise
723 insert at point (which forces indentation to current column).
725 The default form to insert is 'whatever you inserted last time'
726 \(just hit return when prompted\)\; otherwise the command reads with
727 completion from `sml-forms-alist'."
729 (list (completing-read
730 (format "Form to insert: (default %s) " sml-last-form)
731 sml-forms-alist nil t nil)
733 ;; default is whatever the last insert was...
734 (if (string= name "") (setq name sml-last-form) (setq sml-last-form name))
735 (unless (or (not newline)
736 (save-excursion (beginning-of-line) (looking-at "\\s-*$")))
738 (unless (/= ?w (char-syntax (char-before))) (insert " "))
739 (let ((f (cdr (assoc name sml-forms-alist))))
741 ((commandp f) (command-execute f))
743 (t (error "Undefined form: %s" name)))))
745 ;; See also macros.el in emacs lisp dir.
747 (defun sml-addto-forms-alist (name)
748 "Assign a name to the last keyboard macro defined.
749 Argument NAME is transmogrified to sml-form-NAME which is the symbol
752 The symbol's function definition becomes the keyboard macro string.
754 If that works, NAME is added to `sml-forms-alist' so you'll be able to
755 reinvoke the macro through \\[sml-insert-form]. You might want to save
756 the macro to use in a later editing session -- see `insert-kbd-macro'
757 and add these macros to your .emacs file.
759 See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]."
760 (interactive "sName for last kbd macro (\"sml-form-\" will be added): ")
761 (when (string= name "") (error "No command name given"))
762 (let ((fsym (intern (concat "sml-form-" name))))
763 (name-last-kbd-macro fsym)
764 (message "Macro bound to %s" fsym)
765 (add-to-list 'sml-forms-alist (cons name fsym))))
767 ;; at a pinch these could be added to SML/Forms menu through the good
768 ;; offices of activate-menubar-hook or something... but documentation
769 ;; of this and/or menu-bar-update-hook is sparse in 19.33. anyway, use
770 ;; completing read for sml-insert-form prompt...
772 ;;; & do the user's customisation
773 (run-hooks 'sml-load-hook)
775 ;;; sml-mode.el has just finished.