]> code.delx.au - gnu-emacs-elpa/blob - sml-mode.el
First seemingly acceptable new code.
[gnu-emacs-elpa] / sml-mode.el
1 ;;; sml-mode.el. Major mode for editing (Standard) ML. Version 3.3(beta)
2
3 (defconst rcsid-sml-mode "@(#)$Name$:$Id$")
4
5 ;; Copyright (C) 1989-1999, Lars Bo Nielsen; 1994,1997, Matthew J. Morley
6
7 ;; $Revision$
8 ;; $Date$
9
10 ;; This file is not part of GNU Emacs, but it is distributed under the
11 ;; same conditions.
12
13 ;; ====================================================================
14
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.
19
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.
24
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.
28
29 ;; ====================================================================
30
31 ;;; HISTORY
32
33 ;; Still under construction: History obscure, needs a biographer as
34 ;; well as a M-x doctor. Change Log on request.
35
36 ;; Hacked by Olin Shivers for comint from Lars Bo Nielsen's sml.el.
37
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.
41
42 ;; Author: Lars Bo Nielsen
43 ;; Olin Shivers
44 ;; Fritz Knabe (?)
45 ;; Steven Gilmore (?)
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
50 ;; Keywords: SML
51
52 ;;; DESCRIPTION
53
54 ;; See accompanying info file: sml-mode.info
55
56 ;;; FOR YOUR .EMACS FILE
57
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:
60
61 ;; (setq load-path (cons (expand-file-name "~jones/lib/emacs") load-path))
62
63 ;; Then to access the commands autoload sml-mode with that command:
64
65 ;; (autoload 'sml-mode "sml-mode" "Major mode for editing ML programs." t)
66 ;;
67 ;; If files ending in ".sml" or ".ML" are hereafter considered to contain
68 ;; Standard ML source, put their buffers into sml-mode automatically:
69
70 ;; (setq auto-mode-alist
71 ;; (cons '(("\\.sml$" . sml-mode)
72 ;; ("\\.ML$" . sml-mode)) auto-mode-alist))
73
74 ;; Here's an example of setting things up in the sml-mode-hook:
75
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
80
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:
85
86 ;; (setq sml-load-hook
87 ;; '(lambda() "Highlights." (require 'sml-hilite)))
88
89 ;; hilit19 is the magic that actually does the highlighting. My set up
90 ;; for hilit19 runs something like this:
91
92 ;; (if window-system
93 ;; (setq hilit-background-mode t ; monochrome (alt: 'dark or 'light)
94 ;; hilit-inhibit-hooks nil
95 ;; hilit-inhibit-rebinding nil
96 ;; hilit-quietly t))
97
98 ;; Alternatively, you can (require 'sml-font) which uses the font-lock
99 ;; package instead.
100
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*
103 ;; tree.
104
105 ;;; VERSION STRING
106
107 (defconst sml-mode-version-string
108 "sml-mode, version 3.3")
109
110 (require 'cl)
111 (require 'sml-util)
112 (require 'sml-move)
113 (require 'sml-defs)
114
115 ;;; VARIABLES CONTROLLING INDENTATION
116
117 (defvar sml-indent-level 4
118 "*Indentation of blocks in ML (see also `sml-structure-indent').")
119
120 (defvar sml-structure-indent 4 ; Not currently an option.
121 "*Indentation of signature/structure/functor declarations.")
122
123 (defvar sml-pipe-indent -2
124 "*Extra (usually negative) indentation for lines beginning with `|'.")
125
126 (defvar sml-indent-case-arm 0
127 "*Indentation of case arms.")
128
129 (defvar sml-indent-case-of 2
130 "*Indentation of an `of' on its own line.")
131
132 (defvar sml-indent-equal -2
133 "*Extra (usually negative) indenting for lines beginning with `='.")
134
135 (defvar sml-indent-fn -3
136 "*Extra (usually negative) indenting for lines beginning with `fn'.")
137
138 ;;(defvar sml-indent-paren -1
139 ;; "*Extra (usually negative) indenting for lines beginning with `('.")
140
141 (defvar sml-indent-args 4
142 "*Indentation of args placed on a separate line.")
143
144 (defvar sml-indent-align-args t
145 "*Whether the arguments should be aligned.")
146
147 (defvar sml-case-indent nil
148 "*How to indent case-of expressions.
149 If t: case expr If nil: case expr of
150 of exp1 => ... exp1 => ...
151 | exp2 => ... | exp2 => ...
152
153 The first seems to be the standard in SML/NJ, but the second
154 seems nicer...")
155
156 (defvar sml-nested-if-indent nil
157 "*Determine how nested if-then-else will be formatted:
158 If t: if exp1 then exp2 If nil: if exp1 then exp2
159 else if exp3 then exp4 else if exp3 then exp4
160 else if exp5 then exp6 else if exp5 then exp6
161 else exp7 else exp7")
162
163 (defvar sml-type-of-indent nil
164 "*How to indent `let' `struct' etc.
165 If t: fun foo bar = let If nil: fun foo bar = let
166 val p = 4 val p = 4
167 in in
168 bar + p bar + p
169 end end
170
171 Will not have any effect if the starting keyword is first on the line.")
172
173 (defvar sml-electric-semi-mode nil
174 "*If t, `\;' will self insert, reindent the line, and do a newline.
175 If nil, just insert a `\;'. (To insert while t, do: C-q \;).")
176
177 (defvar sml-paren-lookback 1000
178 "*How far back (in chars) the indentation algorithm should look
179 for open parenthesis. High value means slow indentation algorithm. A
180 value of 1000 (being the equivalent of 20-30 lines) should suffice
181 most uses. (A value of nil, means do not look at all)")
182
183 ;;; OTHER GENERIC MODE VARIABLES
184
185 (defvar sml-mode-info "sml-mode"
186 "*Where to find Info file for sml-mode.
187 The default assumes the info file \"sml-mode.info\" is on Emacs' info
188 directory path. If it is not, either put the file on the standard path
189 or set the variable sml-mode-info to the exact location of this file
190 which is part of the sml-mode 3.2 (and later) distribution. E.g:
191
192 (setq sml-mode-info \"/usr/me/lib/info/sml-mode\")
193
194 in your .emacs file. You can always set it interactively with the
195 set-variable command.")
196
197 (defvar sml-mode-hook nil
198 "*This hook is run when sml-mode is loaded, or a new sml-mode buffer created.
199 This is a good place to put your preferred key bindings.")
200
201 (defvar sml-load-hook nil
202 "*This hook is run when sml-mode (sml-mode.el) is loaded into Emacs.")
203
204 (defvar sml-mode-abbrev-table nil "*SML mode abbrev table (default nil)")
205
206 (defvar sml-error-overlay t
207 "*Non-nil means use an overlay to highlight errorful code in the buffer.
208
209 This gets set when `sml-mode' is invoked\; if you don't like/want SML
210 source errors to be highlighted in this way, do something like
211
212 \(setq-default sml-error-overlay nil\)
213
214 in your `sml-load-hook', say.")
215
216 (make-variable-buffer-local 'sml-error-overlay)
217
218 ;;; CODE FOR SML-MODE
219
220 (defun sml-mode-info ()
221 "Command to access the TeXinfo documentation for sml-mode.
222 See doc for the variable sml-mode-info."
223 (interactive)
224 (require 'info)
225 (condition-case nil
226 (Info-goto-node (concat "(" sml-mode-info ")"))
227 (error (progn
228 (describe-variable 'sml-mode-info)
229 (message "Can't find it... set this variable first!")))))
230
231
232 ;;; Autoload functions -- no-doc is another idea cribbed from AucTeX!
233
234 (let ((sml-no-doc
235 "This function is part of sml-proc, and has not yet been loaded.
236 Full documentation will be available after autoloading the function."))
237
238 (autoload 'run-sml "sml-proc" sml-no-doc t)
239 (autoload 'sml-make "sml-proc" sml-no-doc t)
240 (autoload 'sml-load-file "sml-proc" sml-no-doc t)
241
242 (autoload 'switch-to-sml "sml-proc" sml-no-doc t)
243 (autoload 'sml-send-region "sml-proc" sml-no-doc t)
244 (autoload 'sml-send-buffer "sml-proc" sml-no-doc t)
245 (autoload 'sml-next-error "sml-proc" sml-no-doc t))
246
247 ;; font-lock setup
248
249 (defconst sml-keywords-regexp
250 (sml-syms-re "abstraction" "abstype" "and" "andalso" "as" "before" "case"
251 "datatype" "else" "end" "eqtype" "exception" "do" "fn"
252 "fun" "functor" "handle" "if" "in" "include" "infix"
253 "infixr" "let" "local" "nonfix" "of" "op" "open" "orelse"
254 "overload" "raise" "rec" "sharing" "sig" "signature"
255 "struct" "structure" "then" "type" "val" "where" "while"
256 "with" "withtype")
257 "A regexp that matches any and all keywords of SML.")
258
259 (defconst sml-font-lock-keywords
260 `(;;(sml-font-comments-and-strings)
261 ("\\<\\(fun\\|and\\)\\s-+\\(\\sw+\\)"
262 (1 font-lock-keyword-face)
263 (2 font-lock-function-def-face))
264 ("\\<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+\\('\\s-*\\sw+\\s-+\\)*\\(\\sw+\\)"
265 (1 font-lock-keyword-face)
266 (4 font-lock-type-def-face))
267 ("\\<\\(val\\)\\s-+\\(\\sw+\\>\\s-*\\)?\\(\\sw+\\)\\s-*="
268 (1 font-lock-keyword-face)
269 ;;(6 font-lock-variable-def-face nil t)
270 (3 font-lock-variable-def-face))
271 ("\\<\\(structure\\|functor\\|abstraction\\)\\s-+\\(\\sw+\\)"
272 (1 font-lock-keyword-face)
273 (2 font-lock-module-def-face))
274 ("\\<\\(signature\\)\\s-+\\(\\sw+\\)"
275 (1 font-lock-keyword-face)
276 (2 font-lock-interface-def-face))
277
278 (,sml-keywords-regexp . font-lock-keyword-face))
279 "Regexps matching standard SML keywords.")
280
281 ;; default faces values
282 (flet ((def-face (face def)
283 "Define a face for font-lock."
284 (unless (boundp face)
285 (set face (cond
286 ((facep face) face)
287 ((facep def) (copy-face def face))
288 (t def))))))
289 (def-face 'font-lock-function-def-face 'font-lock-function-name-face)
290 (def-face 'font-lock-type-def-face 'font-lock-type-face)
291 (def-face 'font-lock-module-def-face 'font-lock-function-name-face)
292 (def-face 'font-lock-interface-def-face 'font-lock-type-face)
293 (def-face 'font-lock-variable-def-face 'font-lock-variable-name-face))
294
295 (defvar sml-syntax-prop-table
296 (let ((st (make-syntax-table)))
297 (modify-syntax-entry ?l "(d" st)
298 (modify-syntax-entry ?s "(d" st)
299 (modify-syntax-entry ?d ")l" st)
300 (modify-syntax-entry ?* "." st)
301 st))
302
303 (defun sml-get-depth-st ()
304 (save-excursion
305 (let* ((disp (if (eq (char-before) ?\)) (progn (backward-char) -1) nil))
306 (foo (backward-char))
307 (disp (if (eq (char-before) ?\() (progn (backward-char) 0) disp))
308 (pt (point)))
309 (when disp
310 (let* ((depth
311 (save-match-data
312 (if (re-search-backward "\\*)\\|(\\*" nil t)
313 (+ (or (get-char-property (point) 'comment-depth) 0)
314 (case (char-after) (?\( 1) (?* 0))
315 disp)
316 0)))
317 (depth (if (> depth 0) depth)))
318 (put-text-property pt (1+ pt) 'comment-depth depth)
319 (when depth sml-syntax-prop-table))))))
320
321 (defconst sml-font-lock-syntactic-keywords
322 `(;;("\\<\\(l\\)\\(et\\|ocal\\)\\>" (1 ',sml-syntax-prop-table))
323 ;;("\\<\\(s\\)\\(ig\\truct\\)\\>" (1 ',sml-syntax-prop-table))
324 ;;("\\<en\\(d\\)\\>" (1 ',sml-syntax-prop-table))
325 ("(?\\(\\*\\))?" (1 (sml-get-depth-st)))))
326
327 (defconst sml-font-lock-defaults
328 '(sml-font-lock-keywords nil nil ((?_ . "w") (?' . "w")) nil
329 (font-lock-syntactic-keywords . sml-font-lock-syntactic-keywords)))
330
331 ;; code to get comment fontification working in the face of recursive
332 ;; comments. It's lots more work than it should be. -- stefan
333 ;; (defvar sml-font-cache '((0 . normal))
334 ;; "List of (POSITION . STATE) pairs for an SML buffer.
335 ;; The STATE is either `normal', `comment', or `string'. The POSITION is
336 ;; immediately after the token that caused the state change.")
337 ;; (make-variable-buffer-local 'sml-font-cache)
338
339 ;; (defun sml-font-comments-and-strings (limit)
340 ;; "Fontify SML comments and strings up to LIMIT.
341 ;; Handles nested comments and SML's escapes for breaking a string over lines.
342 ;; Uses sml-font-cache to maintain the fontification state over the buffer."
343 ;; (let ((beg (point))
344 ;; last class)
345 ;; (while (< beg limit)
346 ;; (while (and sml-font-cache
347 ;; (> (caar sml-font-cache) beg))
348 ;; (pop sml-font-cache))
349 ;; (setq last (caar sml-font-cache))
350 ;; (setq class (cdar sml-font-cache))
351 ;; (goto-char last)
352 ;; (cond
353 ;; ((eq class 'normal)
354 ;; (cond
355 ;; ((not (re-search-forward "\\((\\*\\)\\|\\(\"\\)" limit t))
356 ;; (goto-char limit))
357 ;; ((match-beginning 1)
358 ;; (push (cons (point) 'comment) sml-font-cache))
359 ;; ((match-beginning 2)
360 ;; (push (cons (point) 'string) sml-font-cache))))
361 ;; ((eq class 'comment)
362 ;; (cond
363 ;; ((let ((nest 1))
364 ;; (while (and (> nest 0)
365 ;; (re-search-forward "\\((\\*\\)\\|\\(\\*)\\)" limit t))
366 ;; (cond
367 ;; ((match-beginning 1) (incf nest))
368 ;; ((match-beginning 2) (decf nest))))
369 ;; (> nest 0))
370 ;; (goto-char limit))
371 ;; (t
372 ;; (push (cons (point) 'normal) sml-font-cache)))
373 ;; (put-text-property (- last 2) (point) 'face 'font-lock-comment-face))
374 ;; ((eq class 'string)
375 ;; (while (and (re-search-forward
376 ;; "\\(\"\\)\\|\\(\\\\\\s-*\\\\\\)\\|\\(\\\\\"\\)" limit t)
377 ;; (not (match-beginning 1))))
378 ;; (cond
379 ;; ((match-beginning 1)
380 ;; (push (cons (point) 'normal) sml-font-cache))
381 ;; (t
382 ;; (goto-char limit)))
383 ;; (put-text-property (- last 1) (point) 'face 'font-lock-string-face)))
384 ;; (setq beg (point)))))
385
386 ;;; H A C K A T T A C K ! X E M A C S V E R S U S E M A C S
387
388 ;; (cond ((fboundp 'make-extent)
389 ;; ;; suppose this is XEmacs
390
391 ;; (defun sml-make-overlay ()
392 ;; "Create a new text overlay (extent) for the SML buffer."
393 ;; (let ((ex (make-extent 1 1)))
394 ;; (set-extent-property ex 'face 'zmacs-region) ex))
395
396 ;; (defalias 'sml-is-overlay 'extentp)
397
398 ;; (defun sml-overlay-active-p ()
399 ;; "Determine whether the current buffer's error overlay is visible."
400 ;; (and (sml-is-overlay sml-error-overlay)
401 ;; (not (zerop (extent-length sml-error-overlay)))))
402
403 ;; (defalias 'sml-move-overlay 'set-extent-endpoints))
404
405 ;; ((fboundp 'make-overlay)
406 ;; otherwise assume it's Emacs
407
408 (defun sml-make-overlay ()
409 "Create a new text overlay (extent) for the SML buffer."
410 (let ((ex (make-overlay 0 0)))
411 (overlay-put ex 'face 'region) ex))
412
413 (defalias 'sml-is-overlay 'overlayp)
414
415 (defun sml-overlay-active-p ()
416 "Determine whether the current buffer's error overlay is visible."
417 (and (sml-is-overlay sml-error-overlay)
418 (not (equal (overlay-start sml-error-overlay)
419 (overlay-end sml-error-overlay)))))
420
421 (defalias 'sml-move-overlay 'move-overlay);;)
422 ;; (t
423 ;; ;; what *is* this!?
424 ;; (defalias 'sml-is-overlay 'ignore)
425 ;; (defalias 'sml-overlay-active-p 'ignore)
426 ;; (defalias 'sml-make-overlay 'ignore)
427 ;; (defalias 'sml-move-overlay 'ignore)))
428
429 ;;; MORE CODE FOR SML-MODE
430
431 (defun sml-mode-version ()
432 "This file's version number (sml-mode)."
433 (interactive)
434 (message sml-mode-version-string))
435
436 ;;;###Autoload
437 (defun sml-mode ()
438 "Major mode for editing ML code.
439 Tab indents for ML code.
440 Comments are delimited with (* ... *).
441 Blank lines and form-feeds separate paragraphs.
442 Delete converts tabs to spaces as it moves back.
443
444 For information on running an inferior ML process, see the documentation
445 for inferior-sml-mode (set this up with \\[sml]).
446
447 Customisation: Entry to this mode runs the hooks on sml-mode-hook.
448
449 Variables controlling the indentation
450 =====================================
451
452 Seek help (\\[describe-variable]) on individual variables to get current settings.
453
454 sml-indent-level (default 4)
455 The indentation of a block of code.
456
457 sml-pipe-indent (default -2)
458 Extra indentation of a line starting with \"|\".
459
460 sml-case-indent (default nil)
461 Determine the way to indent case-of expression.
462
463 sml-nested-if-indent (default nil)
464 Determine how nested if-then-else expressions are formatted.
465
466 sml-type-of-indent (default nil)
467 How to indent let, struct, local, etc.
468 Will not have any effect if the starting keyword is first on the line.
469
470 sml-electric-semi-mode (default nil)
471 If t, a `\;' will reindent line, and perform a newline.
472
473 sml-paren-lookback (default 1000)
474 Determines how far back (in chars) the indentation algorithm should
475 look to match parenthesis. A value of nil, means do not look at all.
476
477 Mode map
478 ========
479 \\{sml-mode-map}"
480
481 (interactive)
482 (kill-all-local-variables)
483 (sml-mode-variables)
484 (use-local-map sml-mode-map)
485 (setq major-mode 'sml-mode)
486 (setq mode-name "SML")
487 (set (make-local-variable 'outline-regexp) sml-outline-regexp)
488 (run-hooks 'sml-mode-hook)) ; Run the hook last
489
490 (defun sml-mode-variables ()
491 (set-syntax-table sml-mode-syntax-table)
492 (setq local-abbrev-table sml-mode-abbrev-table)
493 ;; A paragraph is separated by blank lines or ^L only.
494
495 (set (make-local-variable 'paragraph-start)
496 (concat "^[\t ]*$\\|" page-delimiter))
497 (set (make-local-variable 'paragraph-separate) paragraph-start)
498 (set (make-local-variable 'indent-line-function) 'sml-indent-line)
499 (set (make-local-variable 'comment-start) "(* ")
500 (set (make-local-variable 'comment-end) " *)")
501 (set (make-local-variable 'comment-column) 40)
502 (set (make-local-variable 'comment-start-skip) "(\\*+[ \t]?")
503 (set (make-local-variable 'comment-indent-function) 'sml-comment-indent)
504 (set (make-local-variable 'font-lock-defaults) sml-font-lock-defaults)
505 ;;(set (make-local-variable 'parse-sexp-lookup-properties) t)
506 ;;(set (make-local-variable 'parse-sexp-ignore-comments) t)
507 (setq sml-error-overlay (and sml-error-overlay (sml-make-overlay))))
508
509 (defun sml-error-overlay (undo &optional beg end buffer)
510 "Move `sml-error-overlay' so it surrounds the text region in the
511 current buffer. If the buffer-local variable `sml-error-overlay' is
512 non-nil it should be an overlay \(or extent, in XEmacs speak\)\; this
513 function moves the overlay over the current region. If the optional
514 BUFFER argument is given, move the overlay in that buffer instead of
515 the current buffer.
516
517 Called interactively, the optional prefix argument UNDO indicates that
518 the overlay should simply be removed: \\[universal-argument] \
519 \\[sml-error-overlay]."
520 (interactive "P")
521 (save-excursion
522 (set-buffer (or buffer (current-buffer)))
523 (if (sml-is-overlay sml-error-overlay)
524 (if undo
525 (sml-move-overlay sml-error-overlay 1 1)
526 ;; if active regions, signals mark not active if no region set
527 (let ((beg (or beg (region-beginning)))
528 (end (or end (region-end))))
529 (sml-move-overlay sml-error-overlay beg end))))))
530
531 (defun sml-electric-pipe ()
532 "Insert a \"|\".
533 Depending on the context insert the name of function, a \"=>\" etc."
534 (interactive)
535 (sml-with-ist
536 (let ((text
537 (save-excursion
538 (sml-find-matching-starter sml-pipehead-re)
539 (cond
540 ;; It was a function, insert the function name
541 ((or (looking-at "fun\\>")
542 (and (looking-at "and\\>")
543 (save-excursion
544 (sml-find-matching-starter
545 (sml-syms-re "datatype" "abstype" "fun"))
546 (looking-at "fun\\>"))))
547 (forward-word 1) (sml-forward-spaces)
548 (concat
549 (buffer-substring (point) (progn (forward-word 1) (point)))
550 " = "))
551
552 ((looking-at (sml-syms-re "case" "handle" "fn")) " => ")
553 ((looking-at (sml-syms-re "abstype" "datatype" "and")) "")
554 (t (error "Wow, now, there's a bug"))))))
555
556 (unless (save-excursion (skip-chars-backward "\t ") (bolp)) (insert "\n"))
557 (insert "| " text)
558 (sml-indent-line)
559 (beginning-of-line)
560 (skip-chars-forward "\t |")
561 (skip-syntax-forward "w")
562 (skip-chars-forward "\t ")
563 (when (= ?= (char-after)) (backward-char)))))
564
565 (defun sml-electric-semi ()
566 "Inserts a \;.
567 If variable sml-electric-semi-mode is t, indent the current line, insert
568 a newline, and indent."
569 (interactive)
570 (insert "\;")
571 (if sml-electric-semi-mode
572 (reindent-then-newline-and-indent)))
573
574 ;;; INDENTATION !!!
575
576 (defun sml-mark-function ()
577 "Synonym for mark-paragraph -- sorry.
578 If anyone has a good algorithm for this..."
579 (interactive)
580 (mark-paragraph))
581
582 (defun sml-indent-region (begin end)
583 "Indent region of ML code."
584 (interactive "r")
585 (message "Indenting region...")
586 (save-excursion
587 (goto-char end) (setq end (point-marker)) (goto-char begin)
588 (while (< (point) end)
589 (skip-chars-forward "\t\n ")
590 (sml-indent-line)
591 (end-of-line))
592 (move-marker end nil))
593 (message "Indenting region... done"))
594
595 (defun sml-indent-line ()
596 "Indent current line of ML code."
597 (interactive)
598 (let ((indent (sml-calculate-indentation)))
599 (if (/= (current-indentation) indent)
600 (save-excursion ;; Added 890601 (point now stays)
601 (let ((beg (progn (beginning-of-line) (point))))
602 (skip-chars-forward "\t ")
603 (delete-region beg (point))
604 (indent-to indent))))
605 ;; If point is before indentation, move point to indentation
606 (if (< (current-column) (current-indentation))
607 (skip-chars-forward "\t "))))
608
609 (defun sml-back-to-outer-indent ()
610 "Unindents to the next outer level of indentation."
611 (interactive)
612 (save-excursion
613 (beginning-of-line)
614 (skip-chars-forward "\t ")
615 (let ((start-column (current-column))
616 (indent (current-column)))
617 (if (> start-column 0)
618 (progn
619 (save-excursion
620 (while (>= indent start-column)
621 (if (re-search-backward "^[^\n]" nil t)
622 (setq indent (current-indentation))
623 (setq indent 0))))
624 (backward-delete-char-untabify (- start-column indent)))))))
625
626 (defun sml-find-comment-indent ()
627 (save-excursion
628 (let ((depth 1))
629 (while (> depth 0)
630 (if (re-search-backward "(\\*\\|\\*)" nil t)
631 (cond
632 ((looking-at "*)") (incf depth))
633 ((looking-at comment-start-skip) (decf depth)))
634 (setq depth -1)))
635 (if (= depth 0)
636 (current-column)
637 nil))))
638
639 (defun sml-calculate-indentation ()
640 (save-excursion
641 (beginning-of-line) (skip-chars-forward "\t ")
642 (sml-with-ist
643 (let ((indent 0)
644 (sml-point (point)))
645 (or
646 ;;(and (bobp) 0)
647
648 ;; Indentation for comments alone on a line, matches the
649 ;; proper indentation of the next line.
650 (and (looking-at comment-start-skip) (sml-forward-spaces) nil)
651
652 ;; continued comment
653 (and (looking-at "\\*") (setq indent (sml-find-comment-indent))
654 (1+ indent))
655
656 ;; Continued string ? (Added 890113 lbn)
657 (and (looking-at "\\\\")
658 (save-excursion
659 (if (save-excursion (previous-line 1)
660 (beginning-of-line)
661 (looking-at "[\t ]*\\\\"))
662 (progn (previous-line 1) (current-indentation))
663 (if (re-search-backward "[^\\\\]\"" nil t)
664 (1+ (current-indentation))
665 0))))
666
667 (and (looking-at "in\\>") ; Match the beginning let/local
668 (sml-find-match-indent "\\<in\\>" "\\<l\\(ocal\\|et\\)\\>"))
669
670 (and (looking-at "end\\>") ; Match the beginning
671 ;; FIXME: should match "in" if available. Or maybe not
672 (sml-find-match-indent "\\<end\\>" sml-begin-symbols-re))
673
674 (and (looking-at "else\\>") ; Match the if
675 (progn
676 (sml-find-match-backward "\\<else\\>" "\\<if\\>")
677 (sml-move-if (backward-word 1)
678 (and sml-nested-if-indent
679 (looking-at "else[ \t]+if\\>")))
680 (current-column)))
681
682 (and (looking-at "then\\>") ; Match the if + extra indentation
683 (sml-find-match-indent "\\<then\\>" "\\<if\\>" t))
684
685 (and (looking-at "of\\>")
686 (progn
687 (sml-find-match-backward "\\<of\\>" "\\<case\\>")
688 (+ (current-column) sml-indent-case-of)))
689
690 (and (looking-at sml-starters-re)
691 (let ((sym (sml-move-read (sml-move-if (not (sml-backward-arg))))))
692 (if sym (sml-get-sym-indent sym)
693 (sml-find-matching-starter sml-starters-re)
694 (current-column))))
695
696 (and (looking-at "|") (sml-indent-pipe))
697
698 (sml-indent-arg)
699 (sml-indent-default))))))
700
701 ;; (let ((indent (current-column)))
702 ;; ;;(skip-chars-forward "\t (")
703 ;; (cond
704 ;; ;; a "let fun" or "let val"
705 ;; ((looking-at "let \\(fun\\|val\\)\\>")
706 ;; (+ (current-column) 4 sml-indent-level))
707 ;; ;; Started val/fun/structure...
708 ;; ;; Indent after "=>" pattern, but only if its not an fn _ =>
709 ;; ;; (890726)
710 ;; ((looking-at ".*=>")
711 ;; (if (looking-at ".*\\<fn\\>.*=>")
712 ;; indent
713 ;; (+ indent sml-indent-case-arm)))
714 ;; ;; else keep the same indentation as previous line
715 ;; (t indent)))))))))
716
717
718 ;;(and (setq indent (sml-get-indent)) nil)
719
720 ;;(and (looking-at "=[^>]") (+ indent sml-indent-equal))
721 ;;(and (looking-at "fn\\>") (+ indent sml-indent-fn))
722 ;; (and (looking-at "(") (+ indent sml-indent-paren))
723
724 ;;(and sml-paren-lookback ; Look for open parenthesis ?
725 ;; (max indent (sml-get-paren-indent)))
726 ;;indent)))))
727
728 (defun sml-indent-pipe ()
729 (when (sml-find-matching-starter (concat "|\\|\\<of\\>\\|" sml-pipehead-re)
730 (sml-op-prec "|" 'back))
731 (if (looking-at "|")
732 (if (sml-bolp) (current-column) (sml-indent-pipe))
733 (cond
734 ((looking-at "datatype")
735 (re-search-forward "=")
736 (forward-char))
737 ((looking-at "case\\>")
738 (sml-forward-sym) ;skip `case'
739 (sml-find-match-forward "\\<case\\>" "\\<of\\>"))
740 (t
741 (forward-word 1)))
742 (sml-forward-spaces)
743 (+ sml-pipe-indent (current-column)))))
744
745
746 (defun sml-indent-arg ()
747 (and (save-excursion (ignore-errors (sml-forward-arg)))
748 ;;(not (looking-at sml-not-arg-re))
749 ;; looks like a function or an argument
750 (sml-move-if (sml-backward-arg))
751 ;; an argument
752 (if (save-excursion (not (sml-backward-arg)))
753 ;; a first argument
754 (+ (current-column) sml-indent-args)
755 ;; not a first arg
756 (while (and (/= (current-column) (current-indentation))
757 (sml-move-if (sml-backward-arg))))
758 (unless (save-excursion (sml-backward-arg))
759 ;; all earlier args are on the same line
760 (sml-forward-arg) (sml-forward-spaces))
761 (current-column))))
762
763 (defun sml-re-assoc (al sym)
764 (when sym
765 (cdr (assoc* sym al
766 :test (lambda (x y) (string-match y x))))))
767 (defun sml-get-indent (data n &optional strict)
768 (eval (if (listp data)
769 (nth n data)
770 (and (not strict) data))))
771
772 (defun sml-dangling-sym ()
773 (save-excursion
774 (and (not (sml-bolp))
775 (< (sml-point-after (end-of-line))
776 (sml-point-after (sml-forward-sym)
777 (sml-forward-spaces))))))
778
779 (defun sml-get-sym-indent (sym &optional style)
780 "expects to be looking-at SYM."
781 (let ((indent-data (sml-re-assoc sml-indent-starters sym))
782 (delegate (eval (sml-re-assoc sml-delegate sym))))
783 (or (when indent-data
784 (if (or style (not delegate))
785 ;; normal indentation
786 (let ((indent (sml-get-indent indent-data (or style 0))))
787 (when indent
788 (+ (if (sml-dangling-sym)
789 (sml-indent-default 'noindent)
790 (current-column))
791 indent)))
792 ;; delgate indentation to the parent
793 (sml-forward-sym) (sml-backward-sexp nil)
794 (let* ((parent-sym (save-excursion (sml-move-read (sml-forward-sym))))
795 (parent-indent (sml-re-assoc sml-indent-starters parent-sym)))
796 ;; check the special rules
797 (sml-move-if (backward-word 1)
798 (looking-at "\\<else[ \t]+if\\>"))
799 (+ (if (sml-dangling-sym)
800 (sml-indent-default 'noindent)
801 (current-column))
802 (or (sml-get-indent indent-data 1 'strict)
803 (sml-get-indent parent-indent 1 'strict)
804 (sml-get-indent indent-data 0)
805 (sml-get-indent parent-indent 0))))))
806 ;; (save-excursion
807 ;; (sml-forward-sym)
808 ;; (when (> (sml-point-after (end-of-line))
809 ;; (progn (sml-forward-spaces) (point)))
810 ;; (current-column)))
811 )))
812
813 (defun sml-indent-default (&optional noindent)
814 (let* ((sym-after (save-excursion (sml-move-read (sml-forward-sym))))
815 (prec-after (sml-op-prec sym-after 'back))
816 (_ (sml-backward-spaces))
817 (sym-before (sml-move-read (sml-backward-sym)))
818 (prec (or (sml-op-prec sym-before 'back) prec-after 100))
819 sexp)
820 (or (and sym-before (sml-get-sym-indent sym-before))
821 (progn
822 ;;(sml-forward-sym)
823 (while (and (not (sml-bolp))
824 (sml-move-if (sml-backward-sexp (1- prec)))
825 (not (sml-bolp)))
826 (while (sml-move-if (sml-backward-sexp prec))))
827 (or (and (not (sml-bolp))
828 (= prec 65) (string-equal "=" sym-before) ;Yuck!!
829 (save-excursion
830 (sml-backward-spaces)
831 (let* ((sym (sml-move-read (sml-backward-sym)))
832 (sym-indent (sml-re-assoc sml-indent-starters sym)))
833 (when sym-indent
834 (if noindent
835 (current-column)
836 (sml-get-sym-indent sym 1))))))
837 (current-column))))))
838
839
840 (defun sml-bolp ()
841 (save-excursion
842 (skip-chars-backward " \t|") (bolp)))
843
844 ;; (defun sml-goto-first-subexp ()
845 ;; (let ((initpoint (point)))
846
847 ;; (let ((argp (and (looking-at "[[({a-zA-Z0-9_'#~]\\|$")
848 ;; (not (looking-at (concat "[ \t]*" sml-not-arg-regexp))))))
849 ;; (while (and argp (not (bobp)))
850 ;; (let* ((endpoint (point))
851 ;; (startpoint endpoint))
852 ;; (setq argp
853 ;; (ignore-errors
854 ;; (sml-backward-sexp t)
855 ;; (setq startpoint (point))
856 ;; (and (not (looking-at (concat "[[(]\\|" sml-keywords-regexp)))
857 ;; (progn (sml-forward-sexp)
858 ;; (sml-skip-spaces)
859 ;; (>= (point) endpoint)))))
860 ;; (goto-char (if argp startpoint endpoint))))
861 ;; (let ((res (point)))
862 ;; (sml-backward-spaces) (skip-syntax-backward "^ ")
863 ;; (if (looking-at "*\\|:[^=]\\|->\\|of\\>")
864 ;; (goto-char initpoint)
865 ;; (goto-char res)
866 ;; (sml-skip-spaces))))))
867
868 ;; maybe `|' should be set to word-syntax in our temp syntax table ?
869 (defun sml-current-indentation ()
870 (save-excursion
871 (beginning-of-line)
872 (skip-chars-forward " \t|")
873 (current-column)))
874
875 ;; (defun sml-get-indent ()
876 ;; (save-excursion
877 ;; ;;(let ((endpoint (point)))
878
879 ;; ;; let's try to see whether we are inside an `f a1 a2 ..' expression
880 ;; ;;(sml-goto-first-subexp)
881 ;; ;;(setq rover (current-column))
882 ;; ;;(sml-skip-spaces)
883 ;; (cond
884 ;; ;; ((< (point) endpoint)
885 ;; ;; ;; we're not the first subexp
886 ;; ;; (sml-forward-sexp)
887 ;; ;; (if (and sml-indent-align-args
888 ;; ;; (progn (sml-skip-spaces) (< (point) endpoint)))
889 ;; ;; ;; we're not the second subexp
890 ;; ;; (current-column)
891 ;; ;; (+ rover sml-indent-args)))
892
893 ;; ;; we're not inside an `f a1 a2 ..' expr
894 ;; ((progn ;;(goto-char endpoint)
895 ;; (sml-backward-spaces)
896 ;; (/= (skip-chars-backward ";,") 0))
897 ;; (sml-backward-sexps (concat "[[(]\\'\\|" sml-user-begin-symbols-re))
898 ;; (current-column))
899
900 ;; (t
901 ;; (while (/= (current-column) (current-indentation))
902 ;; (sml-backward-sexp t))
903 ;; (when (looking-at "\\<of\\>") (forward-word 1))
904 ;; (skip-chars-forward "\t |")
905 ;; (let ((indent (current-column)))
906 ;; ;;(skip-chars-forward "\t (")
907 ;; (cond
908 ;; ;; a "let fun" or "let val"
909 ;; ((looking-at "let \\(fun\\|val\\)\\>")
910 ;; (+ (current-column) 4 sml-indent-level))
911 ;; ;; Started val/fun/structure...
912 ;; ((looking-at sml-indent-starters-reg)
913 ;; (+ (current-column) sml-indent-level))
914 ;; ;; Indent after "=>" pattern, but only if its not an fn _ =>
915 ;; ;; (890726)
916 ;; ((looking-at ".*=>")
917 ;; (if (looking-at ".*\\<fn\\>.*=>")
918 ;; indent
919 ;; (+ indent sml-indent-case-arm)))
920 ;; ;; else keep the same indentation as previous line
921 ;; (t indent)))))))
922
923 ;; (defun sml-get-paren-indent ()
924 ;; (save-excursion
925 ;; (condition-case ()
926 ;; (progn
927 ;; (up-list -1)
928 ;; (if (save-excursion
929 ;; (forward-char 1)
930 ;; (looking-at sml-indent-starters-reg))
931 ;; (1+ (+ (current-column) sml-indent-level))
932 ;; (1+ (current-column))))
933 ;; (error 0))))
934
935 ;; (defun sml-inside-comment-or-string-p ()
936 ;; (let ((start (point)))
937 ;; (if (save-excursion
938 ;; (condition-case ()
939 ;; (progn
940 ;; (search-backward "(*")
941 ;; (search-forward "*)")
942 ;; (forward-char -1) ; A "*)" is not inside the comment
943 ;; (> (point) start))
944 ;; (error nil)))
945 ;; t
946 ;; (let ((numb 0))
947 ;; (save-excursion
948 ;; (save-restriction
949 ;; (narrow-to-region (progn (beginning-of-line) (point)) start)
950 ;; (condition-case ()
951 ;; (while t
952 ;; (search-forward "\"")
953 ;; (setq numb (1+ numb)))
954 ;; (error (if (and (not (zerop numb))
955 ;; (not (zerop (% numb 2))))
956 ;; t nil)))))))))
957
958 ;; (defun sml-find-match-backward (unquoted-this this match)
959 ;; (let ((case-fold-search nil)
960 ;; (level 1)
961 ;; (pattern (concat this "\\|" match)))
962 ;; (while (not (zerop level))
963 ;; (if (sml-re-search-backward pattern)
964 ;; (setq level (cond
965 ;; ((looking-at this) (1+ level))
966 ;; ((looking-at match) (1- level))))
967 ;; ;; The right match couldn't be found
968 ;; (error (concat "Unbalanced: " unquoted-this))))))
969
970 (defun sml-find-match-indent (this match &optional indented)
971 (save-excursion
972 (sml-find-match-backward this match)
973 (if (or indented (not (sml-dangling-sym)))
974 (current-column)
975 (sml-indent-default 'noindent))))
976
977 (defun sml-find-matching-starter (regexp &optional prec)
978 (sml-backward-sexp prec)
979 (while (not (or (looking-at regexp) (bobp)))
980 (sml-backward-sexp prec))
981 (not (bobp)))
982
983 ;; (defun sml-re-search-backward (regexpr)
984 ;; (let ((case-fold-search nil) (found t))
985 ;; (if (re-search-backward regexpr nil t)
986 ;; (progn
987 ;; (condition-case ()
988 ;; (while (sml-inside-comment-or-string-p)
989 ;; (re-search-backward regexpr))
990 ;; (error (setq found nil)))
991 ;; found)
992 ;; nil)))
993
994 (defun sml-comment-indent ()
995 (if (looking-at "^(\\*") ; Existing comment at beginning
996 0 ; of line stays there.
997 (save-excursion
998 (skip-chars-backward " \t")
999 (max (1+ (current-column)) ; Else indent at comment column
1000 comment-column)))) ; except leave at least one space.
1001
1002 ;;; INSERTING PROFORMAS (COMMON SML-FORMS)
1003
1004 (defvar sml-forms-alist
1005 '(("let") ("local") ("case") ("abstype") ("datatype")
1006 ("signature") ("structure") ("functor"))
1007 "*The list of templates to auto-insert.
1008
1009 You can extend this alist to your heart's content. For each additional
1010 template NAME in the list, declare a keyboard macro or function (or
1011 interactive command) called 'sml-form-NAME'.
1012
1013 If 'sml-form-NAME' is a function it takes no arguments and should
1014 insert the template at point\; if this is a command it may accept any
1015 sensible interactive call arguments\; keyboard macros can't take
1016 arguments at all. Apropos keyboard macros, see `name-last-kbd-macro'
1017 and `sml-addto-forms-alist'.
1018
1019 `sml-forms-alist' understands let, local, case, abstype, datatype,
1020 signature, structure, and functor by default.")
1021
1022 ;; See also macros.el in emacs lisp dir.
1023
1024 (defun sml-addto-forms-alist (name)
1025 "Assign a name to the last keyboard macro defined.
1026 Argument NAME is transmogrified to sml-form-NAME which is the symbol
1027 actually defined.
1028
1029 The symbol's function definition becomes the keyboard macro string.
1030
1031 If that works, NAME is added to `sml-forms-alist' so you'll be able to
1032 reinvoke the macro through \\[sml-insert-form]. You might want to save
1033 the macro to use in a later editing session -- see `insert-kbd-macro'
1034 and add these macros to your .emacs file.
1035
1036 See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]."
1037 (interactive "sName for last kbd macro (\"sml-form-\" will be added): ")
1038 (if (string-equal name "")
1039 (error "No command name given")
1040 (name-last-kbd-macro (intern (concat "sml-form-" name)))
1041 (message (concat "Macro bound to sml-form-" name))
1042 (or (assoc name sml-forms-alist)
1043 (setq sml-forms-alist (cons (list name) sml-forms-alist)))))
1044
1045 ;; at a pinch these could be added to SML/Forms menu through the good
1046 ;; offices of activate-menubar-hook or something... but documentation
1047 ;; of this and/or menu-bar-update-hook is sparse in 19.33. anyway, use
1048 ;; completing read for sml-insert-form prompt...
1049
1050 (defvar sml-last-form "let"
1051 "The most recent sml form inserted.")
1052
1053 (defun sml-insert-form (arg)
1054 "Interactive short-cut to insert a common ML form.
1055 If a perfix argument is given insert a newline and indent first, or
1056 just move to the proper indentation if the line is blank\; otherwise
1057 insert at point (which forces indentation to current column).
1058
1059 The default form to insert is 'whatever you inserted last time'
1060 \(just hit return when prompted\)\; otherwise the command reads with
1061 completion from `sml-forms-alist'."
1062 (interactive "P")
1063 (let ((name (completing-read
1064 (format "Form to insert: (default %s) " sml-last-form)
1065 sml-forms-alist nil t nil)))
1066 ;; default is whatever the last insert was...
1067 (if (string= name "") (setq name sml-last-form))
1068 (setq sml-last-form name)
1069 (if arg
1070 (if (save-excursion (beginning-of-line) (looking-at "[ \t]*$"))
1071 (sml-indent-line)
1072 (newline-and-indent)))
1073 (cond ((string= name "let") (sml-form-let))
1074 ((string= name "local") (sml-form-local))
1075 ((string= name "case") (sml-form-case))
1076 ((string= name "abstype") (sml-form-abstype))
1077 ((string= name "datatype") (sml-form-datatype))
1078 ((string= name "functor") (sml-form-functor))
1079 ((string= name "structure") (sml-form-structure))
1080 ((string= name "signature") (sml-form-signature))
1081 (t
1082 (let ((template (intern (concat "sml-form-" name))))
1083 (if (fboundp template)
1084 (if (commandp template)
1085 ;; it may be a named kbd macro too
1086 (command-execute template)
1087 (funcall template))
1088 (error
1089 (format "Undefined format function: %s" template))))))))
1090
1091 (defun sml-form-let ()
1092 "Insert a `let in end' template."
1093 (interactive)
1094 (sml-let-local "let"))
1095
1096 (defun sml-form-local ()
1097 "Insert a `local in end' template."
1098 (interactive)
1099 (sml-let-local "local"))
1100
1101 (defun sml-let-local (starter)
1102 "Insert a let or local template, depending on STARTER string."
1103 (let ((indent (current-column)))
1104 (insert starter)
1105 (insert "\n") (indent-to (+ sml-indent-level indent))
1106 (save-excursion ; so point returns here
1107 (insert "\n")
1108 (indent-to indent)
1109 (insert "in\n")
1110 (indent-to (+ sml-indent-level indent))
1111 (insert "\n")
1112 (indent-to indent)
1113 (insert "end"))))
1114
1115 (defun sml-form-case ()
1116 "Insert a case expression template, prompting for the case-expresion."
1117 (interactive)
1118 (let ((expr (read-string "Case expr: "))
1119 (indent (current-column)))
1120 (insert (concat "case " expr))
1121 (if sml-case-indent
1122 (progn
1123 (insert "\n")
1124 (indent-to (+ 2 indent))
1125 (insert "of "))
1126 (insert " of\n")
1127 (indent-to (+ indent sml-indent-level)))
1128 (save-excursion (insert " => "))))
1129
1130 (defun sml-form-signature ()
1131 "Insert a generative signature binding, prompting for the name."
1132 (interactive)
1133 (let ((indent (current-column))
1134 (name (read-string "Signature name: ")))
1135 (insert (concat "signature " name " ="))
1136 (insert "\n")
1137 (indent-to (+ sml-structure-indent indent))
1138 (insert "sig\n")
1139 (indent-to (+ sml-structure-indent sml-indent-level indent))
1140 (save-excursion
1141 (insert "\n")
1142 (indent-to (+ sml-structure-indent indent))
1143 (insert "end"))))
1144
1145 (defun sml-form-structure ()
1146 "Insert a generative structure binding, prompting for the name.
1147 The command also prompts for any signature constraint -- you should
1148 specify \":\" or \":>\" and the constraining signature."
1149 (interactive)
1150 (let ((indent (current-column))
1151 (name (read-string (concat "Structure name: ")))
1152 (signame (read-string "Signature constraint (default none): ")))
1153 (insert (concat "structure " name " "))
1154 (insert (if (string= "" signame) "=" (concat signame " =")))
1155 (insert "\n")
1156 (indent-to (+ sml-structure-indent indent))
1157 (insert "struct\n")
1158 (indent-to (+ sml-structure-indent sml-indent-level indent))
1159 (save-excursion
1160 (insert "\n")
1161 (indent-to (+ sml-structure-indent indent))
1162 (insert "end"))))
1163
1164 (defun sml-form-functor ()
1165 "Insert a genarative functor binding, prompting for the name.
1166 The command also prompts for the required signature constraint -- you
1167 should specify \":\" or \":>\" and the constraining signature."
1168 (interactive)
1169 (let ((indent(current-indentation))
1170 (name (read-string "Name of functor: "))
1171 (signame (read-string "Signature constraint: " ":" )))
1172 (insert (concat "functor " name " () " signame " ="))
1173 (insert "\n")
1174 (indent-to (+ sml-structure-indent indent))
1175 (insert "struct\n")
1176 (indent-to (+ sml-structure-indent sml-indent-level indent))
1177 (save-excursion ; return to () instead?
1178 (insert "\n")
1179 (indent-to (+ sml-structure-indent indent))
1180 (insert "end"))))
1181
1182 (defun sml-form-datatype ()
1183 "Insert a datatype declaration, prompting for name and type parameter."
1184 (interactive)
1185 (let ((indent (current-indentation))
1186 (type (read-string "Datatype type parameter (default none): "))
1187 (name (read-string (concat "Name of datatype: "))))
1188 (insert (concat "datatype "
1189 (if (string= type "") "" (concat type " "))
1190 name " ="))
1191 (insert "\n")
1192 (indent-to (+ sml-indent-level indent))))
1193
1194 (defun sml-form-abstype ()
1195 "Insert an abstype declaration, prompting for name and type parameter."
1196 (interactive)
1197 (let ((indent(current-indentation))
1198 (type (read-string "Abstype type parameter (default none): "))
1199 (name (read-string "Name of abstype: ")))
1200 (insert (concat "abstype "
1201 (if (string= type "") "" (concat type " "))
1202 name " ="))
1203 (insert "\n")
1204 (indent-to (+ sml-indent-level indent))
1205 (save-excursion
1206 (insert "\n")
1207 (indent-to indent)
1208 (insert "with\n")
1209 (indent-to (+ sml-indent-level indent))
1210 (insert "\n")
1211 (indent-to indent)
1212 (insert "end"))))
1213
1214 ;;; Load the menus, if they can be found on the load-path
1215
1216 (condition-case nil
1217 (require 'sml-menus)
1218 (error (message "Sorry, not able to load SML mode menus.")))
1219
1220 ;;; & do the user's customisation
1221
1222 (add-hook 'sml-load-hook 'sml-mode-version t)
1223
1224 (run-hooks 'sml-load-hook)
1225
1226 ;;; sml-mode.el has just finished.
1227 (provide 'sml-mode)