Mercurial > hg > xemacs-beta
comparison lisp/modes/vrml-mode.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 ;;; vrml-mode.el --- major mode for editing VRML (.wrl) files | |
2 | |
3 ;; Copyright (C) 1994 Free Software Foundation, Inc. | |
4 ;; Copyright (C) 1996 Ben Wing. | |
5 | |
6 ;; Author: Ben Wing <wing@666.com> | |
7 ;; Keywords: languages vrml modes | |
8 | |
9 ;; This file is part of XEmacs. | |
10 | |
11 ;; XEmacs is free software; you can redistribute it and/or modify it | |
12 ;; under the terms of the GNU General Public License as published by | |
13 ;; the Free Software Foundation; either version 2, or (at your option) | |
14 ;; any later version. | |
15 | |
16 ;; XEmacs is distributed in the hope that it will be useful, but | |
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 ;; General Public License for more details. | |
20 | |
21 ;; You should have received a copy of the GNU General Public License | |
22 ;; along with XEmacs; see the file COPYING. If not, write to the Free | |
23 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 | |
25 ;;; Synched up with: Not in FSF. | |
26 | |
27 ;;; Commentary: | |
28 | |
29 ;; Mostly bastardized from tcl.el. | |
30 | |
31 ;; HOW TO INSTALL: | |
32 ;; Put the following forms in your .emacs to enable autoloading of VRML | |
33 ;; mode, and auto-recognition of ".wrl" files. | |
34 ;; | |
35 ;; (autoload 'vrml-mode "vrml" "VRML mode." t) | |
36 ;; (setq auto-mode-alist (append '(("\\.wrl\\'" . vrml-mode)) | |
37 ;; auto-mode-alist)) | |
38 ;; | |
39 | |
40 ;;; Code: | |
41 | |
42 ;; | |
43 ;; User variables. | |
44 ;; | |
45 | |
46 (defvar vrml-indent-level 3 | |
47 "*Indentation of VRML statements with respect to containing block.") | |
48 | |
49 (defvar vrml-auto-newline nil | |
50 "*Non-nil means automatically newline before and after braces | |
51 inserted in VRML code.") | |
52 | |
53 (defvar vrml-tab-always-indent t | |
54 "*Control effect of TAB key. | |
55 If t (the default), always indent current line. | |
56 If nil and point is not in the indentation area at the beginning of | |
57 the line, a TAB is inserted. | |
58 Other values cause the first possible action from the following list | |
59 to take place: | |
60 | |
61 1. Move from beginning of line to correct indentation. | |
62 2. Delete an empty comment. | |
63 3. Move forward to start of comment, indenting if necessary. | |
64 4. Move forward to end of line, indenting if necessary. | |
65 5. Create an empty comment. | |
66 6. Move backward to start of comment, indenting if necessary.") | |
67 | |
68 (defvar vrml-use-hairy-comment-detector t | |
69 "*If not `nil', the the more complicated, but slower, comment | |
70 detecting function is used.") | |
71 | |
72 (defvar vrml-mode-abbrev-table nil | |
73 "Abbrev table used while in VRML mode.") | |
74 (define-abbrev-table 'vrml-mode-abbrev-table ()) | |
75 | |
76 (defvar vrml-mode-map () | |
77 "Keymap used in VRML mode.") | |
78 (if (null vrml-mode-map) | |
79 (progn | |
80 (setq vrml-mode-map (make-sparse-keymap)) | |
81 (set-keymap-name vrml-mode-map 'vrml-mode-map) | |
82 (define-key vrml-mode-map "{" 'vrml-electric-brace) | |
83 (define-key vrml-mode-map "}" 'vrml-electric-brace) | |
84 (define-key vrml-mode-map "\e\C-q" 'indent-vrml-exp) | |
85 (define-key vrml-mode-map "\177" 'backward-delete-char-untabify) | |
86 (define-key vrml-mode-map "\t" 'vrml-indent-command) | |
87 (define-key vrml-mode-map "\M-;" 'vrml-indent-for-comment) | |
88 )) | |
89 | |
90 (defvar vrml-mode-syntax-table nil | |
91 "Syntax table in use in vrml-mode buffers.") | |
92 | |
93 (if vrml-mode-syntax-table | |
94 () | |
95 (setq vrml-mode-syntax-table (make-syntax-table)) | |
96 (modify-syntax-entry ?\n ">" vrml-mode-syntax-table) | |
97 (modify-syntax-entry ?\f ">" vrml-mode-syntax-table) | |
98 (modify-syntax-entry ?\# "<" vrml-mode-syntax-table) | |
99 (modify-syntax-entry ?\\ "\\" vrml-mode-syntax-table) | |
100 (modify-syntax-entry ?% "_" vrml-mode-syntax-table) | |
101 (modify-syntax-entry ?@ "_" vrml-mode-syntax-table) | |
102 (modify-syntax-entry ?& "_" vrml-mode-syntax-table) | |
103 (modify-syntax-entry ?* "_" vrml-mode-syntax-table) | |
104 (modify-syntax-entry ?- "_" vrml-mode-syntax-table) | |
105 (modify-syntax-entry ?: "_" vrml-mode-syntax-table) | |
106 (modify-syntax-entry ?! "_" vrml-mode-syntax-table) | |
107 (modify-syntax-entry ?$ "_" vrml-mode-syntax-table) | |
108 (modify-syntax-entry ?/ "_" vrml-mode-syntax-table) | |
109 (modify-syntax-entry ?~ "_" vrml-mode-syntax-table) | |
110 (modify-syntax-entry ?< "_" vrml-mode-syntax-table) | |
111 (modify-syntax-entry ?= "_" vrml-mode-syntax-table) | |
112 (modify-syntax-entry ?> "_" vrml-mode-syntax-table) | |
113 (modify-syntax-entry ?| "_" vrml-mode-syntax-table) | |
114 (modify-syntax-entry ?+ "." vrml-mode-syntax-table) | |
115 (modify-syntax-entry ?\' "\"" vrml-mode-syntax-table)) | |
116 | |
117 (defvar vrml-mode-hook nil | |
118 "Hook run on entry to VRML mode.") | |
119 | |
120 (defvar vrml-keyword-list | |
121 '( | |
122 ; shape nodes: | |
123 "AsciiText" "Cone" "Cube" "Cylinder" "IndexedFaceSet" "IndexedLineSet" | |
124 "PointSet" "Sphere" | |
125 ; geometry and material nodes: | |
126 "Coordinate3" "FontStyle" "Info" "LOD" "Material" "MaterialBinding" | |
127 "Normal" "NormalBinding" "Texture2" "Texture2Transform" | |
128 "TextureCoordinate2" "ShapeHints" | |
129 ; transformation nodes: | |
130 "MatrixTransform" "Rotation" "Scale" "Transform" "Translation" | |
131 ;camera nodes: | |
132 "OrthographicCamera" "PerspectiveCamera" | |
133 ;lighting nodes: | |
134 "DirectionalLight" "PointLight" "SpotLight" | |
135 ;group nodes: | |
136 "Group" "Separator" "Switch" "TransformSeparator" "WWWAnchor" | |
137 ;other: | |
138 "WWWInline" | |
139 ;new VRML 2.0 nodes (#### not yet classified) | |
140 "Anchor" "Appearance" "AudioClip" "Background" "Billboard" "Box" | |
141 "Collision" "Color" "ColorInterpolator" "Coordinate" | |
142 "CoordinateInterpolator" "CylinderSensor" "DiskSensor" "ElevationGrid" | |
143 "Extrusion" "Fog" "FontStyle" "ImageTexture" "Inline" "MovieTexture" | |
144 "NavigationInfo" "NormalInterpolator" "OrientationInterpolator" | |
145 "PixelTexture" "PlaneSensor" "PositionInterpolator" "ProximitySensor" | |
146 "ScalarInterpolator" "Script" "Shape" "Sound" "SphereSensor" "Text" | |
147 "TextureTransform" "TextureCoordinate" "TimeSensor" "TouchSensor" | |
148 "Viewpoint" "VisibilitySensor" "WorldInfo" | |
149 ;VRML 2.0 node fields | |
150 "eventIn" "eventOut" "field" "exposedField" | |
151 ;misc. VRML 2.0 keywords (DEF, PROTO, EXTERNPROTO handled below) | |
152 "USE" "ROUTE" "TO" "IS" "TRUE" "FALSE" "NULL" | |
153 )) | |
154 | |
155 (defconst vrml-font-lock-keywords | |
156 (list | |
157 ;; Names of functions (and other "defining things"). | |
158 (list "\\(DEF\\|PROTO\\|EXTERNPROTO\\)[ \t\n]+\\([^ \t\n]+\\)" | |
159 2 'font-lock-function-name-face) | |
160 | |
161 ;; Keywords. Only recognized if surrounded by whitespace. | |
162 ;; FIXME consider using "not word or symbol", not | |
163 ;; "whitespace". | |
164 (cons (concat "\\(\\s-\\|^\\)\\(" | |
165 ;; FIXME Use regexp-quote? | |
166 (mapconcat 'identity vrml-keyword-list "\\|") | |
167 "\\)\\(\\s-\\|$\\)") | |
168 2) | |
169 ) | |
170 "Keywords to highlight for VRML. See variable `font-lock-keywords'.") | |
171 | |
172 ;;;###autoload | |
173 (defun vrml-mode () | |
174 "Major mode for editing VRML code. | |
175 Expression and list commands understand all VRML brackets. | |
176 Tab indents for VRML code. | |
177 Paragraphs are separated by blank lines only. | |
178 Delete converts tabs to spaces as it moves back. | |
179 | |
180 Variables controlling indentation style: | |
181 vrml-indent-level | |
182 Indentation of VRML statements within surrounding block. | |
183 | |
184 Variables controlling user interaction with mode (see variable | |
185 documentation for details): | |
186 vrml-tab-always-indent | |
187 Controls action of TAB key. | |
188 vrml-auto-newline | |
189 Non-nil means automatically newline before and after braces | |
190 inserted in VRML code. | |
191 | |
192 Turning on VRML mode calls the value of the variable `vrml-mode-hook' | |
193 with no args, if that value is non-nil. Read the documentation for | |
194 `vrml-mode-hook' to see what kinds of interesting hook functions | |
195 already exist. | |
196 | |
197 Commands: | |
198 \\{vrml-mode-map}" | |
199 (interactive) | |
200 (kill-all-local-variables) | |
201 (use-local-map vrml-mode-map) | |
202 (setq major-mode 'vrml-mode) | |
203 (setq mode-name "VRML") | |
204 (setq local-abbrev-table vrml-mode-abbrev-table) | |
205 (set-syntax-table vrml-mode-syntax-table) | |
206 | |
207 (make-local-variable 'paragraph-start) | |
208 (make-local-variable 'paragraph-separate) | |
209 (if (fboundp 'move-to-left-margin) | |
210 (progn | |
211 ;; In FSF Emacs 19.29 / XEmacs 19.14, you aren't supposed to | |
212 ;; start these with a ^. | |
213 (setq paragraph-start "$\\|") | |
214 (setq paragraph-separate paragraph-start)) | |
215 (setq paragraph-start (concat "^$\\|" page-delimiter)) | |
216 (setq paragraph-separate paragraph-start)) | |
217 (make-local-variable 'paragraph-ignore-fill-prefix) | |
218 (setq paragraph-ignore-fill-prefix t) | |
219 (make-local-variable 'fill-paragraph-function) | |
220 (setq fill-paragraph-function 'vrml-do-fill-paragraph) | |
221 | |
222 (make-local-variable 'indent-line-function) | |
223 (setq indent-line-function 'vrml-indent-line) | |
224 (make-local-variable 'require-final-newline) | |
225 (setq require-final-newline t) | |
226 | |
227 (make-local-variable 'comment-start) | |
228 (setq comment-start "# ") | |
229 (make-local-variable 'comment-start-skip) | |
230 (setq comment-start-skip "#+ *") | |
231 (make-local-variable 'comment-column) | |
232 (setq comment-column 40) | |
233 (make-local-variable 'comment-end) | |
234 (setq comment-end "") | |
235 | |
236 (make-local-variable 'outline-regexp) | |
237 (setq outline-regexp "[^\n\^M]") | |
238 (make-local-variable 'outline-level) | |
239 (setq outline-level 'vrml-outline-level) | |
240 | |
241 (make-local-variable 'font-lock-keywords) | |
242 (setq font-lock-keywords vrml-font-lock-keywords) | |
243 | |
244 (make-local-variable 'parse-sexp-ignore-comments) | |
245 (setq parse-sexp-ignore-comments t) | |
246 | |
247 (make-local-variable 'defun-prompt-regexp) | |
248 (setq defun-prompt-regexp "^[^ \t\n#}][^\n}]+}*[ \t]+") | |
249 | |
250 ;; Settings for new dabbrev code. | |
251 (make-local-variable 'dabbrev-case-fold-search) | |
252 (setq dabbrev-case-fold-search nil) | |
253 (make-local-variable 'dabbrev-case-replace) | |
254 (setq dabbrev-case-replace nil) | |
255 (make-local-variable 'dabbrev-abbrev-skip-leading-regexp) | |
256 (setq dabbrev-abbrev-skip-leading-regexp "[$!]") | |
257 (make-local-variable 'dabbrev-abbrev-char-regexp) | |
258 (setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_") | |
259 | |
260 (run-hooks 'vrml-mode-hook)) | |
261 | |
262 ;; This is used for closing braces. If vrml-auto-newline is set, can | |
263 ;; insert a newline both before and after the brace, depending on | |
264 ;; context. FIXME should this be configurable? Does anyone use this? | |
265 (defun vrml-electric-brace (arg) | |
266 "Insert character and correct line's indentation." | |
267 (interactive "p") | |
268 ;; If auto-newlining and there is stuff on the same line, insert a | |
269 ;; newline first. | |
270 (if vrml-auto-newline | |
271 (progn | |
272 (if (save-excursion | |
273 (skip-chars-backward " \t") | |
274 (bolp)) | |
275 () | |
276 (vrml-indent-line) | |
277 (newline)) | |
278 ;; In auto-newline case, must insert a newline after each | |
279 ;; brace. So an explicit loop is needed. | |
280 (while (> arg 0) | |
281 (insert last-command-char) | |
282 (vrml-indent-line) | |
283 (newline) | |
284 (setq arg (1- arg)))) | |
285 (self-insert-command arg)) | |
286 (vrml-indent-line)) | |
287 | |
288 | |
289 | |
290 (defun vrml-indent-command (&optional arg) | |
291 "Indent current line as VRML code, or in some cases insert a tab character. | |
292 If vrml-tab-always-indent is t (the default), always indent current line. | |
293 If vrml-tab-always-indent is nil and point is not in the indentation | |
294 area at the beginning of the line, a TAB is inserted. | |
295 Other values of vrml-tab-always-indent cause the first possible action | |
296 from the following list to take place: | |
297 | |
298 1. Move from beginning of line to correct indentation. | |
299 2. Delete an empty comment. | |
300 3. Move forward to start of comment, indenting if necessary. | |
301 4. Move forward to end of line, indenting if necessary. | |
302 5. Create an empty comment. | |
303 6. Move backward to start of comment, indenting if necessary." | |
304 (interactive "p") | |
305 (cond | |
306 ((not vrml-tab-always-indent) | |
307 ;; Indent if in indentation area, otherwise insert TAB. | |
308 (if (<= (current-column) (current-indentation)) | |
309 (vrml-indent-line) | |
310 (self-insert-command arg))) | |
311 ((eq vrml-tab-always-indent t) | |
312 ;; Always indent. | |
313 (vrml-indent-line)) | |
314 (t | |
315 ;; "Perl-mode" style TAB command. | |
316 (let* ((ipoint (point)) | |
317 (eolpoint (progn | |
318 (end-of-line) | |
319 (point))) | |
320 (comment-p (vrml-in-comment))) | |
321 (cond | |
322 ((= ipoint (save-excursion | |
323 (beginning-of-line) | |
324 (point))) | |
325 (beginning-of-line) | |
326 (vrml-indent-line) | |
327 ;; If indenting didn't leave us in column 0, go to the | |
328 ;; indentation. Otherwise leave point at end of line. This | |
329 ;; is a hack. | |
330 (if (= (point) (save-excursion | |
331 (beginning-of-line) | |
332 (point))) | |
333 (end-of-line) | |
334 (back-to-indentation))) | |
335 ((and comment-p (looking-at "[ \t]*$")) | |
336 ;; Empty comment, so delete it. We also delete any ";" | |
337 ;; characters at the end of the line. I think this is | |
338 ;; friendlier, but I don't know how other people will feel. | |
339 (backward-char) | |
340 (skip-chars-backward " \t;") | |
341 (delete-region (point) eolpoint)) | |
342 ((and comment-p (< ipoint (point))) | |
343 ;; Before comment, so skip to it. | |
344 (vrml-indent-line) | |
345 (indent-for-comment)) | |
346 ((/= ipoint eolpoint) | |
347 ;; Go to end of line (since we're not there yet). | |
348 (goto-char eolpoint) | |
349 (vrml-indent-line)) | |
350 ((not comment-p) | |
351 (vrml-indent-line) | |
352 (vrml-indent-for-comment)) | |
353 (t | |
354 ;; Go to start of comment. We don't leave point where it is | |
355 ;; because we want to skip comment-start-skip. | |
356 (vrml-indent-line) | |
357 (indent-for-comment))))))) | |
358 | |
359 (defun vrml-indent-line () | |
360 "Indent current line as VRML code. | |
361 Return the amount the indentation changed by." | |
362 (let ((indent (calculate-vrml-indent nil)) | |
363 beg shift-amt | |
364 (case-fold-search nil) | |
365 (pos (- (point-max) (point)))) | |
366 (beginning-of-line) | |
367 (setq beg (point)) | |
368 (cond ((eq indent nil) | |
369 (setq indent (current-indentation))) | |
370 (t | |
371 (skip-chars-forward " \t") | |
372 (if (listp indent) (setq indent (car indent))) | |
373 (cond ((= (following-char) ?}) | |
374 (setq indent (- indent vrml-indent-level))) | |
375 ((= (following-char) ?\]) | |
376 (setq indent (- indent 1)))))) | |
377 (skip-chars-forward " \t") | |
378 (setq shift-amt (- indent (current-column))) | |
379 (if (zerop shift-amt) | |
380 (if (> (- (point-max) pos) (point)) | |
381 (goto-char (- (point-max) pos))) | |
382 (delete-region beg (point)) | |
383 (indent-to indent) | |
384 ;; If initial point was within line's indentation, | |
385 ;; position after the indentation. Else stay at same point in text. | |
386 (if (> (- (point-max) pos) (point)) | |
387 (goto-char (- (point-max) pos)))) | |
388 shift-amt)) | |
389 | |
390 (defun calculate-vrml-indent (&optional parse-start) | |
391 "Return appropriate indentation for current line as VRML code. | |
392 In usual case returns an integer: the column to indent to. | |
393 Returns nil if line starts inside a string, t if in a comment." | |
394 (save-excursion | |
395 (beginning-of-line) | |
396 (let* ((indent-point (point)) | |
397 (case-fold-search nil) | |
398 state | |
399 containing-sexp | |
400 found-next-line) | |
401 (if parse-start | |
402 (goto-char parse-start) | |
403 (beginning-of-defun)) | |
404 (while (< (point) indent-point) | |
405 (setq parse-start (point)) | |
406 (setq state (parse-partial-sexp (point) indent-point 0)) | |
407 (setq containing-sexp (car (cdr state)))) | |
408 (cond ((or (nth 3 state) (nth 4 state)) | |
409 ;; Inside comment or string. Return nil or t if should | |
410 ;; not change this line | |
411 (nth 4 state)) | |
412 ((null containing-sexp) | |
413 ;; Line is at top level. | |
414 0) | |
415 (t | |
416 (goto-char containing-sexp) | |
417 (let* ((expr-start (point))) | |
418 ;; Find the first statement in the block and indent | |
419 ;; like it. The first statement in the block might be | |
420 ;; on the same line, so what we do is skip all | |
421 ;; "virtually blank" lines, looking for a non-blank | |
422 ;; one. A line is virtually blank if it only contains | |
423 ;; a comment and whitespace. We do it this funky way | |
424 ;; because we want to know if we've found a statement | |
425 ;; on some line _after_ the line holding the sexp | |
426 ;; opener. | |
427 (goto-char containing-sexp) | |
428 (forward-char) | |
429 (if (and (< (point) indent-point) | |
430 (looking-at "[ \t]*\\(#.*\\)?$")) | |
431 (progn | |
432 (forward-line) | |
433 (while (and (< (point) indent-point) | |
434 (looking-at "[ \t]*\\(#.*\\)?$")) | |
435 (setq found-next-line t) | |
436 (forward-line)))) | |
437 (if (not (or (= (char-after containing-sexp) ?{) | |
438 (and (= (char-after containing-sexp) ?\[) | |
439 (save-excursion | |
440 (goto-char containing-sexp) | |
441 (skip-chars-backward " \t\n") | |
442 (forward-char -8) | |
443 (looking-at "children"))))) | |
444 (progn | |
445 ;; Line is continuation line, or the sexp opener | |
446 ;; is not a curly brace, or we are are looking at | |
447 ;; an `expr' expression (which must be split | |
448 ;; specially). So indentation is column of first | |
449 ;; good spot after sexp opener. If there is no | |
450 ;; nonempty line before the indentation point, we | |
451 ;; use the column of the character after the sexp | |
452 ;; opener. | |
453 (if (>= (point) indent-point) | |
454 (progn | |
455 (goto-char containing-sexp) | |
456 (forward-char)) | |
457 (skip-chars-forward " \t")) | |
458 (current-column)) | |
459 ;; After a curly brace, and not a continuation line. | |
460 ;; So take indentation from first good line after | |
461 ;; start of block, unless that line is on the same | |
462 ;; line as the opening brace. In this case use the | |
463 ;; indentation of the opening brace's line, plus | |
464 ;; another indent step. If we are in the body part | |
465 ;; of an "if" or "while" then the indentation is | |
466 ;; taken from the line holding the start of the | |
467 ;; statement. | |
468 (if (and (< (point) indent-point) | |
469 found-next-line) | |
470 (current-indentation) | |
471 (if t ; commands-p | |
472 (goto-char expr-start) | |
473 (goto-char containing-sexp)) | |
474 (+ (current-indentation) vrml-indent-level))))))))) | |
475 | |
476 | |
477 | |
478 (defun indent-vrml-exp () | |
479 "Indent each line of the VRML grouping following point." | |
480 (interactive) | |
481 (let ((indent-stack (list nil)) | |
482 (contain-stack (list (point))) | |
483 (case-fold-search nil) | |
484 outer-loop-done inner-loop-done state ostate | |
485 this-indent last-sexp | |
486 (next-depth 0) | |
487 last-depth) | |
488 (save-excursion | |
489 (forward-sexp 1)) | |
490 (save-excursion | |
491 (setq outer-loop-done nil) | |
492 (while (and (not (eobp)) (not outer-loop-done)) | |
493 (setq last-depth next-depth) | |
494 ;; Compute how depth changes over this line | |
495 ;; plus enough other lines to get to one that | |
496 ;; does not end inside a comment or string. | |
497 ;; Meanwhile, do appropriate indentation on comment lines. | |
498 (setq inner-loop-done nil) | |
499 (while (and (not inner-loop-done) | |
500 (not (and (eobp) (setq outer-loop-done t)))) | |
501 (setq ostate state) | |
502 (setq state (parse-partial-sexp (point) (progn (end-of-line) (point)) | |
503 nil nil state)) | |
504 (setq next-depth (car state)) | |
505 (if (and (car (cdr (cdr state))) | |
506 (>= (car (cdr (cdr state))) 0)) | |
507 (setq last-sexp (car (cdr (cdr state))))) | |
508 (if (or (nth 4 ostate)) | |
509 (vrml-indent-line)) | |
510 (if (or (nth 3 state)) | |
511 (forward-line 1) | |
512 (setq inner-loop-done t))) | |
513 (if (<= next-depth 0) | |
514 (setq outer-loop-done t)) | |
515 (if outer-loop-done | |
516 nil | |
517 ;; If this line had ..))) (((.. in it, pop out of the levels | |
518 ;; that ended anywhere in this line, even if the final depth | |
519 ;; doesn't indicate that they ended. | |
520 (while (> last-depth (nth 6 state)) | |
521 (setq indent-stack (cdr indent-stack) | |
522 contain-stack (cdr contain-stack) | |
523 last-depth (1- last-depth))) | |
524 (if (/= last-depth next-depth) | |
525 (setq last-sexp nil)) | |
526 ;; Add levels for any parens that were started in this line. | |
527 (while (< last-depth next-depth) | |
528 (setq indent-stack (cons nil indent-stack) | |
529 contain-stack (cons nil contain-stack) | |
530 last-depth (1+ last-depth))) | |
531 (if (null (car contain-stack)) | |
532 (setcar contain-stack | |
533 (or (car (cdr state)) | |
534 (save-excursion | |
535 (forward-sexp -1) | |
536 (point))))) | |
537 (forward-line 1) | |
538 (skip-chars-forward " \t") | |
539 (if (eolp) | |
540 nil | |
541 (if (and (car indent-stack) | |
542 (>= (car indent-stack) 0)) | |
543 ;; Line is on an existing nesting level. | |
544 (setq this-indent (car indent-stack)) | |
545 ;; Just started a new nesting level. | |
546 ;; Compute the standard indent for this level. | |
547 (let ((val (calculate-vrml-indent | |
548 (if (car indent-stack) | |
549 (- (car indent-stack)))))) | |
550 (setcar indent-stack | |
551 (setq this-indent val)) | |
552 )) | |
553 (cond ((not (numberp this-indent))) | |
554 ((= (following-char) ?}) | |
555 (setq this-indent (- this-indent vrml-indent-level))) | |
556 ((= (following-char) ?\]) | |
557 (setq this-indent (- this-indent 1)))) | |
558 ;; Put chosen indentation into effect. | |
559 (or (null this-indent) | |
560 (= (current-column) | |
561 this-indent) | |
562 (progn | |
563 (delete-region (point) (progn (beginning-of-line) (point))) | |
564 (indent-to | |
565 this-indent)))))))) | |
566 ) | |
567 | |
568 ;; | |
569 ;; Auto-fill support. | |
570 ;; | |
571 | |
572 (defun vrml-real-command-p () | |
573 "Return nil if point is not at the beginning of a command. | |
574 A command is the first word on an otherwise empty line, or the | |
575 first word following an opening brace." | |
576 (save-excursion | |
577 (skip-chars-backward " \t") | |
578 (cond | |
579 ((bobp) t) | |
580 ((bolp) | |
581 (backward-char) | |
582 ;; Note -- continued comments are not supported here. I | |
583 ;; consider those to be a wart on the language. | |
584 (not (eq ?\\ (preceding-char)))) | |
585 (t | |
586 (memq (preceding-char) '(?{)))))) | |
587 | |
588 ;; FIXME doesn't actually return t. See last case. | |
589 (defun vrml-real-comment-p () | |
590 "Return t if point is just after the `#' beginning a real comment. | |
591 Does not check to see if previous char is actually `#'. | |
592 A real comment is either at the beginning of the buffer, | |
593 preceeded only by whitespace on the line, or has a preceeding | |
594 semicolon, opening brace, or opening bracket on the same line." | |
595 (save-excursion | |
596 (backward-char) | |
597 (vrml-real-command-p))) | |
598 | |
599 (defun vrml-hairy-scan-for-comment (state end always-stop) | |
600 "Determine if point is in a comment. | |
601 Returns a list of the form `(FLAG . STATE)'. STATE can be used | |
602 as input to future invocations. FLAG is nil if not in comment, | |
603 t otherwise. If in comment, leaves point at beginning of comment. | |
604 See also `vrml-simple-scan-for-comment', a simpler version that is | |
605 often right." | |
606 (let ((bol (save-excursion | |
607 (goto-char end) | |
608 (beginning-of-line) | |
609 (point))) | |
610 real-comment | |
611 last-cstart) | |
612 (while (and (not last-cstart) (< (point) end)) | |
613 (setq real-comment nil) ;In case we've looped around and it is | |
614 ;set. | |
615 (setq state (parse-partial-sexp (point) end nil nil state t)) | |
616 (if (nth 4 state) | |
617 (progn | |
618 ;; If ALWAYS-STOP is set, stop even if we don't have a | |
619 ;; real comment, or if the comment isn't on the same line | |
620 ;; as the end. | |
621 (if always-stop (setq last-cstart (point))) | |
622 ;; If we have a real comment, then set the comment | |
623 ;; starting point if we are on the same line as the ending | |
624 ;; location. | |
625 (setq real-comment (vrml-real-comment-p)) | |
626 (if real-comment | |
627 (progn | |
628 (and (> (point) bol) (setq last-cstart (point))) | |
629 ;; NOTE Emacs 19 has a misfeature whereby calling | |
630 ;; parse-partial-sexp with COMMENTSTOP set and with | |
631 ;; an initial list that says point is in a comment | |
632 ;; will cause an immediate return. So we must skip | |
633 ;; over the comment ourselves. | |
634 (beginning-of-line 2))) | |
635 ;; Frob the state to make it look like we aren't in a | |
636 ;; comment. | |
637 (setcar (nthcdr 4 state) nil)))) | |
638 (and last-cstart | |
639 (goto-char last-cstart)) | |
640 (cons real-comment state))) | |
641 | |
642 (defun vrml-hairy-in-comment () | |
643 "Return t if point is in a comment, and leave point at beginning | |
644 of comment." | |
645 (let ((save (point))) | |
646 (beginning-of-defun) | |
647 (car (vrml-hairy-scan-for-comment nil save nil)))) | |
648 | |
649 (defun vrml-simple-in-comment () | |
650 "Return t if point is in comment, and leave point at beginning | |
651 of comment. This is faster than `vrml-hairy-in-comment', but is | |
652 correct less often." | |
653 (let ((save (point)) | |
654 comment) | |
655 (beginning-of-line) | |
656 (while (and (< (point) save) (not comment)) | |
657 (search-forward "#" save 'move) | |
658 (setq comment (vrml-real-comment-p))) | |
659 comment)) | |
660 | |
661 (defun vrml-in-comment () | |
662 "Return t if point is in comment, and leave point at beginning | |
663 of comment." | |
664 (if vrml-use-hairy-comment-detector | |
665 (vrml-hairy-in-comment) | |
666 (vrml-simple-in-comment))) | |
667 | |
668 (defun vrml-do-fill-paragraph (ignore) | |
669 "fill-paragraph function for VRML mode. Only fills in a comment." | |
670 (let (in-comment col where) | |
671 (save-excursion | |
672 (end-of-line) | |
673 (setq in-comment (vrml-in-comment)) | |
674 (if in-comment | |
675 (progn | |
676 (setq where (1+ (point))) | |
677 (setq col (1- (current-column)))))) | |
678 (and in-comment | |
679 (save-excursion | |
680 (back-to-indentation) | |
681 (= col (current-column))) | |
682 ;; In a comment. Set the fill prefix, and find the paragraph | |
683 ;; boundaries by searching for lines that look like | |
684 ;; comment-only lines. | |
685 (let ((fill-prefix (buffer-substring (progn | |
686 (beginning-of-line) | |
687 (point)) | |
688 where)) | |
689 p-start p-end) | |
690 ;; Search backwards. | |
691 (save-excursion | |
692 (while (looking-at "^[ \t]*#") | |
693 (forward-line -1)) | |
694 (forward-line) | |
695 (setq p-start (point))) | |
696 | |
697 ;; Search forwards. | |
698 (save-excursion | |
699 (while (looking-at "^[ \t]*#") | |
700 (forward-line)) | |
701 (setq p-end (point))) | |
702 | |
703 ;; Narrow and do the fill. | |
704 (save-restriction | |
705 (narrow-to-region p-start p-end) | |
706 (fill-paragraph ignore))))) | |
707 t) | |
708 | |
709 (defun vrml-do-auto-fill () | |
710 "Auto-fill function for VRML mode. Only auto-fills in a comment." | |
711 (if (> (current-column) fill-column) | |
712 (let ((fill-prefix "# ") | |
713 in-comment col) | |
714 (save-excursion | |
715 (setq in-comment (vrml-in-comment)) | |
716 (if in-comment | |
717 (setq col (1- (current-column))))) | |
718 (if in-comment | |
719 (progn | |
720 (do-auto-fill) | |
721 (save-excursion | |
722 (back-to-indentation) | |
723 (delete-region (point) (save-excursion | |
724 (beginning-of-line) | |
725 (point))) | |
726 (indent-to-column col))))))) | |
727 | |
728 (defun vrml-indent-for-comment () | |
729 "Indent this line's comment to comment column, or insert an empty comment. | |
730 Is smart about syntax of VRML comments. | |
731 Parts of this were taken from indent-for-comment (simple.el)." | |
732 (interactive "*") | |
733 (end-of-line) | |
734 (or (vrml-in-comment) | |
735 (progn | |
736 ;; Not in a comment, so we have to insert one. Create an | |
737 ;; empty comment (since there isn't one on this line). | |
738 (skip-chars-backward " \t") | |
739 (let ((eolpoint (point))) | |
740 (beginning-of-line) | |
741 (if (/= (point) eolpoint) | |
742 (progn | |
743 (goto-char eolpoint) | |
744 (insert | |
745 "# ") | |
746 (backward-char)))))) | |
747 ;; Point is just after the "#" starting a comment. Move it as | |
748 ;; appropriate. | |
749 (let* ((indent (funcall comment-indent-function)) | |
750 (begpos (progn | |
751 (backward-char) | |
752 (point)))) | |
753 (if (/= begpos indent) | |
754 (progn | |
755 (skip-chars-backward " \t" (save-excursion | |
756 (beginning-of-line) | |
757 (point))) | |
758 (delete-region (point) begpos) | |
759 (indent-to indent))) | |
760 (looking-at comment-start-skip) ; Always true. | |
761 (goto-char (match-end 0)) | |
762 ;; I don't like the effect of the next two. | |
763 ;;(skip-chars-backward " \t" (match-beginning 0)) | |
764 ;;(skip-chars-backward "^ \t" (match-beginning 0)) | |
765 )) | |
766 | |
767 ;;; vrml-mode.el ends here |