1 : |
monnier |
535 |
;;; sml-mode.el --- Major mode for editing (Standard) ML
|
2 : |
monnier |
32 |
|
3 : |
monnier |
541 |
;; Copyright (C) 1989 Lars Bo Nielsen
|
4 : |
|
|
;; Copyright (C) 1994-1997 Matthew J. Morley
|
5 : |
|
|
;; Copyright (C) 1999-2000 Stefan Monnier
|
6 : |
monnier |
319 |
|
7 : |
monnier |
32 |
;; $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 : |
|
|
|
32 : |
monnier |
535 |
;;; Commentary:
|
33 : |
|
|
;;
|
34 : |
|
|
|
35 : |
|
|
;;; HISTORY
|
36 : |
|
|
|
37 : |
monnier |
32 |
;; Still under construction: History obscure, needs a biographer as
|
38 : |
|
|
;; well as a M-x doctor. Change Log on request.
|
39 : |
|
|
|
40 : |
|
|
;; Hacked by Olin Shivers for comint from Lars Bo Nielsen's sml.el.
|
41 : |
|
|
|
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.
|
45 : |
|
|
|
46 : |
monnier |
319 |
;; Author: Lars Bo Nielsen
|
47 : |
|
|
;; Olin Shivers
|
48 : |
|
|
;; Fritz Knabe (?)
|
49 : |
|
|
;; Steven Gilmore (?)
|
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
|
54 : |
|
|
;; Keywords: SML
|
55 : |
|
|
|
56 : |
monnier |
535 |
;;; DESCRIPTION
|
57 : |
monnier |
32 |
|
58 : |
|
|
;; See accompanying info file: sml-mode.info
|
59 : |
|
|
|
60 : |
|
|
;;; FOR YOUR .EMACS FILE
|
61 : |
|
|
|
62 : |
monnier |
535 |
;; If sml-mode.el lives in some non-standard directory, you must tell
|
63 : |
monnier |
32 |
;; emacs where to get it. This may or may not be necessary:
|
64 : |
|
|
|
65 : |
|
|
;; (setq load-path (cons (expand-file-name "~jones/lib/emacs") load-path))
|
66 : |
|
|
|
67 : |
|
|
;; Then to access the commands autoload sml-mode with that command:
|
68 : |
|
|
|
69 : |
|
|
;; (autoload 'sml-mode "sml-mode" "Major mode for editing ML programs." t)
|
70 : |
|
|
;;
|
71 : |
|
|
;; If files ending in ".sml" or ".ML" are hereafter considered to contain
|
72 : |
|
|
;; Standard ML source, put their buffers into sml-mode automatically:
|
73 : |
|
|
|
74 : |
|
|
;; (setq auto-mode-alist
|
75 : |
|
|
;; (cons '(("\\.sml$" . sml-mode)
|
76 : |
|
|
;; ("\\.ML$" . sml-mode)) auto-mode-alist))
|
77 : |
|
|
|
78 : |
|
|
;; Here's an example of setting things up in the sml-mode-hook:
|
79 : |
|
|
|
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
|
84 : |
|
|
|
85 : |
|
|
;; sml-mode-hook is run whenever a new sml-mode buffer is created.
|
86 : |
|
|
|
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*
|
89 : |
|
|
;; tree.
|
90 : |
|
|
|
91 : |
monnier |
535 |
;;; Code:
|
92 : |
monnier |
32 |
|
93 : |
monnier |
541 |
(eval-when-compile (require 'cl))
|
94 : |
monnier |
319 |
(require 'sml-util)
|
95 : |
|
|
(require 'sml-move)
|
96 : |
|
|
(require 'sml-defs)
|
97 : |
monnier |
32 |
|
98 : |
|
|
;;; VARIABLES CONTROLLING INDENTATION
|
99 : |
|
|
|
100 : |
monnier |
394 |
(defcustom sml-indent-level 4
|
101 : |
|
|
"*Indentation of blocks in ML (see also `sml-structure-indent')."
|
102 : |
|
|
:group 'sml
|
103 : |
|
|
:type '(integer))
|
104 : |
monnier |
32 |
|
105 : |
monnier |
394 |
(defcustom sml-indent-args sml-indent-level
|
106 : |
|
|
"*Indentation of args placed on a separate line."
|
107 : |
|
|
:group 'sml
|
108 : |
|
|
:type '(integer))
|
109 : |
monnier |
39 |
|
110 : |
monnier |
341 |
;; (defvar sml-indent-align-args t
|
111 : |
|
|
;; "*Whether the arguments should be aligned.")
|
112 : |
monnier |
39 |
|
113 : |
monnier |
341 |
;; (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 => ...
|
118 : |
monnier |
334 |
|
119 : |
monnier |
341 |
;; The first seems to be the standard in SML/NJ, but the second
|
120 : |
|
|
;; seems nicer...")
|
121 : |
monnier |
32 |
|
122 : |
monnier |
394 |
(defcustom sml-electric-semi-mode nil
|
123 : |
|
|
"*If non-nil, `\;' will self insert, reindent the line, and do a newline.
|
124 : |
monnier |
535 |
If nil, just insert a `\;'. (To insert while t, do: \\[quoted-insert] \;)."
|
125 : |
monnier |
394 |
:group 'sml
|
126 : |
|
|
:type '(boolean))
|
127 : |
monnier |
32 |
|
128 : |
|
|
;;; OTHER GENERIC MODE VARIABLES
|
129 : |
|
|
|
130 : |
|
|
(defvar sml-mode-info "sml-mode"
|
131 : |
monnier |
535 |
"*Where to find Info file for `sml-mode'.
|
132 : |
monnier |
32 |
The default assumes the info file \"sml-mode.info\" is on Emacs' info
|
133 : |
monnier |
535 |
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
|
135 : |
monnier |
32 |
|
136 : |
monnier |
535 |
(setq sml-mode-info \"/usr/me/lib/info/sml-mode\")
|
137 : |
monnier |
32 |
|
138 : |
|
|
in your .emacs file. You can always set it interactively with the
|
139 : |
|
|
set-variable command.")
|
140 : |
|
|
|
141 : |
|
|
(defvar sml-mode-hook nil
|
142 : |
monnier |
535 |
"*Run upon entering `sml-mode'.
|
143 : |
monnier |
32 |
This is a good place to put your preferred key bindings.")
|
144 : |
|
|
|
145 : |
monnier |
535 |
(defvar sml-mode-abbrev-table nil "*Abbrev table for `sml-mode'.")
|
146 : |
monnier |
32 |
|
147 : |
monnier |
535 |
;;; CODE FOR SML-MODE
|
148 : |
monnier |
32 |
|
149 : |
|
|
(defun sml-mode-info ()
|
150 : |
monnier |
535 |
"Command to access the TeXinfo documentation for `sml-mode'.
|
151 : |
|
|
See doc for the variable `sml-mode-info'."
|
152 : |
monnier |
32 |
(interactive)
|
153 : |
|
|
(require 'info)
|
154 : |
|
|
(condition-case nil
|
155 : |
monnier |
535 |
(info sml-mode-info)
|
156 : |
monnier |
32 |
(error (progn
|
157 : |
|
|
(describe-variable 'sml-mode-info)
|
158 : |
|
|
(message "Can't find it... set this variable first!")))))
|
159 : |
|
|
|
160 : |
|
|
|
161 : |
|
|
;;; Autoload functions -- no-doc is another idea cribbed from AucTeX!
|
162 : |
|
|
|
163 : |
monnier |
319 |
(let ((sml-no-doc
|
164 : |
|
|
"This function is part of sml-proc, and has not yet been loaded.
|
165 : |
|
|
Full documentation will be available after autoloading the function."))
|
166 : |
monnier |
32 |
|
167 : |
monnier |
332 |
(autoload 'run-sml "sml-proc" sml-no-doc t)
|
168 : |
|
|
(autoload 'sml-compile "sml-proc" sml-no-doc t)
|
169 : |
|
|
(autoload 'sml-load-file "sml-proc" sml-no-doc t)
|
170 : |
|
|
(autoload 'switch-to-sml "sml-proc" sml-no-doc t)
|
171 : |
|
|
(autoload 'sml-send-region "sml-proc" sml-no-doc t)
|
172 : |
|
|
(autoload 'sml-send-buffer "sml-proc" sml-no-doc t))
|
173 : |
monnier |
32 |
|
174 : |
monnier |
33 |
;; font-lock setup
|
175 : |
|
|
|
176 : |
monnier |
39 |
(defconst sml-keywords-regexp
|
177 : |
monnier |
319 |
(sml-syms-re "abstraction" "abstype" "and" "andalso" "as" "before" "case"
|
178 : |
|
|
"datatype" "else" "end" "eqtype" "exception" "do" "fn"
|
179 : |
|
|
"fun" "functor" "handle" "if" "in" "include" "infix"
|
180 : |
|
|
"infixr" "let" "local" "nonfix" "of" "op" "open" "orelse"
|
181 : |
|
|
"overload" "raise" "rec" "sharing" "sig" "signature"
|
182 : |
|
|
"struct" "structure" "then" "type" "val" "where" "while"
|
183 : |
monnier |
535 |
"with" "withtype" "o")
|
184 : |
monnier |
39 |
"A regexp that matches any and all keywords of SML.")
|
185 : |
|
|
|
186 : |
monnier |
300 |
(defconst sml-font-lock-keywords
|
187 : |
|
|
`(;;(sml-font-comments-and-strings)
|
188 : |
monnier |
347 |
("\\<\\(fun\\|and\\)\\s-+\\('\\sw+\\s-+\\)*\\(\\sw+\\)"
|
189 : |
monnier |
33 |
(1 font-lock-keyword-face)
|
190 : |
monnier |
394 |
(3 font-lock-function-name-face))
|
191 : |
monnier |
347 |
("\\<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+\\('\\sw+\\s-+\\)*\\(\\sw+\\)"
|
192 : |
monnier |
33 |
(1 font-lock-keyword-face)
|
193 : |
monnier |
319 |
(4 font-lock-type-def-face))
|
194 : |
|
|
("\\<\\(val\\)\\s-+\\(\\sw+\\>\\s-*\\)?\\(\\sw+\\)\\s-*="
|
195 : |
monnier |
33 |
(1 font-lock-keyword-face)
|
196 : |
|
|
;;(6 font-lock-variable-def-face nil t)
|
197 : |
monnier |
394 |
(3 font-lock-variable-name-face))
|
198 : |
monnier |
319 |
("\\<\\(structure\\|functor\\|abstraction\\)\\s-+\\(\\sw+\\)"
|
199 : |
monnier |
33 |
(1 font-lock-keyword-face)
|
200 : |
|
|
(2 font-lock-module-def-face))
|
201 : |
monnier |
319 |
("\\<\\(signature\\)\\s-+\\(\\sw+\\)"
|
202 : |
monnier |
33 |
(1 font-lock-keyword-face)
|
203 : |
|
|
(2 font-lock-interface-def-face))
|
204 : |
|
|
|
205 : |
monnier |
39 |
(,sml-keywords-regexp . font-lock-keyword-face))
|
206 : |
monnier |
33 |
"Regexps matching standard SML keywords.")
|
207 : |
|
|
|
208 : |
monnier |
394 |
(defface font-lock-type-def-face
|
209 : |
|
|
'((t (:bold t)))
|
210 : |
|
|
"Font Lock mode face used to highlight type definitions."
|
211 : |
|
|
:group 'font-lock-highlighting-faces)
|
212 : |
|
|
(defvar font-lock-type-def-face 'font-lock-type-def-face
|
213 : |
|
|
"Face name to use for type definitions.")
|
214 : |
monnier |
33 |
|
215 : |
monnier |
394 |
(defface font-lock-module-def-face
|
216 : |
|
|
'((t (:bold t)))
|
217 : |
|
|
"Font Lock mode face used to highlight module definitions."
|
218 : |
|
|
:group 'font-lock-highlighting-faces)
|
219 : |
|
|
(defvar font-lock-module-def-face 'font-lock-module-def-face
|
220 : |
|
|
"Face name to use for module definitions.")
|
221 : |
|
|
|
222 : |
|
|
(defface font-lock-interface-def-face
|
223 : |
|
|
'((t (:bold t)))
|
224 : |
|
|
"Font Lock mode face used to highlight interface definitions."
|
225 : |
|
|
:group 'font-lock-highlighting-faces)
|
226 : |
|
|
(defvar font-lock-interface-def-face 'font-lock-interface-def-face
|
227 : |
|
|
"Face name to use for interface definitions.")
|
228 : |
|
|
|
229 : |
monnier |
535 |
;;;
|
230 : |
|
|
;;; Code to handle nested comments and unusual string escape sequences
|
231 : |
|
|
;;;
|
232 : |
monnier |
33 |
|
233 : |
monnier |
535 |
(defsyntax sml-syntax-prop-table
|
234 : |
|
|
'((?\\ . ".") (?* . "."))
|
235 : |
|
|
"Syntax table for text-properties")
|
236 : |
|
|
|
237 : |
|
|
;; For Emacsen that have no built-in support for nested comments
|
238 : |
monnier |
300 |
(defun sml-get-depth-st ()
|
239 : |
|
|
(save-excursion
|
240 : |
|
|
(let* ((disp (if (eq (char-before) ?\)) (progn (backward-char) -1) nil))
|
241 : |
|
|
(foo (backward-char))
|
242 : |
|
|
(disp (if (eq (char-before) ?\() (progn (backward-char) 0) disp))
|
243 : |
|
|
(pt (point)))
|
244 : |
|
|
(when disp
|
245 : |
|
|
(let* ((depth
|
246 : |
|
|
(save-match-data
|
247 : |
|
|
(if (re-search-backward "\\*)\\|(\\*" nil t)
|
248 : |
|
|
(+ (or (get-char-property (point) 'comment-depth) 0)
|
249 : |
|
|
(case (char-after) (?\( 1) (?* 0))
|
250 : |
|
|
disp)
|
251 : |
|
|
0)))
|
252 : |
|
|
(depth (if (> depth 0) depth)))
|
253 : |
|
|
(put-text-property pt (1+ pt) 'comment-depth depth)
|
254 : |
monnier |
319 |
(when depth sml-syntax-prop-table))))))
|
255 : |
monnier |
300 |
|
256 : |
|
|
(defconst sml-font-lock-syntactic-keywords
|
257 : |
monnier |
535 |
`(("^\\s-*\\(\\\\\\)" (1 ',sml-syntax-prop-table))
|
258 : |
|
|
,@(unless sml-builtin-nested-comments-flag
|
259 : |
|
|
'(("(?\\(\\*\\))?" (1 (sml-get-depth-st)))))))
|
260 : |
monnier |
300 |
|
261 : |
|
|
(defconst sml-font-lock-defaults
|
262 : |
monnier |
319 |
'(sml-font-lock-keywords nil nil ((?_ . "w") (?' . "w")) nil
|
263 : |
monnier |
300 |
(font-lock-syntactic-keywords . sml-font-lock-syntactic-keywords)))
|
264 : |
|
|
|
265 : |
monnier |
33 |
|
266 : |
monnier |
32 |
;;; MORE CODE FOR SML-MODE
|
267 : |
|
|
|
268 : |
monnier |
535 |
;;;###Autoload
|
269 : |
|
|
(add-to-list 'auto-mode-alist '("\\.s\\(ml\\|ig\\)\\'" . sml-mode))
|
270 : |
monnier |
32 |
|
271 : |
|
|
;;;###Autoload
|
272 : |
monnier |
535 |
(define-derived-mode sml-mode fundamental-mode "SML"
|
273 : |
|
|
"\\<sml-mode-map>Major mode for editing ML code.
|
274 : |
|
|
This mode runs `sml-mode-hook' just before exiting.
|
275 : |
monnier |
32 |
\\{sml-mode-map}"
|
276 : |
monnier |
535 |
(set (make-local-variable 'font-lock-defaults) sml-font-lock-defaults)
|
277 : |
monnier |
319 |
(set (make-local-variable 'outline-regexp) sml-outline-regexp)
|
278 : |
monnier |
535 |
(sml-mode-variables))
|
279 : |
monnier |
32 |
|
280 : |
|
|
(defun sml-mode-variables ()
|
281 : |
|
|
(set-syntax-table sml-mode-syntax-table)
|
282 : |
|
|
(setq local-abbrev-table sml-mode-abbrev-table)
|
283 : |
|
|
;; A paragraph is separated by blank lines or ^L only.
|
284 : |
monnier |
33 |
|
285 : |
|
|
(set (make-local-variable 'paragraph-start)
|
286 : |
|
|
(concat "^[\t ]*$\\|" page-delimiter))
|
287 : |
|
|
(set (make-local-variable 'paragraph-separate) paragraph-start)
|
288 : |
|
|
(set (make-local-variable 'indent-line-function) 'sml-indent-line)
|
289 : |
|
|
(set (make-local-variable 'comment-start) "(* ")
|
290 : |
|
|
(set (make-local-variable 'comment-end) " *)")
|
291 : |
monnier |
535 |
(set (make-local-variable 'comment-nested) t)
|
292 : |
monnier |
378 |
;;(set (make-local-variable 'block-comment-start) "* ")
|
293 : |
|
|
;;(set (make-local-variable 'block-comment-end) "")
|
294 : |
monnier |
33 |
(set (make-local-variable 'comment-column) 40)
|
295 : |
monnier |
378 |
(set (make-local-variable 'comment-start-skip) "(\\*+\\s-*")
|
296 : |
monnier |
535 |
(set (make-local-variable 'comment-indent-function) 'sml-comment-indent))
|
297 : |
monnier |
32 |
|
298 : |
|
|
(defun sml-electric-pipe ()
|
299 : |
monnier |
319 |
"Insert a \"|\".
|
300 : |
monnier |
32 |
Depending on the context insert the name of function, a \"=>\" etc."
|
301 : |
|
|
(interactive)
|
302 : |
monnier |
319 |
(sml-with-ist
|
303 : |
monnier |
334 |
(unless (save-excursion (skip-chars-backward "\t ") (bolp)) (insert "\n"))
|
304 : |
|
|
(insert "| ")
|
305 : |
monnier |
319 |
(let ((text
|
306 : |
|
|
(save-excursion
|
307 : |
monnier |
334 |
(backward-char 2) ;back over the just inserted "| "
|
308 : |
|
|
(sml-find-matching-starter sml-pipehead-re
|
309 : |
|
|
(sml-op-prec "|" 'back))
|
310 : |
|
|
(let ((sym (sml-forward-sym)))
|
311 : |
|
|
(sml-forward-spaces)
|
312 : |
|
|
(cond
|
313 : |
|
|
((string= sym "|")
|
314 : |
|
|
(let ((f (sml-forward-sym)))
|
315 : |
|
|
(sml-find-forward "\\(=>\\|=\\||\\)\\S.")
|
316 : |
|
|
(cond
|
317 : |
|
|
((looking-at "|") "") ;probably a datatype
|
318 : |
|
|
((looking-at "=>") " => ") ;`case', or `fn' or `handle'
|
319 : |
|
|
((looking-at "=") (concat f " = "))))) ;a function
|
320 : |
|
|
((string= sym "and")
|
321 : |
|
|
;; could be a datatype or a function
|
322 : |
|
|
(while (and (setq sym (sml-forward-sym))
|
323 : |
|
|
(string-match "^'" sym))
|
324 : |
|
|
(sml-forward-spaces))
|
325 : |
|
|
(sml-forward-spaces)
|
326 : |
|
|
(if (or (not sym)
|
327 : |
|
|
(equal (sml-forward-sym) "d="))
|
328 : |
|
|
""
|
329 : |
|
|
(concat sym " = ")))
|
330 : |
|
|
;; trivial cases
|
331 : |
|
|
((string= sym "fun")
|
332 : |
|
|
(while (and (setq sym (sml-forward-sym))
|
333 : |
|
|
(string-match "^'" sym))
|
334 : |
|
|
(sml-forward-spaces))
|
335 : |
|
|
(concat sym " = "))
|
336 : |
monnier |
542 |
((member sym '("case" "handle" "fn" "of")) " => ")
|
337 : |
monnier |
334 |
((member sym '("abstype" "datatype")) "")
|
338 : |
|
|
(t (error "Wow, now, there's a bug")))))))
|
339 : |
monnier |
32 |
|
340 : |
monnier |
334 |
(insert text)
|
341 : |
monnier |
535 |
(indent-according-to-mode)
|
342 : |
monnier |
319 |
(beginning-of-line)
|
343 : |
|
|
(skip-chars-forward "\t |")
|
344 : |
|
|
(skip-syntax-forward "w")
|
345 : |
|
|
(skip-chars-forward "\t ")
|
346 : |
|
|
(when (= ?= (char-after)) (backward-char)))))
|
347 : |
|
|
|
348 : |
monnier |
32 |
(defun sml-electric-semi ()
|
349 : |
monnier |
535 |
"Insert a \;.
|
350 : |
|
|
If variable `sml-electric-semi-mode' is t, indent the current line, insert
|
351 : |
monnier |
32 |
a newline, and indent."
|
352 : |
|
|
(interactive)
|
353 : |
|
|
(insert "\;")
|
354 : |
|
|
(if sml-electric-semi-mode
|
355 : |
|
|
(reindent-then-newline-and-indent)))
|
356 : |
|
|
|
357 : |
|
|
;;; INDENTATION !!!
|
358 : |
|
|
|
359 : |
|
|
(defun sml-mark-function ()
|
360 : |
monnier |
535 |
"Synonym for `mark-paragraph' -- sorry.
|
361 : |
monnier |
32 |
If anyone has a good algorithm for this..."
|
362 : |
|
|
(interactive)
|
363 : |
|
|
(mark-paragraph))
|
364 : |
|
|
|
365 : |
monnier |
347 |
;; (defun sml-indent-region (begin end)
|
366 : |
|
|
;; "Indent region of ML code."
|
367 : |
|
|
;; (interactive "r")
|
368 : |
|
|
;; (message "Indenting region...")
|
369 : |
|
|
;; (save-excursion
|
370 : |
|
|
;; (goto-char end) (setq end (point-marker)) (goto-char begin)
|
371 : |
|
|
;; (while (< (point) end)
|
372 : |
|
|
;; (skip-chars-forward "\t\n ")
|
373 : |
monnier |
535 |
;; (indent-according-to-mode)
|
374 : |
monnier |
347 |
;; (end-of-line))
|
375 : |
|
|
;; (move-marker end nil))
|
376 : |
|
|
;; (message "Indenting region... done"))
|
377 : |
monnier |
32 |
|
378 : |
|
|
(defun sml-indent-line ()
|
379 : |
|
|
"Indent current line of ML code."
|
380 : |
|
|
(interactive)
|
381 : |
monnier |
535 |
(indent-line-to (sml-calculate-indentation)))
|
382 : |
monnier |
32 |
|
383 : |
|
|
(defun sml-back-to-outer-indent ()
|
384 : |
|
|
"Unindents to the next outer level of indentation."
|
385 : |
|
|
(interactive)
|
386 : |
|
|
(save-excursion
|
387 : |
|
|
(beginning-of-line)
|
388 : |
|
|
(skip-chars-forward "\t ")
|
389 : |
|
|
(let ((start-column (current-column))
|
390 : |
|
|
(indent (current-column)))
|
391 : |
|
|
(if (> start-column 0)
|
392 : |
|
|
(progn
|
393 : |
|
|
(save-excursion
|
394 : |
|
|
(while (>= indent start-column)
|
395 : |
|
|
(if (re-search-backward "^[^\n]" nil t)
|
396 : |
|
|
(setq indent (current-indentation))
|
397 : |
|
|
(setq indent 0))))
|
398 : |
|
|
(backward-delete-char-untabify (- start-column indent)))))))
|
399 : |
|
|
|
400 : |
monnier |
39 |
(defun sml-find-comment-indent ()
|
401 : |
|
|
(save-excursion
|
402 : |
|
|
(let ((depth 1))
|
403 : |
|
|
(while (> depth 0)
|
404 : |
|
|
(if (re-search-backward "(\\*\\|\\*)" nil t)
|
405 : |
|
|
(cond
|
406 : |
|
|
((looking-at "*)") (incf depth))
|
407 : |
monnier |
300 |
((looking-at comment-start-skip) (decf depth)))
|
408 : |
monnier |
39 |
(setq depth -1)))
|
409 : |
|
|
(if (= depth 0)
|
410 : |
monnier |
334 |
(1+ (current-column))
|
411 : |
monnier |
39 |
nil))))
|
412 : |
|
|
|
413 : |
monnier |
32 |
(defun sml-calculate-indentation ()
|
414 : |
|
|
(save-excursion
|
415 : |
monnier |
319 |
(beginning-of-line) (skip-chars-forward "\t ")
|
416 : |
|
|
(sml-with-ist
|
417 : |
monnier |
334 |
;; Indentation for comments alone on a line, matches the
|
418 : |
|
|
;; proper indentation of the next line.
|
419 : |
monnier |
378 |
(when (looking-at "(\\*") (sml-forward-spaces))
|
420 : |
monnier |
334 |
(let (data
|
421 : |
|
|
(sml-point (point))
|
422 : |
|
|
(sym (save-excursion (sml-forward-sym))))
|
423 : |
monnier |
319 |
(or
|
424 : |
monnier |
347 |
;; allow the user to override the indentation
|
425 : |
monnier |
378 |
(when (looking-at (concat ".*" (regexp-quote comment-start)
|
426 : |
|
|
"[ \t]*fixindent[ \t]*"
|
427 : |
|
|
(regexp-quote comment-end)))
|
428 : |
|
|
(current-indentation))
|
429 : |
monnier |
347 |
|
430 : |
monnier |
319 |
;; continued comment
|
431 : |
monnier |
334 |
(and (looking-at "\\*") (sml-find-comment-indent))
|
432 : |
monnier |
300 |
|
433 : |
monnier |
319 |
;; Continued string ? (Added 890113 lbn)
|
434 : |
|
|
(and (looking-at "\\\\")
|
435 : |
|
|
(save-excursion
|
436 : |
|
|
(if (save-excursion (previous-line 1)
|
437 : |
|
|
(beginning-of-line)
|
438 : |
|
|
(looking-at "[\t ]*\\\\"))
|
439 : |
|
|
(progn (previous-line 1) (current-indentation))
|
440 : |
|
|
(if (re-search-backward "[^\\\\]\"" nil t)
|
441 : |
monnier |
332 |
(1+ (current-column))
|
442 : |
monnier |
319 |
0))))
|
443 : |
monnier |
300 |
|
444 : |
monnier |
334 |
(and (setq data (assoc sym sml-close-paren))
|
445 : |
|
|
(sml-indent-relative sym data))
|
446 : |
monnier |
32 |
|
447 : |
monnier |
319 |
(and (looking-at sml-starters-re)
|
448 : |
monnier |
333 |
(let ((sym (unless (save-excursion (sml-backward-arg))
|
449 : |
|
|
(sml-backward-spaces)
|
450 : |
|
|
(sml-backward-sym))))
|
451 : |
monnier |
319 |
(if sym (sml-get-sym-indent sym)
|
452 : |
monnier |
334 |
;; FIXME: this can take a *long* time !!
|
453 : |
monnier |
319 |
(sml-find-matching-starter sml-starters-re)
|
454 : |
|
|
(current-column))))
|
455 : |
monnier |
300 |
|
456 : |
monnier |
334 |
(and (string= sym "|") (sml-indent-pipe))
|
457 : |
monnier |
300 |
|
458 : |
monnier |
319 |
(sml-indent-arg)
|
459 : |
|
|
(sml-indent-default))))))
|
460 : |
monnier |
300 |
|
461 : |
monnier |
334 |
(defun sml-indent-relative (sym data)
|
462 : |
|
|
(save-excursion
|
463 : |
|
|
(sml-forward-sym) (sml-backward-sexp nil)
|
464 : |
monnier |
339 |
(unless (second data) (sml-backward-spaces) (sml-backward-sym))
|
465 : |
monnier |
334 |
(+ (or (cdr (assoc sym sml-symbol-indent)) 0)
|
466 : |
|
|
(sml-delegated-indent))))
|
467 : |
|
|
|
468 : |
monnier |
319 |
(defun sml-indent-pipe ()
|
469 : |
monnier |
334 |
(when (sml-find-matching-starter sml-pipehead-re
|
470 : |
monnier |
319 |
(sml-op-prec "|" 'back))
|
471 : |
|
|
(if (looking-at "|")
|
472 : |
|
|
(if (sml-bolp) (current-column) (sml-indent-pipe))
|
473 : |
monnier |
341 |
(let ((pipe-indent (or (cdr (assoc "|" sml-symbol-indent)) -2)))
|
474 : |
|
|
(when (looking-at "\\(data\\|abs\\)type\\>")
|
475 : |
|
|
(re-search-forward "="))
|
476 : |
|
|
(sml-forward-sym)
|
477 : |
|
|
(sml-forward-spaces)
|
478 : |
|
|
(+ pipe-indent (current-column))))))
|
479 : |
monnier |
39 |
|
480 : |
monnier |
334 |
(defun sml-find-forward (re)
|
481 : |
|
|
(sml-forward-spaces)
|
482 : |
|
|
(while (and (not (looking-at re))
|
483 : |
|
|
(progn
|
484 : |
|
|
(or (ignore-errors (forward-sexp 1) t) (forward-char 1))
|
485 : |
|
|
(sml-forward-spaces)
|
486 : |
|
|
(not (looking-at re))))))
|
487 : |
monnier |
39 |
|
488 : |
monnier |
319 |
(defun sml-indent-arg ()
|
489 : |
|
|
(and (save-excursion (ignore-errors (sml-forward-arg)))
|
490 : |
|
|
;;(not (looking-at sml-not-arg-re))
|
491 : |
|
|
;; looks like a function or an argument
|
492 : |
|
|
(sml-move-if (sml-backward-arg))
|
493 : |
|
|
;; an argument
|
494 : |
|
|
(if (save-excursion (not (sml-backward-arg)))
|
495 : |
|
|
;; a first argument
|
496 : |
|
|
(+ (current-column) sml-indent-args)
|
497 : |
|
|
;; not a first arg
|
498 : |
|
|
(while (and (/= (current-column) (current-indentation))
|
499 : |
|
|
(sml-move-if (sml-backward-arg))))
|
500 : |
|
|
(unless (save-excursion (sml-backward-arg))
|
501 : |
|
|
;; all earlier args are on the same line
|
502 : |
|
|
(sml-forward-arg) (sml-forward-spaces))
|
503 : |
|
|
(current-column))))
|
504 : |
|
|
|
505 : |
monnier |
334 |
(defun sml-get-indent (data sym)
|
506 : |
|
|
(let ((head-sym (pop data)) d)
|
507 : |
|
|
(cond
|
508 : |
|
|
((not (listp data)) data)
|
509 : |
|
|
((setq d (member sym data)) (second d))
|
510 : |
|
|
((and (consp data) (not (stringp (car data)))) (car data))
|
511 : |
|
|
(t sml-indent-level))))
|
512 : |
monnier |
332 |
|
513 : |
monnier |
319 |
(defun sml-dangling-sym ()
|
514 : |
monnier |
32 |
(save-excursion
|
515 : |
monnier |
319 |
(and (not (sml-bolp))
|
516 : |
|
|
(< (sml-point-after (end-of-line))
|
517 : |
|
|
(sml-point-after (sml-forward-sym)
|
518 : |
|
|
(sml-forward-spaces))))))
|
519 : |
monnier |
32 |
|
520 : |
monnier |
334 |
(defun sml-delegated-indent ()
|
521 : |
|
|
(if (sml-dangling-sym)
|
522 : |
|
|
(sml-indent-default 'noindent)
|
523 : |
|
|
(sml-move-if (backward-word 1)
|
524 : |
monnier |
341 |
(looking-at sml-agglomerate-re))
|
525 : |
monnier |
334 |
(current-column)))
|
526 : |
|
|
|
527 : |
monnier |
319 |
(defun sml-get-sym-indent (sym &optional style)
|
528 : |
monnier |
535 |
"Find the indentation for the SYM we're `looking-at'.
|
529 : |
monnier |
332 |
If indentation is delegated, the point will be at the start of
|
530 : |
monnier |
535 |
the parent at the end of this function.
|
531 : |
|
|
Optional argument STYLE is currently ignored"
|
532 : |
monnier |
334 |
(assert (equal sym (save-excursion (sml-forward-sym))))
|
533 : |
|
|
(save-excursion
|
534 : |
|
|
(let ((delegate (assoc sym sml-close-paren))
|
535 : |
|
|
(head-sym sym))
|
536 : |
monnier |
339 |
(when (and delegate (not (eval (third delegate))))
|
537 : |
monnier |
334 |
;;(sml-find-match-backward sym delegate)
|
538 : |
|
|
(sml-forward-sym) (sml-backward-sexp nil)
|
539 : |
|
|
(setq head-sym
|
540 : |
monnier |
339 |
(if (second delegate)
|
541 : |
monnier |
334 |
(save-excursion (sml-forward-sym))
|
542 : |
|
|
(sml-backward-spaces) (sml-backward-sym))))
|
543 : |
monnier |
32 |
|
544 : |
monnier |
334 |
(let ((idata (assoc head-sym sml-indent-rule)))
|
545 : |
|
|
(when idata
|
546 : |
|
|
;;(if (or style (not delegate))
|
547 : |
|
|
;; normal indentation
|
548 : |
|
|
(let ((indent (sml-get-indent idata sym)))
|
549 : |
|
|
(when indent (+ (sml-delegated-indent) indent)))
|
550 : |
|
|
;; delgate indentation to the parent
|
551 : |
|
|
;;(sml-forward-sym) (sml-backward-sexp nil)
|
552 : |
|
|
;;(let* ((parent-sym (save-excursion (sml-forward-sym)))
|
553 : |
|
|
;; (parent-indent (cdr (assoc parent-sym sml-indent-starters))))
|
554 : |
|
|
;; check the special rules
|
555 : |
|
|
;;(+ (sml-delegated-indent)
|
556 : |
|
|
;; (or (sml-get-indent indent-data 1 'strict)
|
557 : |
|
|
;; (sml-get-indent parent-indent 1 'strict)
|
558 : |
|
|
;; (sml-get-indent indent-data 0)
|
559 : |
|
|
;; (sml-get-indent parent-indent 0))))))))
|
560 : |
|
|
)))))
|
561 : |
|
|
|
562 : |
monnier |
319 |
(defun sml-indent-default (&optional noindent)
|
563 : |
monnier |
333 |
(let* ((sym-after (save-excursion (sml-forward-sym)))
|
564 : |
monnier |
319 |
(_ (sml-backward-spaces))
|
565 : |
monnier |
333 |
(sym-before (sml-backward-sym))
|
566 : |
monnier |
332 |
(sym-indent (and sym-before (sml-get-sym-indent sym-before))))
|
567 : |
monnier |
334 |
(if sym-indent
|
568 : |
monnier |
339 |
;; the previous sym is an indentation introducer: follow the rule
|
569 : |
|
|
(let ((indent-after (or (cdr (assoc sym-after sml-symbol-indent)) 0)))
|
570 : |
|
|
(if noindent (current-column) (+ sym-indent indent-after)))
|
571 : |
|
|
;; default-default
|
572 : |
|
|
(let* ((prec-after (sml-op-prec sym-after 'back))
|
573 : |
|
|
(prec (or (sml-op-prec sym-before 'back) prec-after 100)))
|
574 : |
|
|
;; go back until you hit a symbol that has a lower prec than the
|
575 : |
|
|
;; "current one", or until you backed over a sym that has the same prec
|
576 : |
|
|
;; but is at the beginning of a line.
|
577 : |
|
|
(while (and (not (sml-bolp))
|
578 : |
|
|
(sml-move-if (sml-backward-sexp (1- prec)))
|
579 : |
|
|
(not (sml-bolp)))
|
580 : |
|
|
(while (sml-move-if (sml-backward-sexp prec))))
|
581 : |
|
|
;; the `noindent' case does back over an introductory symbol
|
582 : |
|
|
;; such as `fun', ...
|
583 : |
|
|
(when noindent
|
584 : |
|
|
(sml-move-if
|
585 : |
|
|
(sml-backward-spaces)
|
586 : |
|
|
(string-match sml-starters-re (or (sml-backward-sym) ""))))
|
587 : |
|
|
(current-column)))))
|
588 : |
monnier |
32 |
|
589 : |
monnier |
319 |
|
590 : |
|
|
(defun sml-bolp ()
|
591 : |
monnier |
32 |
(save-excursion
|
592 : |
monnier |
319 |
(skip-chars-backward " \t|") (bolp)))
|
593 : |
monnier |
32 |
|
594 : |
|
|
|
595 : |
monnier |
319 |
;; maybe `|' should be set to word-syntax in our temp syntax table ?
|
596 : |
|
|
(defun sml-current-indentation ()
|
597 : |
monnier |
32 |
(save-excursion
|
598 : |
monnier |
319 |
(beginning-of-line)
|
599 : |
|
|
(skip-chars-forward " \t|")
|
600 : |
|
|
(current-column)))
|
601 : |
monnier |
32 |
|
602 : |
monnier |
300 |
|
603 : |
monnier |
319 |
(defun sml-find-matching-starter (regexp &optional prec)
|
604 : |
monnier |
334 |
(ignore-errors
|
605 : |
|
|
(sml-backward-sexp prec)
|
606 : |
|
|
(while (not (or (looking-at regexp) (bobp)))
|
607 : |
|
|
(sml-backward-sexp prec))
|
608 : |
|
|
(not (bobp))))
|
609 : |
monnier |
319 |
|
610 : |
monnier |
32 |
(defun sml-comment-indent ()
|
611 : |
|
|
(if (looking-at "^(\\*") ; Existing comment at beginning
|
612 : |
|
|
0 ; of line stays there.
|
613 : |
|
|
(save-excursion
|
614 : |
|
|
(skip-chars-backward " \t")
|
615 : |
|
|
(max (1+ (current-column)) ; Else indent at comment column
|
616 : |
|
|
comment-column)))) ; except leave at least one space.
|
617 : |
|
|
|
618 : |
monnier |
535 |
;;; INSERTING PROFORMAS (COMMON SML-FORMS)
|
619 : |
monnier |
32 |
|
620 : |
monnier |
334 |
(defvar sml-forms-alist nil
|
621 : |
monnier |
535 |
"*Alist of code templates.
|
622 : |
|
|
You can extend this alist to your heart's content. For each additional
|
623 : |
monnier |
32 |
template NAME in the list, declare a keyboard macro or function (or
|
624 : |
|
|
interactive command) called 'sml-form-NAME'.
|
625 : |
|
|
If 'sml-form-NAME' is a function it takes no arguments and should
|
626 : |
|
|
insert the template at point\; if this is a command it may accept any
|
627 : |
|
|
sensible interactive call arguments\; keyboard macros can't take
|
628 : |
monnier |
535 |
arguments at all. Apropos keyboard macros, see `name-last-kbd-macro'
|
629 : |
monnier |
32 |
and `sml-addto-forms-alist'.
|
630 : |
|
|
`sml-forms-alist' understands let, local, case, abstype, datatype,
|
631 : |
|
|
signature, structure, and functor by default.")
|
632 : |
|
|
|
633 : |
monnier |
334 |
(defmacro sml-def-skeleton (name interactor &rest elements)
|
634 : |
|
|
(let ((fsym (intern (concat "sml-form-" name))))
|
635 : |
|
|
`(progn
|
636 : |
|
|
(add-to-list 'sml-forms-alist ',(cons name fsym))
|
637 : |
|
|
(define-skeleton ,fsym
|
638 : |
|
|
,(format "SML-mode skeleton for `%s..' expressions" name)
|
639 : |
|
|
,interactor
|
640 : |
monnier |
341 |
,(concat name " ") >
|
641 : |
monnier |
334 |
,@elements))))
|
642 : |
|
|
(put 'sml-def-skeleton 'lisp-indent-function 2)
|
643 : |
monnier |
32 |
|
644 : |
monnier |
334 |
(sml-def-skeleton "let" nil
|
645 : |
|
|
_ "\nin" > "\nend" >)
|
646 : |
monnier |
32 |
|
647 : |
monnier |
334 |
(sml-def-skeleton "if" nil
|
648 : |
|
|
_ " then " > "\nelse " >)
|
649 : |
monnier |
32 |
|
650 : |
monnier |
334 |
(sml-def-skeleton "local" nil
|
651 : |
|
|
_ "\nin" > "\nend" >)
|
652 : |
monnier |
32 |
|
653 : |
monnier |
334 |
(sml-def-skeleton "case" "Case expr: "
|
654 : |
monnier |
341 |
str "\nof " > _ " => ")
|
655 : |
monnier |
32 |
|
656 : |
monnier |
334 |
(sml-def-skeleton "signature" "Signature name: "
|
657 : |
|
|
str " =\nsig" > "\n" > _ "\nend" >)
|
658 : |
monnier |
32 |
|
659 : |
monnier |
334 |
(sml-def-skeleton "structure" "Structure name: "
|
660 : |
|
|
str " =\nstruct" > "\n" > _ "\nend" >)
|
661 : |
monnier |
32 |
|
662 : |
monnier |
334 |
(sml-def-skeleton "functor" "Functor name: "
|
663 : |
|
|
str " () : =\nstruct" > "\n" > _ "\nend" >)
|
664 : |
|
|
|
665 : |
|
|
(sml-def-skeleton "datatype" "Datatype name and type parameters: "
|
666 : |
|
|
str " =" \n)
|
667 : |
|
|
|
668 : |
|
|
(sml-def-skeleton "abstype" "Abstype name and type parameters: "
|
669 : |
|
|
str " =" \n _ "\nwith" > "\nend" >)
|
670 : |
|
|
|
671 : |
|
|
;;
|
672 : |
|
|
|
673 : |
|
|
(defun sml-forms-menu (menu)
|
674 : |
|
|
(easy-menu-filter-return
|
675 : |
|
|
(easy-menu-create-menu "Forms"
|
676 : |
|
|
(mapcar (lambda (x)
|
677 : |
|
|
(let ((name (car x))
|
678 : |
|
|
(fsym (cdr x)))
|
679 : |
|
|
(vector name fsym t)))
|
680 : |
|
|
sml-forms-alist))))
|
681 : |
|
|
|
682 : |
|
|
(defvar sml-last-form "let")
|
683 : |
|
|
|
684 : |
monnier |
341 |
(defun sml-electric-space ()
|
685 : |
|
|
"Expand a symbol into an SML form, or just insert a space.
|
686 : |
|
|
If the point directly precedes a symbol for which an SML form exists,
|
687 : |
|
|
the corresponding form is inserted."
|
688 : |
|
|
(interactive)
|
689 : |
|
|
(let* ((point (point))
|
690 : |
|
|
(sym (sml-backward-sym)))
|
691 : |
|
|
(if (not (and sym (assoc sym sml-forms-alist)))
|
692 : |
|
|
(progn (goto-char point) (insert " "))
|
693 : |
|
|
(delete-region (point) point)
|
694 : |
|
|
(sml-insert-form sym nil))))
|
695 : |
|
|
|
696 : |
monnier |
334 |
(defun sml-insert-form (name newline)
|
697 : |
monnier |
535 |
"Interactive short-cut to insert the NAME common ML form.
|
698 : |
|
|
If a prefix argument is given insert a NEWLINE and indent first, or
|
699 : |
monnier |
32 |
just move to the proper indentation if the line is blank\; otherwise
|
700 : |
|
|
insert at point (which forces indentation to current column).
|
701 : |
|
|
|
702 : |
|
|
The default form to insert is 'whatever you inserted last time'
|
703 : |
monnier |
535 |
\(just hit return when prompted\)\; otherwise the command reads with
|
704 : |
monnier |
32 |
completion from `sml-forms-alist'."
|
705 : |
monnier |
334 |
(interactive
|
706 : |
|
|
(list (completing-read
|
707 : |
|
|
(format "Form to insert: (default %s) " sml-last-form)
|
708 : |
|
|
sml-forms-alist nil t nil)
|
709 : |
|
|
current-prefix-arg))
|
710 : |
|
|
;; default is whatever the last insert was...
|
711 : |
|
|
(if (string= name "") (setq name sml-last-form) (setq sml-last-form name))
|
712 : |
|
|
(unless (or (not newline)
|
713 : |
|
|
(save-excursion (beginning-of-line) (looking-at "\\s-*$")))
|
714 : |
|
|
(insert "\n"))
|
715 : |
monnier |
341 |
(unless (/= ?w (char-syntax (char-before))) (insert " "))
|
716 : |
monnier |
334 |
(let ((f (cdr (assoc name sml-forms-alist))))
|
717 : |
|
|
(cond
|
718 : |
|
|
((commandp f) (command-execute f))
|
719 : |
|
|
(f (funcall f))
|
720 : |
|
|
(t (error "Undefined form: %s" name)))))
|
721 : |
monnier |
32 |
|
722 : |
monnier |
334 |
;; See also macros.el in emacs lisp dir.
|
723 : |
monnier |
32 |
|
724 : |
monnier |
334 |
(defun sml-addto-forms-alist (name)
|
725 : |
|
|
"Assign a name to the last keyboard macro defined.
|
726 : |
|
|
Argument NAME is transmogrified to sml-form-NAME which is the symbol
|
727 : |
|
|
actually defined.
|
728 : |
monnier |
32 |
|
729 : |
monnier |
334 |
The symbol's function definition becomes the keyboard macro string.
|
730 : |
monnier |
32 |
|
731 : |
monnier |
334 |
If that works, NAME is added to `sml-forms-alist' so you'll be able to
|
732 : |
monnier |
535 |
reinvoke the macro through \\[sml-insert-form]. You might want to save
|
733 : |
monnier |
334 |
the macro to use in a later editing session -- see `insert-kbd-macro'
|
734 : |
|
|
and add these macros to your .emacs file.
|
735 : |
monnier |
32 |
|
736 : |
monnier |
334 |
See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]."
|
737 : |
|
|
(interactive "sName for last kbd macro (\"sml-form-\" will be added): ")
|
738 : |
|
|
(when (string= name "") (error "No command name given"))
|
739 : |
|
|
(let ((fsym (intern (concat "sml-form-" name))))
|
740 : |
|
|
(name-last-kbd-macro fsym)
|
741 : |
|
|
(message "Macro bound to %s" fsym)
|
742 : |
|
|
(add-to-list 'sml-forms-alist (cons name fsym))))
|
743 : |
monnier |
32 |
|
744 : |
monnier |
535 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
745 : |
|
|
;;;; SML/NJ's Compilation Manager support ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
746 : |
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
747 : |
monnier |
32 |
|
748 : |
monnier |
535 |
;;;###autoload
|
749 : |
|
|
(add-to-list 'completion-ignored-extensions "CM/")
|
750 : |
|
|
;;;###autoload
|
751 : |
|
|
(add-to-list 'auto-mode-alist '("\\.cm\\'" . sml-cm-mode))
|
752 : |
|
|
;;;###autoload
|
753 : |
|
|
(define-generic-mode 'sml-cm-mode
|
754 : |
|
|
'(("(*" . "*)"))
|
755 : |
|
|
'("library" "Library" "LIBRARY" "group" "Group" "GROUP" "is" "IS"
|
756 : |
|
|
"structure" "functor" "signature" "funsig")
|
757 : |
|
|
nil '("\\.cm\\'")
|
758 : |
|
|
(list (lambda () (local-set-key "\C-c\C-c" 'sml-compile)))
|
759 : |
|
|
"Generic mode for SML/NJ's Compilation Manager configuration files.")
|
760 : |
monnier |
32 |
|
761 : |
monnier |
535 |
|
762 : |
monnier |
319 |
(provide 'sml-mode)
|
763 : |
monnier |
535 |
|
764 : |
|
|
;;; sml-mode.el ends here
|