Mercurial > hg > xemacs-beta
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/modes/vrml-mode.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,767 @@ +;;; vrml-mode.el --- major mode for editing VRML (.wrl) files + +;; Copyright (C) 1994 Free Software Foundation, Inc. +;; Copyright (C) 1996 Ben Wing. + +;; Author: Ben Wing <wing@666.com> +;; Keywords: languages vrml modes + +;; This file is part of XEmacs. + +;; XEmacs is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; XEmacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with XEmacs; see the file COPYING. If not, write to the Free +;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Synched up with: Not in FSF. + +;;; Commentary: + +;; Mostly bastardized from tcl.el. + +;; HOW TO INSTALL: +;; Put the following forms in your .emacs to enable autoloading of VRML +;; mode, and auto-recognition of ".wrl" files. +;; +;; (autoload 'vrml-mode "vrml" "VRML mode." t) +;; (setq auto-mode-alist (append '(("\\.wrl\\'" . vrml-mode)) +;; auto-mode-alist)) +;; + +;;; Code: + +;; +;; User variables. +;; + +(defvar vrml-indent-level 3 + "*Indentation of VRML statements with respect to containing block.") + +(defvar vrml-auto-newline nil + "*Non-nil means automatically newline before and after braces +inserted in VRML code.") + +(defvar vrml-tab-always-indent t + "*Control effect of TAB key. +If t (the default), always indent current line. +If nil and point is not in the indentation area at the beginning of +the line, a TAB is inserted. +Other values cause the first possible action from the following list +to take place: + + 1. Move from beginning of line to correct indentation. + 2. Delete an empty comment. + 3. Move forward to start of comment, indenting if necessary. + 4. Move forward to end of line, indenting if necessary. + 5. Create an empty comment. + 6. Move backward to start of comment, indenting if necessary.") + +(defvar vrml-use-hairy-comment-detector t + "*If not `nil', the the more complicated, but slower, comment +detecting function is used.") + +(defvar vrml-mode-abbrev-table nil + "Abbrev table used while in VRML mode.") +(define-abbrev-table 'vrml-mode-abbrev-table ()) + +(defvar vrml-mode-map () + "Keymap used in VRML mode.") +(if (null vrml-mode-map) + (progn + (setq vrml-mode-map (make-sparse-keymap)) + (set-keymap-name vrml-mode-map 'vrml-mode-map) + (define-key vrml-mode-map "{" 'vrml-electric-brace) + (define-key vrml-mode-map "}" 'vrml-electric-brace) + (define-key vrml-mode-map "\e\C-q" 'indent-vrml-exp) + (define-key vrml-mode-map "\177" 'backward-delete-char-untabify) + (define-key vrml-mode-map "\t" 'vrml-indent-command) + (define-key vrml-mode-map "\M-;" 'vrml-indent-for-comment) + )) + +(defvar vrml-mode-syntax-table nil + "Syntax table in use in vrml-mode buffers.") + +(if vrml-mode-syntax-table + () + (setq vrml-mode-syntax-table (make-syntax-table)) + (modify-syntax-entry ?\n ">" vrml-mode-syntax-table) + (modify-syntax-entry ?\f ">" vrml-mode-syntax-table) + (modify-syntax-entry ?\# "<" vrml-mode-syntax-table) + (modify-syntax-entry ?\\ "\\" vrml-mode-syntax-table) + (modify-syntax-entry ?% "_" vrml-mode-syntax-table) + (modify-syntax-entry ?@ "_" vrml-mode-syntax-table) + (modify-syntax-entry ?& "_" vrml-mode-syntax-table) + (modify-syntax-entry ?* "_" vrml-mode-syntax-table) + (modify-syntax-entry ?- "_" vrml-mode-syntax-table) + (modify-syntax-entry ?: "_" vrml-mode-syntax-table) + (modify-syntax-entry ?! "_" vrml-mode-syntax-table) + (modify-syntax-entry ?$ "_" vrml-mode-syntax-table) + (modify-syntax-entry ?/ "_" vrml-mode-syntax-table) + (modify-syntax-entry ?~ "_" vrml-mode-syntax-table) + (modify-syntax-entry ?< "_" vrml-mode-syntax-table) + (modify-syntax-entry ?= "_" vrml-mode-syntax-table) + (modify-syntax-entry ?> "_" vrml-mode-syntax-table) + (modify-syntax-entry ?| "_" vrml-mode-syntax-table) + (modify-syntax-entry ?+ "." vrml-mode-syntax-table) + (modify-syntax-entry ?\' "\"" vrml-mode-syntax-table)) + +(defvar vrml-mode-hook nil + "Hook run on entry to VRML mode.") + +(defvar vrml-keyword-list + '( + ; shape nodes: + "AsciiText" "Cone" "Cube" "Cylinder" "IndexedFaceSet" "IndexedLineSet" + "PointSet" "Sphere" + ; geometry and material nodes: + "Coordinate3" "FontStyle" "Info" "LOD" "Material" "MaterialBinding" + "Normal" "NormalBinding" "Texture2" "Texture2Transform" + "TextureCoordinate2" "ShapeHints" + ; transformation nodes: + "MatrixTransform" "Rotation" "Scale" "Transform" "Translation" + ;camera nodes: + "OrthographicCamera" "PerspectiveCamera" + ;lighting nodes: + "DirectionalLight" "PointLight" "SpotLight" + ;group nodes: + "Group" "Separator" "Switch" "TransformSeparator" "WWWAnchor" + ;other: + "WWWInline" + ;new VRML 2.0 nodes (#### not yet classified) + "Anchor" "Appearance" "AudioClip" "Background" "Billboard" "Box" + "Collision" "Color" "ColorInterpolator" "Coordinate" + "CoordinateInterpolator" "CylinderSensor" "DiskSensor" "ElevationGrid" + "Extrusion" "Fog" "FontStyle" "ImageTexture" "Inline" "MovieTexture" + "NavigationInfo" "NormalInterpolator" "OrientationInterpolator" + "PixelTexture" "PlaneSensor" "PositionInterpolator" "ProximitySensor" + "ScalarInterpolator" "Script" "Shape" "Sound" "SphereSensor" "Text" + "TextureTransform" "TextureCoordinate" "TimeSensor" "TouchSensor" + "Viewpoint" "VisibilitySensor" "WorldInfo" + ;VRML 2.0 node fields + "eventIn" "eventOut" "field" "exposedField" + ;misc. VRML 2.0 keywords (DEF, PROTO, EXTERNPROTO handled below) + "USE" "ROUTE" "TO" "IS" "TRUE" "FALSE" "NULL" +)) + +(defconst vrml-font-lock-keywords + (list + ;; Names of functions (and other "defining things"). + (list "\\(DEF\\|PROTO\\|EXTERNPROTO\\)[ \t\n]+\\([^ \t\n]+\\)" + 2 'font-lock-function-name-face) + + ;; Keywords. Only recognized if surrounded by whitespace. + ;; FIXME consider using "not word or symbol", not + ;; "whitespace". + (cons (concat "\\(\\s-\\|^\\)\\(" + ;; FIXME Use regexp-quote? + (mapconcat 'identity vrml-keyword-list "\\|") + "\\)\\(\\s-\\|$\\)") + 2) + ) + "Keywords to highlight for VRML. See variable `font-lock-keywords'.") + +;;;###autoload +(defun vrml-mode () + "Major mode for editing VRML code. +Expression and list commands understand all VRML brackets. +Tab indents for VRML code. +Paragraphs are separated by blank lines only. +Delete converts tabs to spaces as it moves back. + +Variables controlling indentation style: + vrml-indent-level + Indentation of VRML statements within surrounding block. + +Variables controlling user interaction with mode (see variable +documentation for details): + vrml-tab-always-indent + Controls action of TAB key. + vrml-auto-newline + Non-nil means automatically newline before and after braces + inserted in VRML code. + +Turning on VRML mode calls the value of the variable `vrml-mode-hook' +with no args, if that value is non-nil. Read the documentation for +`vrml-mode-hook' to see what kinds of interesting hook functions +already exist. + +Commands: +\\{vrml-mode-map}" + (interactive) + (kill-all-local-variables) + (use-local-map vrml-mode-map) + (setq major-mode 'vrml-mode) + (setq mode-name "VRML") + (setq local-abbrev-table vrml-mode-abbrev-table) + (set-syntax-table vrml-mode-syntax-table) + + (make-local-variable 'paragraph-start) + (make-local-variable 'paragraph-separate) + (if (fboundp 'move-to-left-margin) + (progn + ;; In FSF Emacs 19.29 / XEmacs 19.14, you aren't supposed to + ;; start these with a ^. + (setq paragraph-start "$\\|") + (setq paragraph-separate paragraph-start)) + (setq paragraph-start (concat "^$\\|" page-delimiter)) + (setq paragraph-separate paragraph-start)) + (make-local-variable 'paragraph-ignore-fill-prefix) + (setq paragraph-ignore-fill-prefix t) + (make-local-variable 'fill-paragraph-function) + (setq fill-paragraph-function 'vrml-do-fill-paragraph) + + (make-local-variable 'indent-line-function) + (setq indent-line-function 'vrml-indent-line) + (make-local-variable 'require-final-newline) + (setq require-final-newline t) + + (make-local-variable 'comment-start) + (setq comment-start "# ") + (make-local-variable 'comment-start-skip) + (setq comment-start-skip "#+ *") + (make-local-variable 'comment-column) + (setq comment-column 40) + (make-local-variable 'comment-end) + (setq comment-end "") + + (make-local-variable 'outline-regexp) + (setq outline-regexp "[^\n\^M]") + (make-local-variable 'outline-level) + (setq outline-level 'vrml-outline-level) + + (make-local-variable 'font-lock-keywords) + (setq font-lock-keywords vrml-font-lock-keywords) + + (make-local-variable 'parse-sexp-ignore-comments) + (setq parse-sexp-ignore-comments t) + + (make-local-variable 'defun-prompt-regexp) + (setq defun-prompt-regexp "^[^ \t\n#}][^\n}]+}*[ \t]+") + + ;; Settings for new dabbrev code. + (make-local-variable 'dabbrev-case-fold-search) + (setq dabbrev-case-fold-search nil) + (make-local-variable 'dabbrev-case-replace) + (setq dabbrev-case-replace nil) + (make-local-variable 'dabbrev-abbrev-skip-leading-regexp) + (setq dabbrev-abbrev-skip-leading-regexp "[$!]") + (make-local-variable 'dabbrev-abbrev-char-regexp) + (setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_") + + (run-hooks 'vrml-mode-hook)) + +;; This is used for closing braces. If vrml-auto-newline is set, can +;; insert a newline both before and after the brace, depending on +;; context. FIXME should this be configurable? Does anyone use this? +(defun vrml-electric-brace (arg) + "Insert character and correct line's indentation." + (interactive "p") + ;; If auto-newlining and there is stuff on the same line, insert a + ;; newline first. + (if vrml-auto-newline + (progn + (if (save-excursion + (skip-chars-backward " \t") + (bolp)) + () + (vrml-indent-line) + (newline)) + ;; In auto-newline case, must insert a newline after each + ;; brace. So an explicit loop is needed. + (while (> arg 0) + (insert last-command-char) + (vrml-indent-line) + (newline) + (setq arg (1- arg)))) + (self-insert-command arg)) + (vrml-indent-line)) + + + +(defun vrml-indent-command (&optional arg) + "Indent current line as VRML code, or in some cases insert a tab character. +If vrml-tab-always-indent is t (the default), always indent current line. +If vrml-tab-always-indent is nil and point is not in the indentation +area at the beginning of the line, a TAB is inserted. +Other values of vrml-tab-always-indent cause the first possible action +from the following list to take place: + + 1. Move from beginning of line to correct indentation. + 2. Delete an empty comment. + 3. Move forward to start of comment, indenting if necessary. + 4. Move forward to end of line, indenting if necessary. + 5. Create an empty comment. + 6. Move backward to start of comment, indenting if necessary." + (interactive "p") + (cond + ((not vrml-tab-always-indent) + ;; Indent if in indentation area, otherwise insert TAB. + (if (<= (current-column) (current-indentation)) + (vrml-indent-line) + (self-insert-command arg))) + ((eq vrml-tab-always-indent t) + ;; Always indent. + (vrml-indent-line)) + (t + ;; "Perl-mode" style TAB command. + (let* ((ipoint (point)) + (eolpoint (progn + (end-of-line) + (point))) + (comment-p (vrml-in-comment))) + (cond + ((= ipoint (save-excursion + (beginning-of-line) + (point))) + (beginning-of-line) + (vrml-indent-line) + ;; If indenting didn't leave us in column 0, go to the + ;; indentation. Otherwise leave point at end of line. This + ;; is a hack. + (if (= (point) (save-excursion + (beginning-of-line) + (point))) + (end-of-line) + (back-to-indentation))) + ((and comment-p (looking-at "[ \t]*$")) + ;; Empty comment, so delete it. We also delete any ";" + ;; characters at the end of the line. I think this is + ;; friendlier, but I don't know how other people will feel. + (backward-char) + (skip-chars-backward " \t;") + (delete-region (point) eolpoint)) + ((and comment-p (< ipoint (point))) + ;; Before comment, so skip to it. + (vrml-indent-line) + (indent-for-comment)) + ((/= ipoint eolpoint) + ;; Go to end of line (since we're not there yet). + (goto-char eolpoint) + (vrml-indent-line)) + ((not comment-p) + (vrml-indent-line) + (vrml-indent-for-comment)) + (t + ;; Go to start of comment. We don't leave point where it is + ;; because we want to skip comment-start-skip. + (vrml-indent-line) + (indent-for-comment))))))) + +(defun vrml-indent-line () + "Indent current line as VRML code. +Return the amount the indentation changed by." + (let ((indent (calculate-vrml-indent nil)) + beg shift-amt + (case-fold-search nil) + (pos (- (point-max) (point)))) + (beginning-of-line) + (setq beg (point)) + (cond ((eq indent nil) + (setq indent (current-indentation))) + (t + (skip-chars-forward " \t") + (if (listp indent) (setq indent (car indent))) + (cond ((= (following-char) ?}) + (setq indent (- indent vrml-indent-level))) + ((= (following-char) ?\]) + (setq indent (- indent 1)))))) + (skip-chars-forward " \t") + (setq shift-amt (- indent (current-column))) + (if (zerop shift-amt) + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos))) + (delete-region beg (point)) + (indent-to indent) + ;; If initial point was within line's indentation, + ;; position after the indentation. Else stay at same point in text. + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))) + shift-amt)) + +(defun calculate-vrml-indent (&optional parse-start) + "Return appropriate indentation for current line as VRML code. +In usual case returns an integer: the column to indent to. +Returns nil if line starts inside a string, t if in a comment." + (save-excursion + (beginning-of-line) + (let* ((indent-point (point)) + (case-fold-search nil) + state + containing-sexp + found-next-line) + (if parse-start + (goto-char parse-start) + (beginning-of-defun)) + (while (< (point) indent-point) + (setq parse-start (point)) + (setq state (parse-partial-sexp (point) indent-point 0)) + (setq containing-sexp (car (cdr state)))) + (cond ((or (nth 3 state) (nth 4 state)) + ;; Inside comment or string. Return nil or t if should + ;; not change this line + (nth 4 state)) + ((null containing-sexp) + ;; Line is at top level. + 0) + (t + (goto-char containing-sexp) + (let* ((expr-start (point))) + ;; Find the first statement in the block and indent + ;; like it. The first statement in the block might be + ;; on the same line, so what we do is skip all + ;; "virtually blank" lines, looking for a non-blank + ;; one. A line is virtually blank if it only contains + ;; a comment and whitespace. We do it this funky way + ;; because we want to know if we've found a statement + ;; on some line _after_ the line holding the sexp + ;; opener. + (goto-char containing-sexp) + (forward-char) + (if (and (< (point) indent-point) + (looking-at "[ \t]*\\(#.*\\)?$")) + (progn + (forward-line) + (while (and (< (point) indent-point) + (looking-at "[ \t]*\\(#.*\\)?$")) + (setq found-next-line t) + (forward-line)))) + (if (not (or (= (char-after containing-sexp) ?{) + (and (= (char-after containing-sexp) ?\[) + (save-excursion + (goto-char containing-sexp) + (skip-chars-backward " \t\n") + (forward-char -8) + (looking-at "children"))))) + (progn + ;; Line is continuation line, or the sexp opener + ;; is not a curly brace, or we are are looking at + ;; an `expr' expression (which must be split + ;; specially). So indentation is column of first + ;; good spot after sexp opener. If there is no + ;; nonempty line before the indentation point, we + ;; use the column of the character after the sexp + ;; opener. + (if (>= (point) indent-point) + (progn + (goto-char containing-sexp) + (forward-char)) + (skip-chars-forward " \t")) + (current-column)) + ;; After a curly brace, and not a continuation line. + ;; So take indentation from first good line after + ;; start of block, unless that line is on the same + ;; line as the opening brace. In this case use the + ;; indentation of the opening brace's line, plus + ;; another indent step. If we are in the body part + ;; of an "if" or "while" then the indentation is + ;; taken from the line holding the start of the + ;; statement. + (if (and (< (point) indent-point) + found-next-line) + (current-indentation) + (if t ; commands-p + (goto-char expr-start) + (goto-char containing-sexp)) + (+ (current-indentation) vrml-indent-level))))))))) + + + +(defun indent-vrml-exp () + "Indent each line of the VRML grouping following point." + (interactive) + (let ((indent-stack (list nil)) + (contain-stack (list (point))) + (case-fold-search nil) + outer-loop-done inner-loop-done state ostate + this-indent last-sexp + (next-depth 0) + last-depth) + (save-excursion + (forward-sexp 1)) + (save-excursion + (setq outer-loop-done nil) + (while (and (not (eobp)) (not outer-loop-done)) + (setq last-depth next-depth) + ;; Compute how depth changes over this line + ;; plus enough other lines to get to one that + ;; does not end inside a comment or string. + ;; Meanwhile, do appropriate indentation on comment lines. + (setq inner-loop-done nil) + (while (and (not inner-loop-done) + (not (and (eobp) (setq outer-loop-done t)))) + (setq ostate state) + (setq state (parse-partial-sexp (point) (progn (end-of-line) (point)) + nil nil state)) + (setq next-depth (car state)) + (if (and (car (cdr (cdr state))) + (>= (car (cdr (cdr state))) 0)) + (setq last-sexp (car (cdr (cdr state))))) + (if (or (nth 4 ostate)) + (vrml-indent-line)) + (if (or (nth 3 state)) + (forward-line 1) + (setq inner-loop-done t))) + (if (<= next-depth 0) + (setq outer-loop-done t)) + (if outer-loop-done + nil + ;; If this line had ..))) (((.. in it, pop out of the levels + ;; that ended anywhere in this line, even if the final depth + ;; doesn't indicate that they ended. + (while (> last-depth (nth 6 state)) + (setq indent-stack (cdr indent-stack) + contain-stack (cdr contain-stack) + last-depth (1- last-depth))) + (if (/= last-depth next-depth) + (setq last-sexp nil)) + ;; Add levels for any parens that were started in this line. + (while (< last-depth next-depth) + (setq indent-stack (cons nil indent-stack) + contain-stack (cons nil contain-stack) + last-depth (1+ last-depth))) + (if (null (car contain-stack)) + (setcar contain-stack + (or (car (cdr state)) + (save-excursion + (forward-sexp -1) + (point))))) + (forward-line 1) + (skip-chars-forward " \t") + (if (eolp) + nil + (if (and (car indent-stack) + (>= (car indent-stack) 0)) + ;; Line is on an existing nesting level. + (setq this-indent (car indent-stack)) + ;; Just started a new nesting level. + ;; Compute the standard indent for this level. + (let ((val (calculate-vrml-indent + (if (car indent-stack) + (- (car indent-stack)))))) + (setcar indent-stack + (setq this-indent val)) + )) + (cond ((not (numberp this-indent))) + ((= (following-char) ?}) + (setq this-indent (- this-indent vrml-indent-level))) + ((= (following-char) ?\]) + (setq this-indent (- this-indent 1)))) + ;; Put chosen indentation into effect. + (or (null this-indent) + (= (current-column) + this-indent) + (progn + (delete-region (point) (progn (beginning-of-line) (point))) + (indent-to + this-indent)))))))) + ) + +;; +;; Auto-fill support. +;; + +(defun vrml-real-command-p () + "Return nil if point is not at the beginning of a command. +A command is the first word on an otherwise empty line, or the +first word following an opening brace." + (save-excursion + (skip-chars-backward " \t") + (cond + ((bobp) t) + ((bolp) + (backward-char) + ;; Note -- continued comments are not supported here. I + ;; consider those to be a wart on the language. + (not (eq ?\\ (preceding-char)))) + (t + (memq (preceding-char) '(?{)))))) + +;; FIXME doesn't actually return t. See last case. +(defun vrml-real-comment-p () + "Return t if point is just after the `#' beginning a real comment. +Does not check to see if previous char is actually `#'. +A real comment is either at the beginning of the buffer, +preceeded only by whitespace on the line, or has a preceeding +semicolon, opening brace, or opening bracket on the same line." + (save-excursion + (backward-char) + (vrml-real-command-p))) + +(defun vrml-hairy-scan-for-comment (state end always-stop) + "Determine if point is in a comment. +Returns a list of the form `(FLAG . STATE)'. STATE can be used +as input to future invocations. FLAG is nil if not in comment, +t otherwise. If in comment, leaves point at beginning of comment. +See also `vrml-simple-scan-for-comment', a simpler version that is +often right." + (let ((bol (save-excursion + (goto-char end) + (beginning-of-line) + (point))) + real-comment + last-cstart) + (while (and (not last-cstart) (< (point) end)) + (setq real-comment nil) ;In case we've looped around and it is + ;set. + (setq state (parse-partial-sexp (point) end nil nil state t)) + (if (nth 4 state) + (progn + ;; If ALWAYS-STOP is set, stop even if we don't have a + ;; real comment, or if the comment isn't on the same line + ;; as the end. + (if always-stop (setq last-cstart (point))) + ;; If we have a real comment, then set the comment + ;; starting point if we are on the same line as the ending + ;; location. + (setq real-comment (vrml-real-comment-p)) + (if real-comment + (progn + (and (> (point) bol) (setq last-cstart (point))) + ;; NOTE Emacs 19 has a misfeature whereby calling + ;; parse-partial-sexp with COMMENTSTOP set and with + ;; an initial list that says point is in a comment + ;; will cause an immediate return. So we must skip + ;; over the comment ourselves. + (beginning-of-line 2))) + ;; Frob the state to make it look like we aren't in a + ;; comment. + (setcar (nthcdr 4 state) nil)))) + (and last-cstart + (goto-char last-cstart)) + (cons real-comment state))) + +(defun vrml-hairy-in-comment () + "Return t if point is in a comment, and leave point at beginning +of comment." + (let ((save (point))) + (beginning-of-defun) + (car (vrml-hairy-scan-for-comment nil save nil)))) + +(defun vrml-simple-in-comment () + "Return t if point is in comment, and leave point at beginning +of comment. This is faster than `vrml-hairy-in-comment', but is +correct less often." + (let ((save (point)) + comment) + (beginning-of-line) + (while (and (< (point) save) (not comment)) + (search-forward "#" save 'move) + (setq comment (vrml-real-comment-p))) + comment)) + +(defun vrml-in-comment () + "Return t if point is in comment, and leave point at beginning +of comment." + (if vrml-use-hairy-comment-detector + (vrml-hairy-in-comment) + (vrml-simple-in-comment))) + +(defun vrml-do-fill-paragraph (ignore) + "fill-paragraph function for VRML mode. Only fills in a comment." + (let (in-comment col where) + (save-excursion + (end-of-line) + (setq in-comment (vrml-in-comment)) + (if in-comment + (progn + (setq where (1+ (point))) + (setq col (1- (current-column)))))) + (and in-comment + (save-excursion + (back-to-indentation) + (= col (current-column))) + ;; In a comment. Set the fill prefix, and find the paragraph + ;; boundaries by searching for lines that look like + ;; comment-only lines. + (let ((fill-prefix (buffer-substring (progn + (beginning-of-line) + (point)) + where)) + p-start p-end) + ;; Search backwards. + (save-excursion + (while (looking-at "^[ \t]*#") + (forward-line -1)) + (forward-line) + (setq p-start (point))) + + ;; Search forwards. + (save-excursion + (while (looking-at "^[ \t]*#") + (forward-line)) + (setq p-end (point))) + + ;; Narrow and do the fill. + (save-restriction + (narrow-to-region p-start p-end) + (fill-paragraph ignore))))) + t) + +(defun vrml-do-auto-fill () + "Auto-fill function for VRML mode. Only auto-fills in a comment." + (if (> (current-column) fill-column) + (let ((fill-prefix "# ") + in-comment col) + (save-excursion + (setq in-comment (vrml-in-comment)) + (if in-comment + (setq col (1- (current-column))))) + (if in-comment + (progn + (do-auto-fill) + (save-excursion + (back-to-indentation) + (delete-region (point) (save-excursion + (beginning-of-line) + (point))) + (indent-to-column col))))))) + +(defun vrml-indent-for-comment () + "Indent this line's comment to comment column, or insert an empty comment. +Is smart about syntax of VRML comments. +Parts of this were taken from indent-for-comment (simple.el)." + (interactive "*") + (end-of-line) + (or (vrml-in-comment) + (progn + ;; Not in a comment, so we have to insert one. Create an + ;; empty comment (since there isn't one on this line). + (skip-chars-backward " \t") + (let ((eolpoint (point))) + (beginning-of-line) + (if (/= (point) eolpoint) + (progn + (goto-char eolpoint) + (insert + "# ") + (backward-char)))))) + ;; Point is just after the "#" starting a comment. Move it as + ;; appropriate. + (let* ((indent (funcall comment-indent-function)) + (begpos (progn + (backward-char) + (point)))) + (if (/= begpos indent) + (progn + (skip-chars-backward " \t" (save-excursion + (beginning-of-line) + (point))) + (delete-region (point) begpos) + (indent-to indent))) + (looking-at comment-start-skip) ; Always true. + (goto-char (match-end 0)) + ;; I don't like the effect of the next two. + ;;(skip-chars-backward " \t" (match-beginning 0)) + ;;(skip-chars-backward "^ \t" (match-beginning 0)) + )) + +;;; vrml-mode.el ends here