Removed auto-mode-alist hacking for html-mode to files.el.
[bpt/emacs.git] / lisp / progmodes / scheme.el
CommitLineData
c88ab9ce
ER
1;;; scheme.el --- Scheme mode, and its idiosyncratic commands.
2
3a801d0c
ER
3;; Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc.
4
e5167999 5;; Author: Bill Rozas <jinz@prep.ai.mit.edu>
d7b4d18f 6;; Keywords: languages, lisp
e5167999 7
ac5b21ac
RS
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
e5167999 12;; the Free Software Foundation; either version 2, or (at your option)
ac5b21ac
RS
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to
22;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
e5167999 24;;; Commentary:
ac5b21ac 25
e5167999 26;; Adapted from Lisp mode by Bill Rozas, jinx@prep.
ac5b21ac
RS
27;; Initially a query replace of Lisp mode, except for the indentation
28;; of special forms. Probably the code should be merged at some point
29;; so that there is sharing between both libraries.
30
e5167999 31;;; Code:
ac5b21ac
RS
32\f
33(defvar scheme-mode-syntax-table nil "")
34(if (not scheme-mode-syntax-table)
35 (let ((i 0))
36 (setq scheme-mode-syntax-table (make-syntax-table))
37 (set-syntax-table scheme-mode-syntax-table)
38
39 ;; Default is atom-constituent.
40 (while (< i 256)
41 (modify-syntax-entry i "_ ")
42 (setq i (1+ i)))
43
44 ;; Word components.
45 (setq i ?0)
46 (while (<= i ?9)
47 (modify-syntax-entry i "w ")
48 (setq i (1+ i)))
49 (setq i ?A)
50 (while (<= i ?Z)
51 (modify-syntax-entry i "w ")
52 (setq i (1+ i)))
53 (setq i ?a)
54 (while (<= i ?z)
55 (modify-syntax-entry i "w ")
56 (setq i (1+ i)))
57
58 ;; Whitespace
59 (modify-syntax-entry ?\t " ")
60 (modify-syntax-entry ?\n "> ")
61 (modify-syntax-entry ?\f " ")
62 (modify-syntax-entry ?\r " ")
63 (modify-syntax-entry ? " ")
64
65 ;; These characters are delimiters but otherwise undefined.
66 ;; Brackets and braces balance for editing convenience.
67 (modify-syntax-entry ?[ "(] ")
68 (modify-syntax-entry ?] ")[ ")
69 (modify-syntax-entry ?{ "(} ")
70 (modify-syntax-entry ?} "){ ")
71 (modify-syntax-entry ?\| " 23")
72
73 ;; Other atom delimiters
74 (modify-syntax-entry ?\( "() ")
75 (modify-syntax-entry ?\) ")( ")
76 (modify-syntax-entry ?\; "< ")
77 (modify-syntax-entry ?\" "\" ")
78 (modify-syntax-entry ?' " p")
79 (modify-syntax-entry ?` " p")
80
81 ;; Special characters
82 (modify-syntax-entry ?, "_ p")
83 (modify-syntax-entry ?@ "_ p")
84 (modify-syntax-entry ?# "_ p14")
85 (modify-syntax-entry ?\\ "\\ ")))
86\f
87(defvar scheme-mode-abbrev-table nil "")
88(define-abbrev-table 'scheme-mode-abbrev-table ())
89
90(defun scheme-mode-variables ()
91 (set-syntax-table scheme-mode-syntax-table)
92 (setq local-abbrev-table scheme-mode-abbrev-table)
93 (make-local-variable 'paragraph-start)
53e84345 94 (setq paragraph-start (concat "$\\|" page-delimiter))
ac5b21ac
RS
95 (make-local-variable 'paragraph-separate)
96 (setq paragraph-separate paragraph-start)
97 (make-local-variable 'paragraph-ignore-fill-prefix)
98 (setq paragraph-ignore-fill-prefix t)
99 (make-local-variable 'indent-line-function)
100 (setq indent-line-function 'scheme-indent-line)
446e6a14
KH
101 (make-local-variable 'parse-sexp-ignore-comments)
102 (setq parse-sexp-ignore-comments t)
ac5b21ac
RS
103 (make-local-variable 'comment-start)
104 (setq comment-start ";")
105 (make-local-variable 'comment-start-skip)
106 (setq comment-start-skip ";+[ \t]*")
107 (make-local-variable 'comment-column)
108 (setq comment-column 40)
e41b2db1
ER
109 (make-local-variable 'comment-indent-function)
110 (setq comment-indent-function 'scheme-comment-indent)
2441d53d
RS
111 (make-local-variable 'parse-sexp-ignore-comments)
112 (setq parse-sexp-ignore-comments t)
ac5b21ac
RS
113 (setq mode-line-process '("" scheme-mode-line-process)))
114
115(defvar scheme-mode-line-process "")
116
117(defun scheme-mode-commands (map)
118 (define-key map "\t" 'scheme-indent-line)
119 (define-key map "\177" 'backward-delete-char-untabify)
120 (define-key map "\e\C-q" 'scheme-indent-sexp))
121
122(defvar scheme-mode-map nil)
123(if (not scheme-mode-map)
124 (progn
125 (setq scheme-mode-map (make-sparse-keymap))
126 (scheme-mode-commands scheme-mode-map)))
127\f
e2ab0aa6 128;;;###autoload
ac5b21ac
RS
129(defun scheme-mode ()
130 "Major mode for editing Scheme code.
131Editing commands are similar to those of lisp-mode.
132
133In addition, if an inferior Scheme process is running, some additional
134commands will be defined, for evaluating expressions and controlling
135the interpreter, and the state of the process will be displayed in the
136modeline of all Scheme buffers. The names of commands that interact
137with the Scheme process start with \"xscheme-\". For more information
138see the documentation for xscheme-interaction-mode.
139
140Commands:
141Delete converts tabs to spaces as it moves back.
142Blank lines separate paragraphs. Semicolons start comments.
143\\{scheme-mode-map}
144Entry to this mode calls the value of scheme-mode-hook
145if that value is non-nil."
146 (interactive)
147 (kill-all-local-variables)
148 (scheme-mode-initialize)
149 (scheme-mode-variables)
150 (run-hooks 'scheme-mode-hook))
151
152(defun scheme-mode-initialize ()
153 (use-local-map scheme-mode-map)
154 (setq major-mode 'scheme-mode)
155 (setq mode-name "Scheme"))
156
157(defvar scheme-mit-dialect t
158 "If non-nil, scheme mode is specialized for MIT Scheme.
159Set this to nil if you normally use another dialect.")
160\f
161(defun scheme-comment-indent (&optional pos)
162 (save-excursion
163 (if pos (goto-char pos))
164 (cond ((looking-at ";;;") (current-column))
165 ((looking-at ";;")
166 (let ((tem (calculate-scheme-indent)))
167 (if (listp tem) (car tem) tem)))
168 (t
169 (skip-chars-backward " \t")
170 (max (if (bolp) 0 (1+ (current-column)))
171 comment-column)))))
172
173(defvar scheme-indent-offset nil "")
174(defvar scheme-indent-function 'scheme-indent-function "")
175
176(defun scheme-indent-line (&optional whole-exp)
177 "Indent current line as Scheme code.
178With argument, indent any additional lines of the same expression
179rigidly along with this one."
180 (interactive "P")
181 (let ((indent (calculate-scheme-indent)) shift-amt beg end
182 (pos (- (point-max) (point))))
183 (beginning-of-line)
184 (setq beg (point))
185 (skip-chars-forward " \t")
186 (if (looking-at "[ \t]*;;;")
187 ;; Don't alter indentation of a ;;; comment line.
188 nil
189 (if (listp indent) (setq indent (car indent)))
190 (setq shift-amt (- indent (current-column)))
191 (if (zerop shift-amt)
192 nil
193 (delete-region beg (point))
194 (indent-to indent))
195 ;; If initial point was within line's indentation,
196 ;; position after the indentation. Else stay at same point in text.
197 (if (> (- (point-max) pos) (point))
198 (goto-char (- (point-max) pos)))
199 ;; If desired, shift remaining lines of expression the same amount.
200 (and whole-exp (not (zerop shift-amt))
201 (save-excursion
202 (goto-char beg)
203 (forward-sexp 1)
204 (setq end (point))
205 (goto-char beg)
206 (forward-line 1)
207 (setq beg (point))
208 (> end beg))
209 (indent-code-rigidly beg end shift-amt)))))
210\f
211(defun calculate-scheme-indent (&optional parse-start)
212 "Return appropriate indentation for current line as scheme code.
213In usual case returns an integer: the column to indent to.
214Can instead return a list, whose car is the column to indent to.
215This means that following lines at the same level of indentation
216should not necessarily be indented the same way.
217The second element of the list is the buffer position
218of the start of the containing expression."
219 (save-excursion
220 (beginning-of-line)
221 (let ((indent-point (point)) state paren-depth desired-indent (retry t)
222 last-sexp containing-sexp first-sexp-list-p)
223 (if parse-start
224 (goto-char parse-start)
225 (beginning-of-defun))
226 ;; Find outermost containing sexp
227 (while (< (point) indent-point)
228 (setq state (parse-partial-sexp (point) indent-point 0)))
229 ;; Find innermost containing sexp
230 (while (and retry (setq paren-depth (car state)) (> paren-depth 0))
231 (setq retry nil)
232 (setq last-sexp (nth 2 state))
233 (setq containing-sexp (car (cdr state)))
234 ;; Position following last unclosed open.
235 (goto-char (1+ containing-sexp))
236 ;; Is there a complete sexp since then?
237 (if (and last-sexp (> last-sexp (point)))
238 ;; Yes, but is there a containing sexp after that?
239 (let ((peek (parse-partial-sexp last-sexp indent-point 0)))
240 (if (setq retry (car (cdr peek))) (setq state peek))))
241 (if (not retry)
242 ;; Innermost containing sexp found
243 (progn
244 (goto-char (1+ containing-sexp))
245 (if (not last-sexp)
246 ;; indent-point immediately follows open paren.
247 ;; Don't call hook.
248 (setq desired-indent (current-column))
249 ;; Move to first sexp after containing open paren
250 (parse-partial-sexp (point) last-sexp 0 t)
251 (setq first-sexp-list-p (looking-at "\\s("))
252 (cond
253 ((> (save-excursion (forward-line 1) (point))
254 last-sexp)
255 ;; Last sexp is on same line as containing sexp.
256 ;; It's almost certainly a function call.
257 (parse-partial-sexp (point) last-sexp 0 t)
258 (if (/= (point) last-sexp)
259 ;; Indent beneath first argument or, if only one sexp
260 ;; on line, indent beneath that.
261 (progn (forward-sexp 1)
262 (parse-partial-sexp (point) last-sexp 0 t)))
263 (backward-prefix-chars))
264 (t
265 ;; Indent beneath first sexp on same line as last-sexp.
266 ;; Again, it's almost certainly a function call.
267 (goto-char last-sexp)
268 (beginning-of-line)
269 (parse-partial-sexp (point) last-sexp 0 t)
270 (backward-prefix-chars)))))))
271 ;; If looking at a list, don't call hook.
272 (if first-sexp-list-p
273 (setq desired-indent (current-column)))
274 ;; Point is at the point to indent under unless we are inside a string.
eb8c3be9 275 ;; Call indentation hook except when overridden by scheme-indent-offset
ac5b21ac
RS
276 ;; or if the desired indentation has already been computed.
277 (cond ((car (nthcdr 3 state))
278 ;; Inside a string, don't change indentation.
279 (goto-char indent-point)
280 (skip-chars-forward " \t")
281 (setq desired-indent (current-column)))
282 ((and (integerp scheme-indent-offset) containing-sexp)
283 ;; Indent by constant offset
284 (goto-char containing-sexp)
285 (setq desired-indent (+ scheme-indent-offset (current-column))))
286 ((not (or desired-indent
287 (and (boundp 'scheme-indent-function)
288 scheme-indent-function
289 (not retry)
290 (setq desired-indent
291 (funcall scheme-indent-function
292 indent-point state)))))
293 ;; Use default indentation if not computed yet
294 (setq desired-indent (current-column))))
295 desired-indent)))
296\f
297(defun scheme-indent-function (indent-point state)
298 (let ((normal-indent (current-column)))
299 (save-excursion
300 (goto-char (1+ (car (cdr state))))
301 (re-search-forward "\\sw\\|\\s_")
302 (if (/= (point) (car (cdr state)))
303 (let ((function (buffer-substring (progn (forward-char -1) (point))
304 (progn (forward-sexp 1) (point))))
305 method)
306 ;; Who cares about this, really?
307 ;(if (not (string-match "\\\\\\||" function)))
308 (setq function (downcase function))
309 (setq method (get (intern-soft function) 'scheme-indent-function))
310 (cond ((integerp method)
311 (scheme-indent-specform method state indent-point))
312 (method
313 (funcall method state indent-point))
314 ((and (> (length function) 3)
315 (string-equal (substring function 0 3) "def"))
316 (scheme-indent-defform state indent-point))))))))
317
318(defvar scheme-body-indent 2 "")
319\f
320(defun scheme-indent-specform (count state indent-point)
321 (let ((containing-form-start (car (cdr state))) (i count)
322 body-indent containing-form-column)
323 ;; Move to the start of containing form, calculate indentation
324 ;; to use for non-distinguished forms (> count), and move past the
325 ;; function symbol. scheme-indent-function guarantees that there is at
326 ;; least one word or symbol character following open paren of containing
327 ;; form.
328 (goto-char containing-form-start)
329 (setq containing-form-column (current-column))
330 (setq body-indent (+ scheme-body-indent containing-form-column))
331 (forward-char 1)
332 (forward-sexp 1)
333 ;; Now find the start of the last form.
334 (parse-partial-sexp (point) indent-point 1 t)
335 (while (and (< (point) indent-point)
336 (condition-case nil
337 (progn
338 (setq count (1- count))
339 (forward-sexp 1)
340 (parse-partial-sexp (point) indent-point 1 t))
341 (error nil))))
342 ;; Point is sitting on first character of last (or count) sexp.
343 (cond ((> count 0)
344 ;; A distinguished form. Use double scheme-body-indent.
345 (list (+ containing-form-column (* 2 scheme-body-indent))
346 containing-form-start))
347 ;; A non-distinguished form. Use body-indent if there are no
348 ;; distinguished forms and this is the first undistinguished
349 ;; form, or if this is the first undistinguished form and
350 ;; the preceding distinguished form has indentation at least
351 ;; as great as body-indent.
352 ((and (= count 0)
353 (or (= i 0)
354 (<= body-indent normal-indent)))
355 body-indent)
356 (t
357 normal-indent))))
358
359(defun scheme-indent-defform (state indent-point)
360 (goto-char (car (cdr state)))
361 (forward-line 1)
362 (if (> (point) (car (cdr (cdr state))))
363 (progn
364 (goto-char (car (cdr state)))
365 (+ scheme-body-indent (current-column)))))
366\f
367;;; Let is different in Scheme
368
369(defun would-be-symbol (string)
370 (not (string-equal (substring string 0 1) "(")))
371
372(defun next-sexp-as-string ()
373 ;; Assumes that protected by a save-excursion
374 (forward-sexp 1)
375 (let ((the-end (point)))
376 (backward-sexp 1)
377 (buffer-substring (point) the-end)))
378
379;; This is correct but too slow.
380;; The one below works almost always.
381;;(defun scheme-let-indent (state indent-point)
382;; (if (would-be-symbol (next-sexp-as-string))
383;; (scheme-indent-specform 2 state indent-point)
384;; (scheme-indent-specform 1 state indent-point)))
385
386(defun scheme-let-indent (state indent-point)
387 (skip-chars-forward " \t")
61361a07 388 (if (looking-at "[-a-zA-Z0-9+*/?!@$%^&_:~]")
ac5b21ac
RS
389 (scheme-indent-specform 2 state indent-point)
390 (scheme-indent-specform 1 state indent-point)))
391
392;; (put 'begin 'scheme-indent-function 0), say, causes begin to be indented
393;; like defun if the first form is placed on the next line, otherwise
394;; it is indented like any other form (i.e. forms line up under first).
395
396(put 'begin 'scheme-indent-function 0)
397(put 'case 'scheme-indent-function 1)
398(put 'delay 'scheme-indent-function 0)
399(put 'do 'scheme-indent-function 2)
400(put 'lambda 'scheme-indent-function 1)
401(put 'let 'scheme-indent-function 'scheme-let-indent)
402(put 'let* 'scheme-indent-function 1)
403(put 'letrec 'scheme-indent-function 1)
404(put 'sequence 'scheme-indent-function 0)
405
406(put 'call-with-input-file 'scheme-indent-function 1)
407(put 'with-input-from-file 'scheme-indent-function 1)
408(put 'with-input-from-port 'scheme-indent-function 1)
409(put 'call-with-output-file 'scheme-indent-function 1)
410(put 'with-output-to-file 'scheme-indent-function 1)
411(put 'with-output-to-port 'scheme-indent-function 1)
412\f
413;;;; MIT Scheme specific indentation.
414
415(if scheme-mit-dialect
416 (progn
417 (put 'fluid-let 'scheme-indent-function 1)
418 (put 'in-package 'scheme-indent-function 1)
419 (put 'let-syntax 'scheme-indent-function 1)
420 (put 'local-declare 'scheme-indent-function 1)
421 (put 'macro 'scheme-indent-function 1)
422 (put 'make-environment 'scheme-indent-function 0)
423 (put 'named-lambda 'scheme-indent-function 1)
424 (put 'using-syntax 'scheme-indent-function 1)
425
426 (put 'with-input-from-string 'scheme-indent-function 1)
427 (put 'with-output-to-string 'scheme-indent-function 0)
428 (put 'with-values 'scheme-indent-function 1)
429
430 (put 'syntax-table-define 'scheme-indent-function 2)
431 (put 'list-transform-positive 'scheme-indent-function 1)
432 (put 'list-transform-negative 'scheme-indent-function 1)
433 (put 'list-search-positive 'scheme-indent-function 1)
434 (put 'list-search-negative 'scheme-indent-function 1)
435
436 (put 'access-components 'scheme-indent-function 1)
437 (put 'assignment-components 'scheme-indent-function 1)
438 (put 'combination-components 'scheme-indent-function 1)
439 (put 'comment-components 'scheme-indent-function 1)
440 (put 'conditional-components 'scheme-indent-function 1)
441 (put 'disjunction-components 'scheme-indent-function 1)
442 (put 'declaration-components 'scheme-indent-function 1)
443 (put 'definition-components 'scheme-indent-function 1)
444 (put 'delay-components 'scheme-indent-function 1)
445 (put 'in-package-components 'scheme-indent-function 1)
446 (put 'lambda-components 'scheme-indent-function 1)
447 (put 'lambda-components* 'scheme-indent-function 1)
448 (put 'lambda-components** 'scheme-indent-function 1)
449 (put 'open-block-components 'scheme-indent-function 1)
450 (put 'pathname-components 'scheme-indent-function 1)
451 (put 'procedure-components 'scheme-indent-function 1)
452 (put 'sequence-components 'scheme-indent-function 1)
453 (put 'unassigned\?-components 'scheme-indent-function 1)
454 (put 'unbound\?-components 'scheme-indent-function 1)
455 (put 'variable-components 'scheme-indent-function 1)))
456\f
457(defun scheme-indent-sexp ()
458 "Indent each line of the list starting just after point."
459 (interactive)
460 (let ((indent-stack (list nil)) (next-depth 0) bol
461 outer-loop-done inner-loop-done state this-indent)
462 (save-excursion (forward-sexp 1))
463 (save-excursion
464 (setq outer-loop-done nil)
465 (while (not outer-loop-done)
466 (setq last-depth next-depth
467 innerloop-done nil)
468 (while (and (not innerloop-done)
469 (not (setq outer-loop-done (eobp))))
470 (setq state (parse-partial-sexp (point) (progn (end-of-line) (point))
471 nil nil state))
472 (setq next-depth (car state))
473 (if (car (nthcdr 4 state))
474 (progn (indent-for-comment)
475 (end-of-line)
476 (setcar (nthcdr 4 state) nil)))
477 (if (car (nthcdr 3 state))
478 (progn
479 (forward-line 1)
480 (setcar (nthcdr 5 state) nil))
481 (setq innerloop-done t)))
482 (if (setq outer-loop-done (<= next-depth 0))
483 nil
484 (while (> last-depth next-depth)
485 (setq indent-stack (cdr indent-stack)
486 last-depth (1- last-depth)))
487 (while (< last-depth next-depth)
488 (setq indent-stack (cons nil indent-stack)
489 last-depth (1+ last-depth)))
490 (forward-line 1)
491 (setq bol (point))
492 (skip-chars-forward " \t")
493 (if (or (eobp) (looking-at "[;\n]"))
494 nil
495 (if (and (car indent-stack)
496 (>= (car indent-stack) 0))
497 (setq this-indent (car indent-stack))
498 (let ((val (calculate-scheme-indent
499 (if (car indent-stack) (- (car indent-stack))))))
500 (if (integerp val)
501 (setcar indent-stack
502 (setq this-indent val))
3a3f61fa
RS
503 (if (cdr val)
504 (setcar indent-stack (- (car (cdr val)))))
ac5b21ac
RS
505 (setq this-indent (car val)))))
506 (if (/= (current-column) this-indent)
507 (progn (delete-region bol (point))
508 (indent-to this-indent)))))))))
49116ac0
JB
509
510(provide 'scheme)
c88ab9ce
ER
511
512;;; scheme.el ends here