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