|
|
1.1 root 1: ;; sml-mode.el. Major mode for editing (Standard) ML.
2: ;; Copyright (C) 1989, Free Software Foundation, Inc.
3:
4: ;; This file is part of GNU Emacs.
5:
6: ;; GNU Emacs is distributed in the hope that it will be useful,
7: ;; but WITHOUT ANY WARRANTY. No author or distributor
8: ;; accepts responsibility to anyone for the consequences of using it
9: ;; or for whether it serves any particular purpose or works at all,
10: ;; unless he says so in writing. Refer to the GNU Emacs General Public
11: ;; License for full details.
12:
13: ;; Everyone is granted permission to copy, modify and redistribute
14: ;; GNU Emacs, but only under the conditions described in the
15: ;; GNU Emacs General Public License. A copy of this license is
16: ;; supposed to have been given to you along with GNU Emacs so you
17: ;; can know your rights and responsibilities. It should be in a
18: ;; file named COPYING. Among other things, the copyright notice
19: ;; and this notice must be preserved on all copies.
20:
21: ;; AUTHOR Lars Bo Nielsen
22: ;; Aalborg University
23: ;; Computer Science Dept.
24: ;; 9000 Aalborg
25: ;; Denmark
26: ;;
27: ;; EMAIL [email protected]
28: ;; or: ...!mcvax!diku!iesd!lbn
29: ;; or: [email protected]
30: ;;
31: ;; Please let me know if you come up with any ideas, bugs, or fixes.
32: ;;
33:
34:
35: (provide 'sml-mode)
36:
37: (defconst sml-mode-version-string
38: "SML-MODE, Version 2.4 (Oct 1989) ([email protected])")
39:
40: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41: ;;;
42: ;;; CONSTANTS CONTROLLING THE MODE.
43: ;;;
44: ;;; These are the constants you might want to change
45: ;;;
46:
47: ;; The amount of indentation of blocks
48: (defconst sml-indent-level 4 "*Indentation of blocks in sml.")
49:
50: ;; The amount of negative indentation of lines beginning with "|"
51: (defconst sml-pipe-indent -2
52: "*Extra (negative) indentation for lines beginning with |.") ;
53:
54: ;; How do we indent case-of expressions.
55: (defconst sml-case-indent nil
56: "*How to indent case-of expressions.
57: If t: case expr If nil: case expr of
58: of exp1 => ... exp1 => ...
59: | exp2 => ... | exp2 => ...
60: \nThe first seems to be the standard in NJ-SML. The second is the default.")
61:
62: (defconst sml-nested-if-indent nil
63: "*If set to t, nested if-then-else expression will have the same
64: indentation as:
65: if exp1 then exp2
66: else if exp3 then exp4
67: else if exp5 then exp6
68: else exp7")
69:
70: (defconst sml-type-of-indent t
71: "*How to indent `let' `struct' etc.
72:
73: If t: fun foo bar = let If nil: fun foo bar = let
74: val p = 4 val p = 4
75: in in
76: bar + p bar + p
77: end end
78:
79: Will not have any effect if the starting keyword is first on the line.")
80:
81: (defconst sml-electric-semi-mode t
82: "*If t, a `\;' will insert itself, reindent the line, and perform a newline.
83: If nil, just insert a `\;'. (To insert while t, do: C-q \;).")
84:
85: ;; How far should the indentation algorithm look to find open parenthesis
86: (defconst sml-paren-lookback 200
87: "*Determines how far back (in chars) the indentation algorithm
88: should look for open parenthesis. High value means slow indentation
89: algorithm. A value of 200 (being the equivalent of 4-6 lines) should
90: suffice most uses. (A value of nil, means do not look at all)")
91:
92: ;; The command used to start up the sml-program.
93: (defconst sml-prog-name "sml" "*Name of program to run as sml.")
94:
95: ;; If t, you will be asked which program to run when the inferior
96: ;; shell starts up. Usefull if you have exported images of sml.
97: (defconst sml-prog-name-ask-p nil
98: "*Should you be asked for the name of the program to run.")
99:
100: ;; The left delimmitter for `use file'
101: (defconst sml-use-left-delim "\""
102: "*The left delimiter for the filename when using \"use\".
103: To be set to `[\\\"' for Edinburgh SML, and `\\\"' for New Jersey SML.
104: Correspondes to `sml-use-right-delim'.")
105:
106: ;; The right delimmitter for `use file'
107: (defconst sml-use-right-delim "\""
108: "*The right delimiter for the filename when using \"use\".
109: To be set to `\\\"]' for Edinburgh SML, and `\\\"' for New Jersey SML.
110: Correspondes to `sml-use-left-delim'.")
111:
112: ;; A regular expression matching the prompt pattern in the inferior
113: ;; shell
114: (defconst sml-shell-prompt-pattern "^[^\-=]*[\-=] *"
115: "*The prompt pattern for the inferion shell running sml.")
116:
117: ;; The template used for temporary files, created when a region is
118: ;; send to the inferior process running sml.
119: (defconst sml-tmp-template "/tmp/sml.tmp."
120: "*Template for the temporary file, created by sml-simulate-send-region.")
121:
122: ;; The name of the process running sml (This will also be the name of
123: ;; the buffer).
124: (defconst sml-process-name "SML" "*The name of the SML-process")
125:
126: ;;;
127: ;;; END OF CONSTANTS CONTROLLING THE MODE.
128: ;;;
129: ;;; If you change anything below, you are on your own.
130: ;;;
131: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
132:
133:
134: (defvar sml-mode-map nil "The mode map used in sml-mode.")
135: (if sml-mode-map
136: ()
137: (setq sml-mode-map (make-sparse-keymap))
138: (define-key sml-mode-map "\C-c'" 'sml-next-error)
139: (define-key sml-mode-map "\C-c\C-v" 'sml-mode-version)
140: (define-key sml-mode-map "\C-c\C-u" 'sml-save-buffer-use-file)
141: (define-key sml-mode-map "\C-c\C-s" 'sml-pop-to-shell)
142: (define-key sml-mode-map "\C-c\C-r" 'sml-send-region)
143: (define-key sml-mode-map "\C-c\C-m" 'sml-region)
144: (define-key sml-mode-map "\C-c\C-k" 'sml-skip-errors)
145: (define-key sml-mode-map "\C-c\C-f" 'sml-run-on-file)
146: (define-key sml-mode-map "\C-c\C-c" 'sml-send-function)
147: (define-key sml-mode-map "\C-c\C-b" 'sml-send-buffer)
148: (define-key sml-mode-map "\C-ci" 'sml-import-file)
149: (define-key sml-mode-map "\e|" 'sml-electric-pipe)
150: (define-key sml-mode-map "\C-j" 'reindent-then-newline-and-indent)
151: (define-key sml-mode-map "\177" 'backward-delete-char-untabify)
152: (define-key sml-mode-map "\;" 'sml-electric-semi)
153: (define-key sml-mode-map "\C-c\t" 'sml-indent-region)
154: (define-key sml-mode-map "\t" 'sml-indent-line))
155:
156: (defvar sml-mode-syntax-table nil "The syntax table used in sml-mode.")
157: (if sml-mode-syntax-table
158: ()
159: (setq sml-mode-syntax-table (make-syntax-table))
160: (modify-syntax-entry ?\( "()1" sml-mode-syntax-table)
161: (modify-syntax-entry ?\) ")(4" sml-mode-syntax-table)
162: (modify-syntax-entry ?\\ "\\" sml-mode-syntax-table)
163: (modify-syntax-entry ?* ". 23" sml-mode-syntax-table)
164: ;; Special characters in sml-mode to be treated as normal
165: ;; characters:
166: (modify-syntax-entry ?_ "w" sml-mode-syntax-table)
167: (modify-syntax-entry ?\' "w" sml-mode-syntax-table))
168:
169:
170: (defun sml-mode ()
171: "Major mode for editing SML code.
172: Tab indents for SML code.
173: Comments are delimited with (* ... *).
174: Paragraphs are separated by blank lines only.
175: Delete converts tabs to spaces as it moves back.
176:
177: Key bindings:
178: =============
179:
180: \\[sml-indent-line]\t - Indent current line.
181: \\[reindent-then-newline-and-indent]\t - Reindent line, newline and indent.
182: \\[sml-indent-region]\t - Indent region.
183: \\[sml-electric-pipe]\t - Insert a \"|\". Insert function name, \"=>\" etc.
184: \\[sml-region]\t - Insert a common used structure.
185: \\[sml-pop-to-shell]\t - Pop to the sml window.
186: \\[sml-next-error]\t - Find the next error.
187: \\[sml-save-buffer-use-file]\t - Save the buffer, and send a \"use file\".
188: \\[sml-send-region]\t - Send region (point and mark) to sml.
189: \\[sml-run-on-file]\t - Send a \"use file\" to sml.
190: \\[sml-import-file]\t - Send a \"import file\" to sml.
191: \\[sml-send-function]\t - Send function to sml.
192: \\[sml-send-buffer]\t - Send whole buffer to sml.
193: \\[sml-mode-version]\t - Get the version of sml-mode
194:
195:
196: Variables controlling the indentation
197: =====================================
198:
199: sml-indent-level (default 4)
200: The indentation of a block of code.
201:
202: sml-pipe-indent (default -2)
203: Extra indentation of a line starting with \"|\".
204:
205: sml-case-indent (default nil)
206: Determine the way to indent case-of expression.
207: If t: case expr If nil: case expr of
208: of exp1 => ... exp1 => ...
209: | exp2 => ... | exp2 => ...
210:
211: The first seems to be the standard in NJ-SML. The second is the default.
212:
213: sml-nested-if-indent (default nil)
214: If set to t, nested if-then-else expression will have the same
215: indentation as:
216: if exp1 then exp2
217: else if exp3 then exp4
218: else if exp5 then exp6
219: else exp7
220:
221: sml-type-of-indent (default t)
222: How to indent `let' `struct' etc.
223:
224: If t: fun foo bar = let If nil: fun foo bar = let
225: val p = 4 val p = 4
226: in in
227: bar + p bar + p
228: end end
229:
230: Will not have any effect if the starting keyword is first on the line.
231:
232: sml-electric-semi-mode (default t)
233: If t, a `\;' will reindent line, and perform a newline.
234:
235: Mode map
236: ========
237: \\{sml-mode-map}
238: Runs sml-mode-hook if non nil."
239: (interactive)
240: (kill-all-local-variables)
241: (use-local-map sml-mode-map)
242: (setq major-mode 'sml-mode)
243: (setq mode-name "Sml")
244: (define-abbrev-table 'sml-mode-abbrev-table ())
245: (setq local-abbrev-table sml-mode-abbrev-table)
246: (set-syntax-table sml-mode-syntax-table)
247: ;; A paragraph is seperated by blank lines (or ^L) only.
248: (make-local-variable 'paragraph-start)
249: (setq paragraph-start (concat "^[\t ]*$\\|" page-delimiter))
250: (make-local-variable 'paragraph-separate)
251: (setq paragraph-separate paragraph-start)
252: (make-local-variable 'indent-line-function)
253: (setq indent-line-function 'sml-indent-line)
254: (make-local-variable 'require-final-newline) ; Always put a new-line
255: (setq require-final-newline t) ; in the end of file
256: (make-local-variable 'comment-start)
257: (setq comment-start "(* ")
258: (make-local-variable 'comment-end)
259: (setq comment-end " *)")
260: (make-local-variable 'comment-column)
261: (setq comment-column 39) ; Start of comment in this column
262: (make-local-variable 'comment-start-skip)
263: (setq comment-start-skip "(\\*+[ \t]?") ; This matches a start of comment
264: (make-local-variable 'comment-indent-hook)
265: (setq comment-indent-hook 'sml-comment-indent)
266: ;;
267: ;; Adding these will fool the matching of parens. I really don't
268: ;; know why. It would be nice to have comments treated as
269: ;; white-space
270: ;;
271: ;; (make-local-variable 'parse-sexp-ignore-comments)
272: ;; (setq parse-sexp-ignore-comments t)
273: ;;
274: (run-hooks 'sml-mode-hook)) ; Run the hook
275:
276: (defconst sml-pipe-matchers-reg
277: "\\bcase\\b\\|\\bfn\\b\\|\\bfun\\b\\|\\bhandle\\b\
278: \\|\\bdatatype\\b\\|\\babstype\\b\\|\\band\\b"
279: "The keywords a `|' can follow.")
280:
281: (defun sml-electric-pipe ()
282: "Insert a \"|\". Depending on the context insert the name of
283: function, a \"=>\" etc."
284: (interactive)
285: (let ((here (point))
286: (match (save-excursion
287: (sml-find-matching-starter sml-pipe-matchers-reg)
288: (point)))
289: (tmp " => ")
290: (case-or-handle-exp t))
291: (if (/= (save-excursion (beginning-of-line) (point))
292: (save-excursion (skip-chars-backward "\t ") (point)))
293: (insert "\n"))
294: (insert "|")
295: (save-excursion
296: (goto-char match)
297: (cond
298: ;; It was a function, insert the function name
299: ((looking-at "fun\\b")
300: (setq tmp (concat " " (buffer-substring
301: (progn (forward-char 3)
302: (skip-chars-forward "\t\n ") (point))
303: (progn (forward-word 1) (point))) " "))
304: (setq case-or-handle-exp nil))
305: ;; It was a datatype, insert nothing
306: ((looking-at "datatype\\b\\|abstype\\b")
307: (setq tmp " ") (setq case-or-handle-exp nil))
308: ;; If is and, then we have to see what is was
309: ((looking-at "and\\b")
310: (let (isfun)
311: (save-excursion
312: (condition-case ()
313: (progn
314: (re-search-backward "datatype\\b\\|abstype\\b\\|fun\\b")
315: (setq isfun (looking-at "fun\\b")))
316: (error (setq isfun nil))))
317: (if isfun
318: (progn
319: (setq tmp
320: (concat " " (buffer-substring
321: (progn (forward-char 3)
322: (skip-chars-forward "\t\n ") (point))
323: (progn (forward-word 1) (point))) " "))
324: (setq case-or-handle-exp nil))
325: (setq tmp " ") (setq case-or-handle-exp nil))))))
326: (insert tmp)
327: (sml-indent-line)
328: (beginning-of-line)
329: (skip-chars-forward "\t ")
330: (forward-char (1+ (length tmp)))
331: (if case-or-handle-exp
332: (forward-char -4))))
333:
334: (defun sml-electric-semi ()
335: "If sml-electric-semi-mode is t, indent the current line, and newline."
336: (interactive)
337: (insert "\;")
338: (if sml-electric-semi-mode
339: (reindent-then-newline-and-indent)))
340:
341: (defun sml-mode-version ()
342: (interactive)
343: (message sml-mode-version-string))
344:
345:
346: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
347: ;;;
348: ;;; SHORT CUTS (sml-region)
349: ;;;
350: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
351:
352: (defconst sml-region-alist
353: '(("let") ("local") ("signature") ("structure") ("datatype")
354: ("case") ("functor") ("abstype"))
355: "The list of regions to auto-insert.")
356:
357: (defun sml-region ()
358: "Interactive short-cut. Insert a common used structure in sml."
359: (interactive)
360: (let ((newline nil) ; Did we insert a newline
361: (name (completing-read "Region to insert: (default let) "
362: sml-region-alist nil t nil)))
363: ;; default is "let"
364: (if (string= name "") (setq name "let"))
365: ;; Insert a newline if point is not at empty line
366: (sml-indent-line) ; Indent the current line
367: (if (save-excursion (beginning-of-line) (skip-chars-forward "\t ") (eolp))
368: ()
369: (setq newline t)
370: (insert "\n"))
371: (condition-case ()
372: (cond
373: ((string= name "let") (sml-let))
374: ((string= name "local") (sml-local))
375: ((string= name "structure") (sml-structure))
376: ((string= name "signature") (sml-signature))
377: ((string= name "functor") (sml-functor))
378: ((string= name "case") (sml-case))
379: ((string= name "abstype") (sml-abstype))
380: ((string= name "datatype") (sml-datatype)))
381: (quit (if newline
382: (progn
383: (delete-char -1)
384: (beep)))))))
385:
386: (defun sml-let ()
387: "Insert a `let in end'."
388: (interactive) (sml-let-local "let"))
389:
390: (defun sml-local ()
391: "Insert a `local in end'."
392: (interactive) (sml-let-local "local"))
393:
394: (defun sml-signature ()
395: "Insert a `signature ??? = sig end', prompting for name."
396: (interactive) (sml-structure-signature "signature"))
397:
398: (defun sml-structure ()
399: "Insert a `structure ??? = struct end', prompting for name."
400: (interactive) (sml-structure-signature "structure"))
401:
402: (defun sml-case ()
403: "Insert a case, prompting for case-expresion."
404: (interactive)
405: (let (indent (expr (read-string "Case expr: ")))
406: (insert (concat "case " expr))
407: (sml-indent-line)
408: (setq indent (current-indentation))
409: (end-of-line)
410: (if sml-case-indent
411: (progn
412: (insert "\n")
413: (indent-to (+ 2 indent))
414: (insert "of "))
415: (insert " of\n")
416: (indent-to (+ indent sml-indent-level)))
417: (save-excursion (insert " => "))))
418:
419: (defun sml-let-local (starter)
420: (let (indent)
421: (insert starter)
422: (sml-indent-line)
423: (setq indent (current-indentation))
424: (end-of-line)
425: (insert "\n") (indent-to (+ sml-indent-level indent))
426: (insert "\n") (indent-to indent)
427: (insert "in\n") (indent-to (+ sml-indent-level indent))
428: (insert "\n") (indent-to indent)
429: (insert "end") (previous-line 3) (end-of-line)))
430:
431: (defun sml-structure-signature (which)
432: (let (indent
433: (name (read-string (concat "Name of " which ": "))))
434: (insert (concat which " " name " ="))
435: (sml-indent-line)
436: (setq indent (current-indentation))
437: (end-of-line)
438: (insert "\n") (indent-to (+ sml-indent-level indent))
439: (insert (if (string= which "signature") "sig\n" "struct\n"))
440: (indent-to (+ (* 2 sml-indent-level) indent))
441: (insert "\n") (indent-to (+ sml-indent-level indent))
442: (insert "end") (previous-line 1) (end-of-line)))
443:
444: (defun sml-functor ()
445: "Insert a `funtor ??? () : ??? = struct end', prompting for name and type."
446: (let (indent
447: (name (read-string "Name of functor: "))
448: (signame (read-string "Signature type of functor: ")))
449: (insert (concat "functor " name " () : " signame " ="))
450: (sml-indent-line)
451: (setq indent (current-indentation))
452: (end-of-line)
453: (insert "\n") (indent-to (+ sml-indent-level indent))
454: (insert "struct\n")
455: (indent-to (+ (* 2 sml-indent-level) indent))
456: (insert "\n") (indent-to (+ sml-indent-level indent))
457: (insert "end") (previous-line 1) (end-of-line)))
458:
459: (defun sml-datatype ()
460: "Insert a `datatype ??? =', prompting for name."
461: (let (indent
462: (type (read-string (concat "Type of datatype (default none): ")))
463: (name (read-string (concat "Name of datatype: "))))
464: (insert (concat "datatype "
465: (if (string= type "") "" (concat type " "))
466: name " ="))
467: (sml-indent-line)
468: (setq indent (current-indentation))
469: (end-of-line) (insert "\n") (indent-to (+ sml-indent-level indent))))
470:
471: (defun sml-abstype ()
472: "Insert an `abstype 'a ??? = with ... end'"
473: (let (indent
474: (typevar (read-string "Name of typevariable (default 'a): "))
475: (type (read-string "Name of abstype: ")))
476: (if (string= typevar "")
477: (setq typevar "'a"))
478: (insert (concat "abstype " typevar " " type " ="))
479: (sml-indent-line)
480: (setq indent (current-indentation))
481: (insert "\n") (indent-to (+ sml-indent-level indent))
482: (insert "\n") (indent-to indent)
483: (insert "with\n") (indent-to (+ sml-indent-level indent))
484: (insert "\n") (indent-to indent)
485: (insert "end")
486: (previous-line 3)
487: (end-of-line)))
488:
489: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
490: ;;;
491: ;;; PARSING ERROR MESSAGES (NOTE: works only with SML of New Jersey)
492: ;;;
493: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
494:
495: (defvar sml-last-error 1 "Last position of error. Initially 1.")
496:
497: (defun sml-next-error ()
498: "Find the next error by passing the *SML* buffer.\n
499: NOTE: This function only knows about the syntax of errors generated by
500: SML of New Jersey, and will only work with this."
501: (interactive)
502: (let ((found t) (tmp-file nil) found-line found-file error-line tmp)
503: (save-excursion
504: (condition-case ()
505: (progn
506: (set-buffer (concat "*" sml-process-name "*" ))
507: (goto-char sml-last-error)
508: (re-search-forward "^.+line.+\\(Error:\\|Warning:\\)")
509: (save-excursion
510: (beginning-of-line)
511: (if (looking-at sml-tmp-template)
512: (setq tmp-file t)))
513: (setq sml-last-error (point))
514: (beginning-of-line)
515: (setq error-line (point))
516: (search-forward ",")
517: (setq found-file (buffer-substring error-line (1- (point))))
518: (search-forward "line ")
519: (setq tmp (point))
520: (skip-chars-forward "[0-9]")
521: (setq found-line (string-to-int (buffer-substring tmp (point)))))
522: (error (setq found nil))))
523: (if found
524: (progn
525: (set-window-start
526: (display-buffer (concat "*" sml-process-name "*")) error-line)
527: (if tmp-file
528: (let ((loop t) (n 0) (tmp-list sml-tmp-files-list))
529: (while loop
530: (setq tmp (car tmp-list))
531: (if (string= (car tmp) found-file)
532: (setq loop nil)
533: (setq tmp-list (cdr tmp-list)))
534: (if (null tmp-list) (setq loop nil)))
535: (if (null tmp)
536: (error "Temporary file not associated with buffer.")
537: (condition-case ()
538: (progn
539: (switch-to-buffer (nth 1 tmp))
540: (message
541: (concat "Error found in temporary file "
542: "(line number may not match)."))
543: (goto-line (1- (+ found-line (nth 2 tmp)))))
544: (error (error "Sorry, buffer doesn't exist any more.")))))
545: (if (file-exists-p found-file)
546: (progn
547: (condition-case ()
548: (progn
549: (find-file found-file)
550: (goto-line found-line))
551: (error ())))
552: (error (concat "File not found: " found-file)))))
553: ;; No error found
554: (if (= sml-last-error 1) ; Did we just start
555: (message "No errors yet")
556: (message "No more errors")) ; Or have we passed all errors
557: (beep))))
558:
559: (defun sml-skip-errors ()
560: "Skip past the rest of the errors."
561: (interactive)
562: (save-excursion
563: (set-buffer (concat "*" sml-process-name "*" ))
564: (setq sml-last-error (point-max))))
565:
566:
567: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
568: ;;;
569: ;;; INDENTATION
570: ;;;
571: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
572:
573: (defun sml-indent-region (begin end)
574: "Indent region of sml code."
575: (interactive "r")
576: (message "Indenting region...")
577: (save-excursion
578: (goto-char end) (setq end (point-marker)) (goto-char begin)
579: (while (< (point) end)
580: (skip-chars-forward "\t\n ")
581: (sml-indent-line)
582: (end-of-line))
583: (move-marker end nil))
584: (message "Indenting region... done"))
585:
586: (defun sml-indent-line ()
587: "Indent current line of sml code."
588: (interactive)
589: (let ((indent (sml-calculate-indentation)))
590: (if (/= (current-indentation) indent)
591: (save-excursion ;; Added 890601 (point now stays)
592: (let ((beg (progn (beginning-of-line) (point))))
593: (skip-chars-forward "\t ")
594: (delete-region beg (point))
595: (indent-to indent))))
596: ;; If point is before indentation, move point to indentation
597: (if (< (current-column) (current-indentation))
598: (skip-chars-forward "\t "))))
599:
600: (defconst sml-indent-starters-reg
601: "abstraction\\b\\|abstype\\b\\|and\\b\\|case\\b\\|datatype\\b\
602: \\|else\\b\\|fun\\b\\|functor\\b\\|if\\b\\|sharing\\b\
603: \\|in\\b\\|infix\\b\\|infixr\\b\\|let\\b\\|local\\b\
604: \\|nonfix\\b\\|of\\b\\|open\\b\\|raise\\b\\|sig\\b\\|signature\\b\
605: \\|struct\\b\\|structure\\b\\|then\\b\\|\\btype\\b\\|val\\b\
606: \\|while\\b\\|with\\b\\|withtype\\b"
607: "The indentation starters. The next line, after one starting with
608: one of these, will be indented.")
609:
610: (defconst sml-starters-reg
611: "\\babstraction\\b\\|\\babstype\\b\\|\\bdatatype\\b\
612: \\|\\bexception\\b\\|\\bfun\\b\\|\\bfunctor\\b\\|\\blocal\\b\
613: \\|\\binfix\\b\\|\\binfixr\\b\\|sharing\\b\
614: \\|\\bnonfix\\b\\|\\bopen\\b\\|\\bsignature\\b\\|\\bstructure\\b\
615: \\|\\btype\\b\\|\\bval\\b\\|\\bwithtype\\b\\|\\bwith\\b"
616: "The starters of new expressions.")
617:
618: (defconst sml-end-starters-reg
619: "\\blet\\b\\|\\blocal\\b\\|\\bsig\\b\\|\\bstruct\\b\\|\\bwith\\b"
620: "Matching reg-expression for the \"end\" keyword.")
621:
622: (defconst sml-starters-indent-after
623: "let\\b\\|local\\b\\|struct\\b\\|in\\b\\|sig\\b\\|with\\b"
624: "Indent after these.")
625:
626: (defun sml-calculate-indentation ()
627: (save-excursion
628: (beginning-of-line) ; Go to first non whitespace
629: (skip-chars-forward "\t ") ; on the line.
630: (cond
631: ;; Indentation for comments alone on a line, matches the
632: ;; proper indentation of the next line. Search only for the
633: ;; next "*)", not for the matching.
634: ((looking-at "(\\*")
635: (if (not (search-forward "*)" nil t))
636: (error "Comment not ended."))
637: (skip-chars-forward "\n\t ")
638: ;; If we are at eob, just indent 0
639: (if (eobp) 0 (sml-calculate-indentation)))
640: ;; Are we looking at a case expression ?
641: ((looking-at "|.*=>")
642: (sml-skip-block)
643: ;; Dont get fooled by fn _ => in case statements (890726)
644: (sml-re-search-backward "=>")
645: (let ((loop t))
646: (while (and loop (save-excursion
647: (beginning-of-line)
648: (looking-at ".*\\bfn\\b.*=>")))
649: (setq loop (sml-re-search-backward "=>"))))
650: (beginning-of-line)
651: (skip-chars-forward "\t ")
652: (cond
653: ((looking-at "|") (current-indentation))
654: ((and sml-case-indent (looking-at "of\\b"))
655: (1+ (current-indentation)))
656: ((looking-at "fn\\b") (1+ (current-indentation)))
657: ((looking-at "handle\\b") (+ (current-indentation) 5))
658: (t (+ (current-indentation) sml-pipe-indent))))
659: ((looking-at "and\\b")
660: (if (sml-find-matching-starter sml-starters-reg)
661: (current-column)
662: 0))
663: ((looking-at "in\\b") ; Match the beginning let/local
664: (sml-find-match-indent "in" "\\bin\\b" "\\blocal\\b\\|\\blet\\b"))
665: ((looking-at "end\\b") ; Match the beginning
666: (sml-find-match-indent "end" "\\bend\\b" sml-end-starters-reg))
667: ((and sml-nested-if-indent (looking-at "else[\t ]*if\\b"))
668: (sml-re-search-backward "\\bif\\b\\|\\belse\\b")
669: (current-indentation))
670: ((looking-at "else\\b") ; Match the if
671: (sml-find-match-indent "else" "\\belse\\b" "\\bif\\b" t))
672: ((looking-at "then\\b") ; Match the if + extra indentation
673: (+ (sml-find-match-indent "then" "\\bthen\\b" "\\bif\\b" t)
674: sml-indent-level))
675: ((and sml-case-indent (looking-at "of\\b"))
676: (sml-re-search-backward "\\bcase\\b")
677: (+ (current-column) 2))
678: ((looking-at sml-starters-reg)
679: (let ((start (point)))
680: (sml-backward-sexp)
681: (if (and (looking-at sml-starters-indent-after)
682: (/= start (point)))
683: (+ (if sml-type-of-indent
684: (current-column)
685: (if (progn (beginning-of-line)
686: (skip-chars-forward "\t ")
687: (looking-at "|"))
688: (- (current-indentation) sml-pipe-indent)
689: (current-indentation)))
690: sml-indent-level)
691: (beginning-of-line)
692: (skip-chars-forward "\t ")
693: (if (and (looking-at sml-starters-indent-after)
694: (/= start (point)))
695: (+ (if sml-type-of-indent
696: (current-column)
697: (current-indentation))
698: sml-indent-level)
699: (goto-char start)
700: (if (sml-find-matching-starter sml-starters-reg)
701: (current-column)
702: 0)))))
703: (t
704: (let ((indent (sml-get-indent)))
705: (cond
706: ((looking-at "|")
707: ;; Lets see if it is the follower of a function definition
708: (if (sml-find-matching-starter
709: "\\bfun\\b\\|\\bfn\\b\\|\\band\\b\\|\\bhandle\\b")
710: (cond
711: ((looking-at "fun\\b") (- (current-column) sml-pipe-indent))
712: ((looking-at "fn\\b") (1+ (current-column)))
713: ((looking-at "and\\b") (1+ (1+ (current-column))))
714: ((looking-at "handle\\b") (+ (current-column) 5)))
715: (+ indent sml-pipe-indent)))
716: (t
717: (if sml-paren-lookback ; Look for open parenthesis ?
718: (max indent (sml-get-paren-indent))
719: indent))))))))
720:
721: (defun sml-get-indent ()
722: (save-excursion
723: (beginning-of-line)
724: (skip-chars-backward "\t\n; ")
725: (if (looking-at ";") (sml-backward-sexp))
726: (cond
727: ((save-excursion (sml-backward-sexp) (looking-at "end\\b"))
728: (- (current-indentation) sml-indent-level))
729: (t
730: (while (/= (current-column) (current-indentation))
731: (sml-backward-sexp))
732: (skip-chars-forward "\t |")
733: (let ((indent (current-column)))
734: (skip-chars-forward "\t (")
735: (cond
736: ;; Started val/fun/structure...
737: ((looking-at sml-indent-starters-reg)
738: (+ (current-column) sml-indent-level))
739: ;; Indent after "=>" pattern, but only if its not an fn _ =>
740: ;; (890726)
741: ((looking-at ".*=>")
742: (if (looking-at ".*\\bfn\\b.*=>")
743: indent
744: (+ indent sml-indent-level)))
745: ;; else keep the same indentation as previous line
746: (t indent)))))))
747:
748: (defun sml-get-paren-indent ()
749: (save-excursion
750: (let ((levelpar 0) ; Level of "()"
751: (levelcurl 0) ; Level of "{}"
752: (levelsqr 0) ; Level of "[]"
753: (backpoint (max (- (point) sml-paren-lookback) (point-min)))
754: (loop t))
755: (while (and (/= levelpar 1) (/= levelsqr 1) (/= levelcurl 1) loop)
756: (if (re-search-backward "[][{}()]" backpoint t)
757: (if (not (sml-inside-comment-or-string-p))
758: (cond
759: ((looking-at "(") (setq levelpar (1+ levelpar)))
760: ((looking-at ")") (setq levelpar (1- levelpar)))
761: ((looking-at "\\[") (setq levelsqr (1+ levelsqr)))
762: ((looking-at "\\]") (setq levelsqr (1- levelsqr)))
763: ((looking-at "{") (setq levelcurl (1+ levelcurl)))
764: ((looking-at "}") (setq levelcurl (1- levelcurl)))))
765: (setq loop nil)))
766: (if loop
767: (1+ (current-column))
768: 0))))
769:
770: (defun sml-inside-comment-or-string-p ()
771: (let ((start (point)))
772: (if (save-excursion
773: (condition-case ()
774: (progn
775: (search-backward "(*")
776: (search-forward "*)")
777: (forward-char -1) ; A "*)" is not inside the comment
778: (> (point) start))
779: (error nil)))
780: t
781: (let ((numb 0))
782: (save-excursion
783: (save-restriction
784: (narrow-to-region (progn (beginning-of-line) (point)) start)
785: (condition-case ()
786: (while t
787: (search-forward "\"")
788: (setq numb (1+ numb)))
789: (error (if (and (not (zerop numb))
790: (not (zerop (% numb 2))))
791: t nil)))))))))
792:
793: (defun sml-skip-block ()
794: (sml-backward-sexp)
795: (if (looking-at "end\\b")
796: (progn
797: (goto-char (sml-find-match-backward "end" "\\bend\\b"
798: sml-end-starters-reg))
799: (skip-chars-backward "\n\t "))
800: ;; Here we will need to skip backward past if-then-else
801: ;; and case-of expression. Please - tell me how !!
802: ))
803:
804: (defun sml-find-match-backward (unquoted-this this match &optional start)
805: (save-excursion
806: (let ((level 1)
807: (pattern (concat this "\\|" match)))
808: (if start (goto-char start))
809: (while (not (zerop level))
810: (if (sml-re-search-backward pattern)
811: (setq level (cond
812: ((looking-at this) (1+ level))
813: ((looking-at match) (1- level))))
814: ;; The right match couldn't be found
815: (error (concat "Unbalanced: " unquoted-this))))
816: (point))))
817:
818: (defun sml-find-match-indent (unquoted-this this match &optional indented)
819: (save-excursion
820: (goto-char (sml-find-match-backward unquoted-this this match))
821: (if (or sml-type-of-indent indented)
822: (current-column)
823: (if (progn
824: (beginning-of-line)
825: (skip-chars-forward "\t ")
826: (looking-at "|"))
827: (- (current-indentation) sml-pipe-indent)
828: (current-indentation)))))
829:
830: (defun sml-find-matching-starter (regexp)
831: (let ((start-let-point (sml-point-inside-let-etc))
832: (start-up-list (sml-up-list))
833: (found t))
834: (if (sml-re-search-backward regexp)
835: (progn
836: (condition-case ()
837: (while (or (/= start-up-list (sml-up-list))
838: (/= start-let-point (sml-point-inside-let-etc)))
839: (re-search-backward regexp))
840: (error (setq found nil)))
841: found)
842: nil)))
843:
844: (defun sml-point-inside-let-etc ()
845: (let ((last nil) (loop t) (found t) (start (point)))
846: (save-excursion
847: (while loop
848: (condition-case ()
849: (progn
850: (re-search-forward "\\bend\\b")
851: (while (sml-inside-comment-or-string-p)
852: (re-search-forward "\\bend\\b"))
853: (forward-char -3)
854: (setq last (sml-find-match-backward "end" "\\bend\\b"
855: sml-end-starters-reg last))
856: (if (< last start)
857: (setq loop nil)
858: (forward-char 3)))
859: (error (progn (setq found nil) (setq loop nil)))))
860: (if found
861: last
862: 0))))
863:
864: (defun sml-re-search-backward (regexpr)
865: (let ((found t))
866: (if (re-search-backward regexpr nil t)
867: (progn
868: (condition-case ()
869: (while (sml-inside-comment-or-string-p)
870: (re-search-backward regexpr))
871: (error (setq found nil)))
872: found)
873: nil)))
874:
875: (defun sml-up-list ()
876: (save-excursion
877: (condition-case ()
878: (progn
879: (up-list 1)
880: (point))
881: (error 0))))
882:
883: (defun sml-backward-sexp ()
884: (condition-case ()
885: (progn
886: (let ((start (point)))
887: (backward-sexp 1)
888: (while (and (/= start (point)) (looking-at "(\\*"))
889: (setq start (point))
890: (backward-sexp 1))))
891: (error (forward-char -1))))
892:
893: (defun sml-comment-indent ()
894: (if (looking-at "^(\\*") ; Existing comment at beginning
895: 0 ; of line stays there.
896: (save-excursion
897: (skip-chars-backward " \t")
898: (1+ (max (current-column) ; Else indent at comment column
899: comment-column))))) ; except leave at least one space.
900:
901:
902: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
903: ;;;
904: ;;; INFERIOR SHELL
905: ;;;
906: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
907:
908: (defvar sml-shell-map nil "The mode map for sml-shell.")
909:
910: (defun sml-shell ()
911: "Inferior shell invoking SML.
912: It is not possible to have more than one shell running SML.
913: Like the shell mode with the additional command:
914:
915: \\[sml-run-on-file]\t Runs sml on the file.
916: \\{sml-shell-map}
917: Variables controlling the mode:
918:
919: sml-prog-name (default \"sml\")
920: The string used to invoke the sml program.
921:
922: sml-prog-name-ask-p (default nil)
923: If t, you will be asked which program to run when the inferior
924: shell starts up. Usefull if you have exported images of sml.
925:
926: sml-use-right-delim (default \"\\\"\")
927: sml-use-left-delim (default \"\\\"\")
928: The left and right delimiter used by your version of sml, for
929: \"use file-name\".
930:
931: sml-process-name (default \"SML\")
932: The name of the process running sml.
933:
934: sml-shell-prompt-pattern (default \"^[^\\-=]*[\\-=] *\")
935: The prompt pattern.
936:
937: Runs sml-shell-hook if not nil."
938: (interactive)
939: (if (not (process-status sml-process-name))
940: (save-excursion ; Process is not running
941: (and sml-prog-name-ask-p
942: (setq sml-prog-name
943: (read-file-name
944: (concat "Sml (default " sml-prog-name "): ")
945: (file-name-directory (buffer-file-name))
946: sml-prog-name)))
947: (message "Starting SML...") ; start up a new process
948: (require 'shell)
949: (set-buffer
950: (make-shell sml-process-name
951: (if (= (string-to-char sml-prog-name) ?~)
952: (expand-file-name sml-prog-name)
953: sml-prog-name)))
954: (erase-buffer) ; Erase the buffer if a previous
955: (if sml-shell-map ; process died in there
956: ()
957: (setq sml-shell-map (copy-sequence shell-mode-map))
958: (define-key sml-shell-map "\C-c\C-f" 'sml-run-on-file))
959: (use-local-map sml-shell-map)
960: (make-local-variable 'shell-prompt-pattern)
961: (setq shell-prompt-pattern sml-shell-prompt-pattern)
962: (setq major-mode 'sml-shell)
963: (setq mode-name "Sml-Shell")
964: (if sml-prog-name-ask-p
965: (setq mode-line-process
966: (list (concat
967: ": %s ("
968: (substring sml-prog-name
969: (string-match "[^/]*$" sml-prog-name)
970: (string-match "$" sml-prog-name))
971: ")"))))
972: (set-process-filter (get-process sml-process-name) 'sml-process-filter)
973: (message "Starting SML... done.")
974: (run-hooks 'sml-shell-hook))))
975:
976: (defun sml-process-filter (proc str)
977: (let ((cur (selected-window))
978: (pop-up-windows t)
979: (process (concat "*" sml-process-name "*")))
980: (pop-to-buffer process)
981: (goto-char (point-max))
982: (insert str)
983: (set-marker (process-mark proc) (point-max))
984: (select-window cur)))
985:
986: (defun sml-pop-to-shell ()
987: "Pop to the buffer running SML"
988: (interactive)
989: (sml-shell)
990: (pop-to-buffer (concat "*" sml-process-name "*")))
991:
992: (defun sml-run-on-file (fil)
993: "Send a use FILE to the inferior shell running sml"
994: (interactive "FUse file: ")
995: (sml-shell)
996: (save-some-buffers)
997: (sml-skip-errors)
998: (send-string sml-process-name
999: (concat "use " sml-use-left-delim (expand-file-name fil)
1000: sml-use-right-delim ";\n")))
1001:
1002: (defun sml-save-buffer-use-file ()
1003: "Save the buffer, and send a `use file' to the inferior shell
1004: running SML."
1005: (interactive)
1006: (let (file)
1007: (if (setq file (buffer-file-name)) ; Is the buffer associated
1008: (progn ; with file ?
1009: (save-buffer)
1010: (sml-shell)
1011: (sml-skip-errors)
1012: (message (concat "use " sml-use-left-delim file sml-use-right-delim))
1013: (send-string sml-process-name
1014: (concat "use " sml-use-left-delim
1015: (expand-file-name file)
1016: sml-use-right-delim ";\n")))
1017: (error "Buffer not associated with file."))))
1018:
1019: (defun sml-import-file ()
1020: "Save the buffer, and send an `import file' to the inferior shell
1021: running SML."
1022: (interactive)
1023: (let (file)
1024: (if (setq file (buffer-file-name))
1025: (if (string-match ".sml$" file)
1026: (progn
1027: (setq file (substring file 0 (string-match ".sml$" file)))
1028: (save-buffer)
1029: (sml-shell)
1030: (sml-skip-errors)
1031: (message (concat "import \"" file "\""))
1032: (send-string sml-process-name
1033: (concat "import " sml-use-left-delim
1034: (expand-file-name file)
1035: sml-use-right-delim ";\n")))
1036: (error "Filename doesn't match \"*.sml\""))
1037: (error "Buffer not associated with file."))))
1038:
1039: (defvar sml-tmp-files-list nil
1040: "List of all temporary files created by sml-simulate-send-region.
1041: Each element in the list is a list with the format:
1042: \n (\"tmp-filename\" buffer start-line)")
1043:
1044: (defvar sml-simulate-send-region-called-p nil
1045: "Has sml-simulate-send-region been called previously.")
1046:
1047: (defun sml-simulate-send-region (point1 point2)
1048: "Simulate send region. As send-region only can handle what ever the
1049: system sets as the default, we have to make a temporary file.
1050: Updates the list of temporary files (sml-tmp-files-list)."
1051: (let ((file (expand-file-name (make-temp-name sml-tmp-template))))
1052: ;; Remove temporary files when we leave emacs
1053: (if (not sml-simulate-send-region-called-p)
1054: (progn
1055: (setq sml-old-kill-emacs-hook kill-emacs-hook)
1056: (setq kill-emacs-hook 'sml-remove-tmp-files)
1057: (setq sml-simulate-send-region-called-p t)))
1058: ;; As make-temp-name can only make 26 unique file names with the
1059: ;; same template (bug in Un*x function mktemp), we add a new
1060: ;; letter to sml-tmp-template.
1061: (if (zerop (% (length sml-tmp-files-list) 25))
1062: (setq sml-tmp-template (concat sml-tmp-template "A")))
1063: (save-excursion
1064: (goto-char point1)
1065: (setq sml-tmp-files-list
1066: (cons (list file
1067: (current-buffer)
1068: (save-excursion ; Calculate line no.
1069: (beginning-of-line)
1070: (1+ (count-lines 1 (point)))))
1071: sml-tmp-files-list)))
1072: (write-region point1 point2 file nil 'dummy)
1073: (sml-shell)
1074: (message "Using temporary file: %s" file)
1075: (send-string
1076: sml-process-name
1077: ;; string to send: use file;
1078: (concat "use " sml-use-left-delim file sml-use-right-delim ";\n"))))
1079:
1080: (defvar sml-old-kill-emacs-hook nil
1081: "Old value of kill-emacs-hook")
1082:
1083: (defun sml-remove-tmp-files ()
1084: "Remove the temporary files, created by sml-simulate-send-region, if
1085: they still exist. Only files recorded in sml-tmp-files-list are removed."
1086: (message "Removing temporary files created by sml-mode...")
1087: (while sml-tmp-files-list
1088: (condition-case ()
1089: (delete-file (car (car sml-tmp-files-list)))
1090: (error ()))
1091: (setq sml-tmp-files-list (cdr sml-tmp-files-list)))
1092: (message "Removing temporary files created by sml-mode... done.")
1093: (run-hooks 'sml-old-kill-emacs-hook))
1094:
1095: (defun sml-send-region ()
1096: "Send region to inferior shell running SML."
1097: (interactive)
1098: (sml-shell)
1099: (sml-skip-errors)
1100: (let (start end)
1101: (save-excursion
1102: (setq end (point))
1103: (exchange-point-and-mark)
1104: (setq start (point)))
1105: (sml-simulate-send-region start end)))
1106:
1107: (defun sml-send-function ()
1108: "Does NOT send the function, but the paragraph, to inferior shell
1109: running SML"
1110: (interactive)
1111: (sml-shell)
1112: (sml-skip-errors)
1113: (let (start end)
1114: (save-excursion
1115: (condition-case ()
1116: (progn
1117: (backward-paragraph)
1118: (setq start (point)))
1119: (error (setq start (point-min))))
1120: (condition-case ()
1121: (progn
1122: (forward-paragraph)
1123: (setq end (point)))
1124: (error (setq end (point-max)))))
1125: (sml-simulate-send-region start end)))
1126:
1127: (defun sml-send-buffer ()
1128: "Send the buffer, to inferior shell running SML"
1129: (interactive)
1130: (sml-shell)
1131: (sml-skip-errors)
1132: (sml-simulate-send-region (point-min) (point-max)))
1133:
1134: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1135: ;;;
1136: ;;; END OF SML-MODE
1137: ;;;
1138: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1139:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.