comparison src/elisp/urweb-mode.el @ 350:3a1e36b14105

First sort-of-working run of urweb-mode
author Adam Chlipala <adamc@hcoop.net>
date Sun, 12 Oct 2008 10:04:17 -0400
parents
children d5148178a7be
comparison
equal deleted inserted replaced
349:beb72f8a7218 350:3a1e36b14105
1 ;;; urweb-mode.el --- Major mode for editing (Standard) ML
2
3 ;; Based on sml-mode:
4 ;; Copyright (C) 1999,2000,2004 Stefan Monnier
5 ;; Copyright (C) 1994-1997 Matthew J. Morley
6 ;; Copyright (C) 1989 Lars Bo Nielsen
7 ;;
8 ;; Modified for urweb-mode:
9 ;; Copyright (C) 2008 Adam Chlipala <adamc@hcoop.net>
10
11 ;; Author: Lars Bo Nielsen
12 ;; Olin Shivers
13 ;; Fritz Knabe (?)
14 ;; Steven Gilmore (?)
15 ;; Matthew Morley <mjm@scs.leeds.ac.uk> (aka <matthew@verisity.com>)
16 ;; Matthias Blume <blume@cs.princeton.edu> (aka <blume@kurims.kyoto-u.ac.jp>)
17 ;; (Stefan Monnier) monnier@cs.yale.edu
18 ;; Adam Chlipala
19
20 ;; This file is not part of GNU Emacs, but it is distributed under the
21 ;; same conditions.
22
23 ;; This program is free software; you can redistribute it and/or
24 ;; modify it under the terms of the GNU General Public License as
25 ;; published by the Free Software Foundation; either version 2, or (at
26 ;; your option) any later version.
27
28 ;; This program is distributed in the hope that it will be useful, but
29 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
30 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31 ;; General Public License for more details.
32
33 ;; You should have received a copy of the GNU General Public License
34 ;; along with GNU Emacs; see the file COPYING. If not, write to the
35 ;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
36
37 ;;; Commentary:
38
39 ;;; HISTORY
40
41 ;; Still under construction: History obscure, needs a biographer as
42 ;; well as a M-x doctor. Change Log on request.
43
44 ;; Hacked by Olin Shivers for comint from Lars Bo Nielsen's urweb.el.
45
46 ;; Hacked by Matthew Morley to incorporate Fritz Knabe's hilite and
47 ;; font-lock patterns, some of Steven Gilmore's (reduced) easy-menus,
48 ;; and numerous bugs and bug-fixes.
49
50 ;;; DESCRIPTION
51
52 ;; See accompanying info file: urweb-mode.info
53
54 ;;; FOR YOUR .EMACS FILE
55
56 ;; If urweb-mode.el lives in some non-standard directory, you must tell
57 ;; emacs where to get it. This may or may not be necessary:
58
59 ;; (add-to-list 'load-path "~jones/lib/emacs/")
60
61 ;; Then to access the commands autoload urweb-mode with that command:
62
63 ;; (load "urweb-mode-startup")
64
65 ;; urweb-mode-hook is run whenever a new urweb-mode buffer is created.
66
67 ;;; Code:
68
69 (eval-when-compile (require 'cl))
70 (require 'urweb-util)
71 (require 'urweb-move)
72 (require 'urweb-defs)
73 (condition-case nil (require 'skeleton) (error nil))
74
75 ;;; VARIABLES CONTROLLING INDENTATION
76
77 (defcustom urweb-indent-level 4
78 "*Indentation of blocks in Ur/Web (see also `urweb-structure-indent')."
79 :group 'urweb
80 :type '(integer))
81
82 (defcustom urweb-indent-args urweb-indent-level
83 "*Indentation of args placed on a separate line."
84 :group 'urweb
85 :type '(integer))
86
87 (defcustom urweb-electric-semi-mode nil
88 "*If non-nil, `\;' will self insert, reindent the line, and do a newline.
89 If nil, just insert a `\;'. (To insert while t, do: \\[quoted-insert] \;)."
90 :group 'urweb
91 :type 'boolean)
92
93 (defcustom urweb-rightalign-and t
94 "If non-nil, right-align `and' with its leader.
95 If nil: If t:
96 datatype a = A datatype a = A
97 and b = B and b = B"
98 :group 'urweb
99 :type 'boolean)
100
101 ;;; OTHER GENERIC MODE VARIABLES
102
103 (defvar urweb-mode-info "urweb-mode"
104 "*Where to find Info file for `urweb-mode'.
105 The default assumes the info file \"urweb-mode.info\" is on Emacs' info
106 directory path. If it is not, either put the file on the standard path
107 or set the variable `urweb-mode-info' to the exact location of this file
108
109 (setq urweb-mode-info \"/usr/me/lib/info/urweb-mode\")
110
111 in your .emacs file. You can always set it interactively with the
112 set-variable command.")
113
114 (defvar urweb-mode-hook nil
115 "*Run upon entering `urweb-mode'.
116 This is a good place to put your preferred key bindings.")
117
118 ;;; CODE FOR Ur/Web-MODE
119
120 (defun urweb-mode-info ()
121 "Command to access the TeXinfo documentation for `urweb-mode'.
122 See doc for the variable `urweb-mode-info'."
123 (interactive)
124 (require 'info)
125 (condition-case nil
126 (info urweb-mode-info)
127 (error (progn
128 (describe-variable 'urweb-mode-info)
129 (message "Can't find it... set this variable first!")))))
130
131
132 ;; font-lock setup
133
134 (defconst urweb-keywords-regexp
135 (urweb-syms-re "and" "case" "class" "con" "constraint" "constraints"
136 "datatype" "else" "end" "extern" "fn" "fold"
137 "fun" "functor" "if" "include"
138 "of" "open"
139 "rec" "sequence" "sig" "signature"
140 "struct" "structure" "table" "then" "type" "val" "where"
141 "with"
142
143 "Name" "Type" "Unit")
144 "A regexp that matches any non-SQL keywords of Ur/Web.")
145
146 (defconst urweb-sql-keywords-regexp
147 (urweb-syms-re "SELECT" "FROM" "AS" "WHERE" "SQL" "GROUP" "ORDER" "BY"
148 "HAVING" "LIMIT" "OFFSET" "ALL" "UNION" "INTERSECT" "EXCEPT"
149 "TRUE" "FALSE" "AND" "OR" "NOT" "COUNT" "AVG" "SUM" "MIN" "MAX"
150 "ASC" "DESC" "INSERT" "INTO" "VALUES" "UPDATE" "SET" "DELETE")
151 "A regexp that matches SQL keywords.")
152
153 ;;; Font-lock settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
154
155 ;; The font lock regular expressions.
156
157 (defconst urweb-font-lock-keywords
158 `(;;(urweb-font-comments-and-strings)
159 (,(concat "\\<\\(fun\\|and\\)\\s-+\\(\\sw+\\)\\s-+[^ \t\n=]")
160 (1 font-lock-keyword-face)
161 (6 font-lock-function-name-face))
162 (,(concat "\\<\\(\\(data\\)?type\\|con\\)\\s-+\\(\\sw+\\)")
163 (1 font-lock-keyword-face)
164 (7 font-lock-type-def-face))
165 ("\\<\\(val\\)\\s-+\\(\\sw+\\>\\s-*\\)?\\(\\sw+\\)\\s-*[=:]"
166 (1 font-lock-keyword-face)
167 (3 font-lock-variable-name-face))
168 ("\\<\\(structure\\|functor\\)\\s-+\\(\\sw+\\)"
169 (1 font-lock-keyword-face)
170 (2 font-lock-module-def-face))
171 ("\\<\\(signature\\)\\s-+\\(\\sw+\\)"
172 (1 font-lock-keyword-face)
173 (2 font-lock-interface-def-face))
174
175 (,urweb-keywords-regexp . font-lock-keyword-face)
176 (,urweb-sql-keywords-regexp . font-lock-sql-face))
177 "Regexps matching standard Ur/Web keywords.")
178
179 (defface font-lock-type-def-face
180 '((t (:bold t)))
181 "Font Lock mode face used to highlight type definitions."
182 :group 'font-lock-highlighting-faces)
183 (defvar font-lock-type-def-face 'font-lock-type-def-face
184 "Face name to use for type definitions.")
185
186 (defface font-lock-module-def-face
187 '((t (:bold t)))
188 "Font Lock mode face used to highlight module definitions."
189 :group 'font-lock-highlighting-faces)
190 (defvar font-lock-module-def-face 'font-lock-module-def-face
191 "Face name to use for module definitions.")
192
193 (defface font-lock-interface-def-face
194 '((t (:bold t)))
195 "Font Lock mode face used to highlight interface definitions."
196 :group 'font-lock-highlighting-faces)
197 (defvar font-lock-interface-def-face 'font-lock-interface-def-face
198 "Face name to use for interface definitions.")
199
200 (defface font-lock-sql-face
201 '((t (:bold t)))
202 "Font Lock mode face used to highlight SQL keywords."
203 :group 'font-lock-highlighting-faces)
204 (defvar font-lock-sql-face 'font-lock-sql-face
205 "Face name to use for SQL keywords.")
206
207 ;;
208 ;; Code to handle nested comments and unusual string escape sequences
209 ;;
210
211 (defsyntax urweb-syntax-prop-table
212 '((?\\ . ".") (?* . "."))
213 "Syntax table for text-properties")
214
215 ;; For Emacsen that have no built-in support for nested comments
216 (defun urweb-get-depth-st ()
217 (save-excursion
218 (let* ((disp (if (eq (char-before) ?\)) (progn (backward-char) -1) nil))
219 (_ (backward-char))
220 (disp (if (eq (char-before) ?\() (progn (backward-char) 0) disp))
221 (pt (point)))
222 (when disp
223 (let* ((depth
224 (save-match-data
225 (if (re-search-backward "\\*)\\|(\\*" nil t)
226 (+ (or (get-char-property (point) 'comment-depth) 0)
227 (case (char-after) (?\( 1) (?* 0))
228 disp)
229 0)))
230 (depth (if (> depth 0) depth)))
231 (put-text-property pt (1+ pt) 'comment-depth depth)
232 (when depth urweb-syntax-prop-table))))))
233
234 (defconst urweb-font-lock-syntactic-keywords
235 `(("^\\s-*\\(\\\\\\)" (1 ',urweb-syntax-prop-table))
236 ,@(unless urweb-builtin-nested-comments-flag
237 '(("(?\\(\\*\\))?" (1 (urweb-get-depth-st)))))))
238
239 (defconst urweb-font-lock-defaults
240 '(urweb-font-lock-keywords nil nil ((?_ . "w") (?' . "w")) nil
241 (font-lock-syntactic-keywords . urweb-font-lock-syntactic-keywords)))
242
243 ;;;;
244 ;;;; Imenu support
245 ;;;;
246
247 (defvar urweb-imenu-regexp
248 (concat "^[ \t]*\\(let[ \t]+\\)?"
249 (regexp-opt (append urweb-module-head-syms
250 '("and" "fun" "datatype" "type")) t)
251 "\\>"))
252
253 (defun urweb-imenu-create-index ()
254 (let (alist)
255 (goto-char (point-max))
256 (while (re-search-backward urweb-imenu-regexp nil t)
257 (save-excursion
258 (let ((kind (match-string 2))
259 (column (progn (goto-char (match-beginning 2)) (current-column)))
260 (location
261 (progn (goto-char (match-end 0))
262 (urweb-forward-spaces)
263 (when (looking-at urweb-tyvarseq-re)
264 (goto-char (match-end 0)))
265 (point)))
266 (name (urweb-forward-sym)))
267 ;; Eliminate trivial renamings.
268 (when (or (not (member kind '("structure" "signature")))
269 (progn (search-forward "=")
270 (urweb-forward-spaces)
271 (looking-at "sig\\|struct")))
272 (push (cons (concat (make-string (/ column 2) ?\ ) name) location)
273 alist)))))
274 alist))
275
276 ;;; MORE CODE FOR URWEB-MODE
277
278 ;;;###autoload (add-to-list 'load-path (file-name-directory load-file-name))
279 ;;;###autoload
280 (add-to-list 'auto-mode-alist '("\\.s\\(ml\\|ig\\)\\'" . urweb-mode))
281
282 ;;;###autoload
283 (define-derived-mode urweb-mode fundamental-mode "Ur/Web"
284 "\\<urweb-mode-map>Major mode for editing Ur/Web code.
285 This mode runs `urweb-mode-hook' just before exiting.
286 \\{urweb-mode-map}"
287 (set (make-local-variable 'font-lock-defaults) urweb-font-lock-defaults)
288 (set (make-local-variable 'outline-regexp) urweb-outline-regexp)
289 (set (make-local-variable 'imenu-create-index-function)
290 'urweb-imenu-create-index)
291 (set (make-local-variable 'add-log-current-defun-function)
292 'urweb-current-fun-name)
293 ;; Treat paragraph-separators in comments as paragraph-separators.
294 (set (make-local-variable 'paragraph-separate)
295 (concat "\\([ \t]*\\*)?\\)?\\(" paragraph-separate "\\)"))
296 (set (make-local-variable 'require-final-newline) t)
297 ;; forward-sexp-function is an experimental variable in my hacked Emacs.
298 (set (make-local-variable 'forward-sexp-function) 'urweb-user-forward-sexp)
299 ;; For XEmacs
300 (easy-menu-add urweb-mode-menu)
301 ;; Compatibility. FIXME: we should use `-' in Emacs-CVS.
302 (unless (boundp 'skeleton-positions) (set (make-local-variable '@) nil))
303 (urweb-mode-variables))
304
305 (defun urweb-mode-variables ()
306 (set-syntax-table urweb-mode-syntax-table)
307 (setq local-abbrev-table urweb-mode-abbrev-table)
308 ;; A paragraph is separated by blank lines or ^L only.
309
310 (set (make-local-variable 'indent-line-function) 'urweb-indent-line)
311 (set (make-local-variable 'comment-start) "(* ")
312 (set (make-local-variable 'comment-end) " *)")
313 (set (make-local-variable 'comment-nested) t)
314 ;;(set (make-local-variable 'block-comment-start) "* ")
315 ;;(set (make-local-variable 'block-comment-end) "")
316 ;; (set (make-local-variable 'comment-column) 40)
317 (set (make-local-variable 'comment-start-skip) "(\\*+\\s-*"))
318
319 (defun urweb-funname-of-and ()
320 "Name of the function this `and' defines, or nil if not a function.
321 Point has to be right after the `and' symbol and is not preserved."
322 (urweb-forward-spaces)
323 (if (looking-at urweb-tyvarseq-re) (goto-char (match-end 0)))
324 (let ((sym (urweb-forward-sym)))
325 (urweb-forward-spaces)
326 (unless (or (member sym '(nil "d="))
327 (member (urweb-forward-sym) '("d=")))
328 sym)))
329
330 ;;; INDENTATION !!!
331
332 (defun urweb-mark-function ()
333 "Synonym for `mark-paragraph' -- sorry.
334 If anyone has a good algorithm for this..."
335 (interactive)
336 (mark-paragraph))
337
338 (defun urweb-indent-line ()
339 "Indent current line of Ur/Web code."
340 (interactive)
341 (let ((savep (> (current-column) (current-indentation)))
342 (indent (max (or (ignore-errors (urweb-calculate-indentation)) 0) 0)))
343 (if savep
344 (save-excursion (indent-line-to indent))
345 (indent-line-to indent))))
346
347 (defun urweb-back-to-outer-indent ()
348 "Unindents to the next outer level of indentation."
349 (interactive)
350 (save-excursion
351 (beginning-of-line)
352 (skip-chars-forward "\t ")
353 (let ((start-column (current-column))
354 (indent (current-column)))
355 (if (> start-column 0)
356 (progn
357 (save-excursion
358 (while (>= indent start-column)
359 (if (re-search-backward "^[^\n]" nil t)
360 (setq indent (current-indentation))
361 (setq indent 0))))
362 (backward-delete-char-untabify (- start-column indent)))))))
363
364 (defun urweb-find-comment-indent ()
365 (save-excursion
366 (let ((depth 1))
367 (while (> depth 0)
368 (if (re-search-backward "(\\*\\|\\*)" nil t)
369 (cond
370 ;; FIXME: That's just a stop-gap.
371 ((eq (get-text-property (point) 'face) 'font-lock-string-face))
372 ((looking-at "*)") (incf depth))
373 ((looking-at comment-start-skip) (decf depth)))
374 (setq depth -1)))
375 (if (= depth 0)
376 (1+ (current-column))
377 nil))))
378
379 (defun urweb-calculate-indentation ()
380 (save-excursion
381 (beginning-of-line) (skip-chars-forward "\t ")
382 (urweb-with-ist
383 ;; Indentation for comments alone on a line, matches the
384 ;; proper indentation of the next line.
385 (when (looking-at "(\\*") (urweb-forward-spaces))
386 (let (data
387 (sym (save-excursion (urweb-forward-sym))))
388 (or
389 ;; Allow the user to override the indentation.
390 (when (looking-at (concat ".*" (regexp-quote comment-start)
391 "[ \t]*fixindent[ \t]*"
392 (regexp-quote comment-end)))
393 (current-indentation))
394
395 ;; Continued comment.
396 (and (looking-at "\\*") (urweb-find-comment-indent))
397
398 ;; Continued string ? (Added 890113 lbn)
399 (and (looking-at "\\\\")
400 (save-excursion
401 (if (save-excursion (previous-line 1)
402 (beginning-of-line)
403 (looking-at "[\t ]*\\\\"))
404 (progn (previous-line 1) (current-indentation))
405 (if (re-search-backward "[^\\\\]\"" nil t)
406 (1+ (current-column))
407 0))))
408
409 ;; Closing parens. Could be handled below with `urweb-indent-relative'?
410 (and (looking-at "\\s)")
411 (save-excursion
412 (skip-syntax-forward ")")
413 (backward-sexp 1)
414 (if (urweb-dangling-sym)
415 (urweb-indent-default 'noindent)
416 (current-column))))
417
418 (and (setq data (assoc sym urweb-close-paren))
419 (urweb-indent-relative sym data))
420
421 (and (member sym urweb-starters-syms)
422 (urweb-indent-starter sym))
423
424 (and (string= sym "|") (urweb-indent-pipe))
425
426 (urweb-indent-arg)
427 (urweb-indent-default))))))
428
429 (defsubst urweb-bolp ()
430 (save-excursion (skip-chars-backward " \t|") (bolp)))
431
432 (defun urweb-indent-starter (orig-sym)
433 "Return the indentation to use for a symbol in `urweb-starters-syms'.
434 Point should be just before the symbol ORIG-SYM and is not preserved."
435 (let ((sym (unless (save-excursion (urweb-backward-arg))
436 (urweb-backward-spaces)
437 (urweb-backward-sym))))
438 (if (member sym '(";" "d=")) (setq sym nil))
439 (if sym (urweb-get-sym-indent sym)
440 ;; FIXME: this can take a *long* time !!
441 (setq sym (urweb-find-matching-starter urweb-starters-syms))
442 ;; Don't align with `and' because it might be specially indented.
443 (if (and (or (equal orig-sym "and") (not (equal sym "and")))
444 (urweb-bolp))
445 (+ (current-column)
446 (if (and urweb-rightalign-and (equal orig-sym "and"))
447 (- (length sym) 3) 0))
448 (urweb-indent-starter orig-sym)))))
449
450 (defun urweb-indent-relative (sym data)
451 (save-excursion
452 (urweb-forward-sym) (urweb-backward-sexp nil)
453 (unless (second data) (urweb-backward-spaces) (urweb-backward-sym))
454 (+ (or (cdr (assoc sym urweb-symbol-indent)) 0)
455 (urweb-delegated-indent))))
456
457 (defun urweb-indent-pipe ()
458 (let ((sym (urweb-find-matching-starter urweb-pipeheads
459 (urweb-op-prec "|" 'back))))
460 (when sym
461 (if (string= sym "|")
462 (if (urweb-bolp) (current-column) (urweb-indent-pipe))
463 (let ((pipe-indent (or (cdr (assoc "|" urweb-symbol-indent)) -2)))
464 (when (or (member sym '("datatype"))
465 (and (equal sym "and")
466 (save-excursion
467 (forward-word 1)
468 (not (urweb-funname-of-and)))))
469 (re-search-forward "="))
470 (urweb-forward-sym)
471 (urweb-forward-spaces)
472 (+ pipe-indent (current-column)))))))
473
474 (defun urweb-find-forward (re)
475 (urweb-forward-spaces)
476 (while (and (not (looking-at re))
477 (progn
478 (or (ignore-errors (forward-sexp 1) t) (forward-char 1))
479 (urweb-forward-spaces)
480 (not (looking-at re))))))
481
482 (defun urweb-indent-arg ()
483 (and (save-excursion (ignore-errors (urweb-forward-arg)))
484 ;;(not (looking-at urweb-not-arg-re))
485 ;; looks like a function or an argument
486 (urweb-move-if (urweb-backward-arg))
487 ;; an argument
488 (if (save-excursion (not (urweb-backward-arg)))
489 ;; a first argument
490 (+ (current-column) urweb-indent-args)
491 ;; not a first arg
492 (while (and (/= (current-column) (current-indentation))
493 (urweb-move-if (urweb-backward-arg))))
494 (unless (save-excursion (urweb-backward-arg))
495 ;; all earlier args are on the same line
496 (urweb-forward-arg) (urweb-forward-spaces))
497 (current-column))))
498
499 (defun urweb-get-indent (data sym)
500 (let (d)
501 (cond
502 ((not (listp data)) data)
503 ((setq d (member sym data)) (cadr d))
504 ((and (consp data) (not (stringp (car data)))) (car data))
505 (t urweb-indent-level))))
506
507 (defun urweb-dangling-sym ()
508 "Non-nil if the symbol after point is dangling.
509 The symbol can be an Ur/Web symbol or an open-paren. \"Dangling\" means that
510 it is not on its own line but is the last element on that line."
511 (save-excursion
512 (and (not (urweb-bolp))
513 (< (urweb-point-after (end-of-line))
514 (urweb-point-after (or (urweb-forward-sym) (skip-syntax-forward "("))
515 (urweb-forward-spaces))))))
516
517 (defun urweb-delegated-indent ()
518 (if (urweb-dangling-sym)
519 (urweb-indent-default 'noindent)
520 (urweb-move-if (backward-word 1)
521 (looking-at urweb-agglomerate-re))
522 (current-column)))
523
524 (defun urweb-get-sym-indent (sym &optional style)
525 "Find the indentation for the SYM we're `looking-at'.
526 If indentation is delegated, point will move to the start of the parent.
527 Optional argument STYLE is currently ignored."
528 (assert (equal sym (save-excursion (urweb-forward-sym))))
529 (save-excursion
530 (let ((delegate (and (not (equal sym "end")) (assoc sym urweb-close-paren)))
531 (head-sym sym))
532 (when (and delegate (not (eval (third delegate))))
533 ;;(urweb-find-match-backward sym delegate)
534 (urweb-forward-sym) (urweb-backward-sexp nil)
535 (setq head-sym
536 (if (second delegate)
537 (save-excursion (urweb-forward-sym))
538 (urweb-backward-spaces) (urweb-backward-sym))))
539
540 (let ((idata (assoc head-sym urweb-indent-rule)))
541 (when idata
542 ;;(if (or style (not delegate))
543 ;; normal indentation
544 (let ((indent (urweb-get-indent (cdr idata) sym)))
545 (when indent (+ (urweb-delegated-indent) indent)))
546 ;; delgate indentation to the parent
547 ;;(urweb-forward-sym) (urweb-backward-sexp nil)
548 ;;(let* ((parent-sym (save-excursion (urweb-forward-sym)))
549 ;; (parent-indent (cdr (assoc parent-sym urweb-indent-starters))))
550 ;; check the special rules
551 ;;(+ (urweb-delegated-indent)
552 ;; (or (urweb-get-indent (cdr indent-data) 1 'strict)
553 ;; (urweb-get-indent (cdr parent-indent) 1 'strict)
554 ;; (urweb-get-indent (cdr indent-data) 0)
555 ;; (urweb-get-indent (cdr parent-indent) 0))))))))
556 )))))
557
558 (defun urweb-indent-default (&optional noindent)
559 (let* ((sym-after (save-excursion (urweb-forward-sym)))
560 (_ (urweb-backward-spaces))
561 (sym-before (urweb-backward-sym))
562 (sym-indent (and sym-before (urweb-get-sym-indent sym-before)))
563 (indent-after (or (cdr (assoc sym-after urweb-symbol-indent)) 0)))
564 (when (equal sym-before "end")
565 ;; I don't understand what's really happening here, but when
566 ;; it's `end' clearly, we need to do something special.
567 (forward-word 1)
568 (setq sym-before nil sym-indent nil))
569 (cond
570 (sym-indent
571 ;; the previous sym is an indentation introducer: follow the rule
572 (if noindent
573 ;;(current-column)
574 sym-indent
575 (+ sym-indent indent-after)))
576 ;; If we're just after a hanging open paren.
577 ((and (eq (char-syntax (preceding-char)) ?\()
578 (save-excursion (backward-char) (urweb-dangling-sym)))
579 (backward-char)
580 (urweb-indent-default))
581 (t
582 ;; default-default
583 (let* ((prec-after (urweb-op-prec sym-after 'back))
584 (prec (or (urweb-op-prec sym-before 'back) prec-after 100)))
585 ;; go back until you hit a symbol that has a lower prec than the
586 ;; "current one", or until you backed over a sym that has the same prec
587 ;; but is at the beginning of a line.
588 (while (and (not (urweb-bolp))
589 (while (urweb-move-if (urweb-backward-sexp (1- prec))))
590 (not (urweb-bolp)))
591 (while (urweb-move-if (urweb-backward-sexp prec))))
592 (if noindent
593 ;; the `noindent' case does back over an introductory symbol
594 ;; such as `fun', ...
595 (progn
596 (urweb-move-if
597 (urweb-backward-spaces)
598 (member (urweb-backward-sym) urweb-starters-syms))
599 (current-column))
600 ;; Use `indent-after' for cases such as when , or ; should be
601 ;; outdented so that their following terms are aligned.
602 (+ (if (progn
603 (if (equal sym-after ";")
604 (urweb-move-if
605 (urweb-backward-spaces)
606 (member (urweb-backward-sym) urweb-starters-syms)))
607 (and sym-after (not (looking-at sym-after))))
608 indent-after 0)
609 (current-column))))))))
610
611
612 ;; maybe `|' should be set to word-syntax in our temp syntax table ?
613 (defun urweb-current-indentation ()
614 (save-excursion
615 (beginning-of-line)
616 (skip-chars-forward " \t|")
617 (current-column)))
618
619
620 (defun urweb-find-matching-starter (syms &optional prec)
621 (let (sym)
622 (ignore-errors
623 (while
624 (progn (urweb-backward-sexp prec)
625 (setq sym (save-excursion (urweb-forward-sym)))
626 (not (or (member sym syms) (bobp)))))
627 (if (member sym syms) sym))))
628
629 (defun urweb-skip-siblings ()
630 (while (and (not (bobp)) (urweb-backward-arg))
631 (urweb-find-matching-starter urweb-starters-syms)))
632
633 (defun urweb-beginning-of-defun ()
634 (let ((sym (urweb-find-matching-starter urweb-starters-syms)))
635 (if (member sym '("fun" "and" "functor" "signature" "structure"
636 "datatype"))
637 (save-excursion (urweb-forward-sym) (urweb-forward-spaces)
638 (urweb-forward-sym))
639 ;; We're inside a "non function declaration": let's skip all other
640 ;; declarations that we find at the same level and try again.
641 (urweb-skip-siblings)
642 ;; Obviously, let's not try again if we're at bobp.
643 (unless (bobp) (urweb-beginning-of-defun)))))
644
645 (defcustom urweb-max-name-components 3
646 "Maximum number of components to use for the current function name."
647 :group 'urweb
648 :type 'integer)
649
650 (defun urweb-current-fun-name ()
651 (save-excursion
652 (let ((count urweb-max-name-components)
653 fullname name)
654 (end-of-line)
655 (while (and (> count 0)
656 (setq name (urweb-beginning-of-defun)))
657 (decf count)
658 (setq fullname (if fullname (concat name "." fullname) name))
659 ;; Skip all other declarations that we find at the same level.
660 (urweb-skip-siblings))
661 fullname)))
662
663 (provide 'urweb-mode)
664
665 ;;; urweb-mode.el ends here