changeset 51478:cc38ef483a77

(sgml-parse-tag-backward): Try and detect when we're starting from within a tag. (sgml-get-context): When called from inside a tag, do something useful. Rename the arg now that it's never used for `full' context anymore. (sgml-calculate-indent): Make `lcon' an argument. Return nil when we don't know what to do. If the initial lcon turns out to be wrong, try again. (sgml-indent-line): If sgml-calculate-indent returns nil, don't indent.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Thu, 05 Jun 2003 01:14:23 +0000
parents 3ee34a66739e
children 4f9ddb5b4715
files lisp/textmodes/sgml-mode.el
diffstat 1 files changed, 123 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/textmodes/sgml-mode.el	Thu Jun 05 00:36:28 2003 +0000
+++ b/lisp/textmodes/sgml-mode.el	Thu Jun 05 01:14:23 2003 +0000
@@ -368,8 +368,8 @@
 	      (looking-at "\\s-*<\\?xml")
 	      (when (re-search-forward
 		     (eval-when-compile
-		       (mapconcat 'identity
-				  '("<!DOCTYPE" "\\(\\w+\\)" "\\(\\w+\\)"
+		 (mapconcat 'identity
+			    '("<!DOCTYPE" "\\(\\w+\\)" "\\(\\w+\\)"
 				    "\"\\([^\"]+\\)\"" "\"\\([^\"]+\\)\"")
 				  "\\s-+"))
 		     nil t)
@@ -1045,8 +1045,14 @@
 Assume that parsing starts from within a textual context.
 Leave point at the beginning of the tag."
   (let (tag-type tag-start tag-end name)
-    (or (search-backward ">" limit 'move)
+    (or (re-search-backward "[<>]" limit 'move)
         (error "No tag found"))
+    (when (eq (char-after) ?<)
+      ;; Oops!! Looks like we were not in a textual context after all!.
+      ;; Let's try to recover.
+      (with-syntax-table sgml-tag-syntax-table
+	(forward-sexp)
+	(forward-char -1)))
     (setq tag-end (1+ (point)))
     (cond
      ((sgml-looking-back-at "--")   ; comment
@@ -1082,15 +1088,17 @@
     (goto-char tag-start)
     (sgml-make-tag tag-type tag-start tag-end name)))
 
-(defun sgml-get-context (&optional full)
+(defun sgml-get-context (&optional until)
   "Determine the context of the current position.
-If FULL is `empty', return even if the context is empty (i.e.
+By default, parse until we find a start-tag as the first thing on a line.
+If UNTIL is `empty', return even if the context is empty (i.e.
 we just skipped over some element and got to a beginning of line).
-If FULL is non-nil, parse back to the beginning of the buffer, otherwise
-parse until we find a start-tag as the first thing on a line.
 
 The context is a list of tag-info structures.  The last one is the tag
-immediately enclosing the current position."
+immediately enclosing the current position.
+
+Point is assumed to be outside of any tag.  If we discover that it's
+not the case, the first tag returned is the one inside which we are."
   (let ((here (point))
 	(ignore nil)
 	(context nil)
@@ -1101,12 +1109,13 @@
     ;;   enclosing start-tags we'll have to ignore.
     (skip-chars-backward " \t\n")      ; Make sure we're not at indentation.
     (while
-	(and (or ignore
-                 (not (if full (eq full 'empty) context))
+	(and (not (eq until 'now))
+	     (or ignore
+		 (not (if until (eq until 'empty) context))
 		 (not (sgml-at-indentation-p))
 		 (and context
 		      (/= (point) (sgml-tag-start (car context)))
-                      (sgml-unclosed-tag-p (sgml-tag-name (car context)))))
+		      (sgml-unclosed-tag-p (sgml-tag-name (car context)))))
 	     (setq tag-info (ignore-errors (sgml-parse-tag-backward))))
 
       ;; This tag may enclose things we thought were tags.  If so,
@@ -1117,6 +1126,10 @@
         (setq context (cdr context)))
 
       (cond
+       ((> (sgml-tag-end tag-info) here)
+	;; Oops!!  Looks like we were not outside of any tag, after all.
+	(push tag-info context)
+	(setq until 'now))
 
        ;; start-tag
        ((eq (sgml-tag-type tag-info) 'open)
@@ -1207,99 +1220,113 @@
   (and (not sgml-xml-mode)
        (member-ignore-case tag-name sgml-unclosed-tags)))
 
-(defun sgml-calculate-indent ()
-  "Calculate the column to which this line should be indented."
-  (let ((lcon (sgml-lexical-context)))
+(defun sgml-calculate-indent (&optional lcon)
+  "Calculate the column to which this line should be indented.
+LCON is the lexical context, if any."
+  (unless lcon (setq lcon (sgml-lexical-context)))
+
+  ;; Indent comment-start markers inside <!-- just like comment-end markers.
+  (if (and (eq (car lcon) 'tag)
+	   (looking-at "--")
+	   (save-excursion (goto-char (cdr lcon)) (looking-at "<!--")))
+      (setq lcon (cons 'comment (+ (cdr lcon) 2))))
+
+  (case (car lcon)
 
-    ;; Indent comment-start markers inside <!-- just like comment-end markers.
-    (if (and (eq (car lcon) 'tag)
-	     (looking-at "--")
-	     (save-excursion (goto-char (cdr lcon)) (looking-at "<!--")))
-	(setq lcon (cons 'comment (+ (cdr lcon) 2))))
+    (string
+     ;; Go back to previous non-empty line.
+     (while (and (> (point) (cdr lcon))
+		 (zerop (forward-line -1))
+		 (looking-at "[ \t]*$")))
+     (if (> (point) (cdr lcon))
+	 ;; Previous line is inside the string.
+	 (current-indentation)
+       (goto-char (cdr lcon))
+       (1+ (current-column))))
 
-    (case (car lcon)
-
-      (string
+    (comment
+     (let ((mark (looking-at "--")))
        ;; Go back to previous non-empty line.
        (while (and (> (point) (cdr lcon))
 		   (zerop (forward-line -1))
-		   (looking-at "[ \t]*$")))
+		   (or (looking-at "[ \t]*$")
+		       (if mark (not (looking-at "[ \t]*--"))))))
        (if (> (point) (cdr lcon))
-	   ;; Previous line is inside the string.
-	   (current-indentation)
+	   ;; Previous line is inside the comment.
+	   (skip-chars-forward " \t")
 	 (goto-char (cdr lcon))
-	 (1+ (current-column))))
+	 ;; Skip `<!' to get to the `--' with which we want to align.
+	 (search-forward "--")
+	 (goto-char (match-beginning 0)))
+       (when (and (not mark) (looking-at "--"))
+	 (forward-char 2) (skip-chars-forward " \t"))
+       (current-column)))
 
-      (comment
-       (let ((mark (looking-at "--")))
-	 ;; Go back to previous non-empty line.
-	 (while (and (> (point) (cdr lcon))
-		     (zerop (forward-line -1))
-		     (or (looking-at "[ \t]*$")
-			 (if mark (not (looking-at "[ \t]*--"))))))
-	 (if (> (point) (cdr lcon))
-	     ;; Previous line is inside the comment.
-	     (skip-chars-forward " \t")
-	   (goto-char (cdr lcon)))
-	 (when (and (not mark) (looking-at "--"))
-	   (forward-char 2) (skip-chars-forward " \t"))
-	 (current-column)))
+    ;; We don't know how to indent it.  Let's be honest about it.
+    (cdata nil)
 
-      (cdata
-       (current-column))
-
-      (tag
+    (tag
+     (goto-char (1+ (cdr lcon)))
+     (skip-chars-forward "^ \t\n")	;Skip tag name.
+     (skip-chars-forward " \t")
+     (if (not (eolp))
+	 (current-column)
+       ;; This is the first attribute: indent.
        (goto-char (1+ (cdr lcon)))
-       (skip-chars-forward "^ \t\n")	;Skip tag name.
-       (skip-chars-forward " \t")
-       (if (not (eolp))
-	   (current-column)
-	 ;; This is the first attribute: indent.
-	 (goto-char (1+ (cdr lcon)))
-	 (+ (current-column) sgml-basic-offset)))
+       (+ (current-column) sgml-basic-offset)))
 
-      (text
-       (while (looking-at "</")
-	 (forward-sexp 1)
-	 (skip-chars-forward " \t"))
-       (let* ((here (point))
-	      (unclosed (and ;; (not sgml-xml-mode)
-			     (looking-at sgml-tag-name-re)
-			     (member-ignore-case (match-string 1)
-						 sgml-unclosed-tags)
-			     (match-string 1)))
-	      (context
-	       ;; If possible, align on the previous non-empty text line.
-	       ;; Otherwise, do a more serious parsing to find the
-	       ;; tag(s) relative to which we should be indenting.
-	       (if (and (not unclosed) (skip-chars-backward " \t")
-			(< (skip-chars-backward " \t\n") 0)
-			(back-to-indentation)
-			(> (point) (cdr lcon)))
-		   nil
-		 (goto-char here)
-		 (nreverse (sgml-get-context (if unclosed nil 'empty)))))
-	      (there (point)))
-	 ;; Ignore previous unclosed start-tag in context.
-	 (while (and context unclosed
-		     (eq t (compare-strings
-			    (sgml-tag-name (car context)) nil nil
-			    unclosed nil nil t)))
-	   (setq context (cdr context)))
-	 ;; Indent to reflect nesting.
-	 (if (and context
-		  (goto-char (sgml-tag-end (car context)))
-		  (skip-chars-forward " \t\n")
-		  (< (point) here) (sgml-at-indentation-p))
-	     (current-column)
-	   (goto-char there)
-	   (+ (current-column)
-	      (* sgml-basic-offset (length context))))))
+    (text
+     (while (looking-at "</")
+       (forward-sexp 1)
+       (skip-chars-forward " \t"))
+     (let* ((here (point))
+	    (unclosed (and ;; (not sgml-xml-mode)
+		       (looking-at sgml-tag-name-re)
+		       (member-ignore-case (match-string 1)
+					   sgml-unclosed-tags)
+		       (match-string 1)))
+	    (context
+	     ;; If possible, align on the previous non-empty text line.
+	     ;; Otherwise, do a more serious parsing to find the
+	     ;; tag(s) relative to which we should be indenting.
+	     (if (and (not unclosed) (skip-chars-backward " \t")
+		      (< (skip-chars-backward " \t\n") 0)
+		      (back-to-indentation)
+		      (> (point) (cdr lcon)))
+		 nil
+	       (goto-char here)
+	       (nreverse (sgml-get-context (if unclosed nil 'empty)))))
+	    (there (point)))
+       ;; Ignore previous unclosed start-tag in context.
+       (while (and context unclosed
+		   (eq t (compare-strings
+			  (sgml-tag-name (car context)) nil nil
+			  unclosed nil nil t)))
+	 (setq context (cdr context)))
+       ;; Indent to reflect nesting.
+       (cond
+	;; If we were not in a text context after all, let's try again.
+	((and context (> (sgml-tag-end (car context)) here))
+	 (goto-char here)
+	 (sgml-calculate-indent
+	  (cons (if (memq (sgml-tag-type (car context)) '(comment cdata))
+		    (sgml-tag-type (car context)) 'tag)
+		(sgml-tag-start (car context)))))
+	;; Align on the first element after the nearest open-tag, if any.
+	((and context
+	      (goto-char (sgml-tag-end (car context)))
+	      (skip-chars-forward " \t\n")
+	      (< (point) here) (sgml-at-indentation-p))
+	 (current-column))
+	(t
+	 (goto-char there)
+	 (+ (current-column)
+	    (* sgml-basic-offset (length context)))))))
 
-      (otherwise
-       (error "Unrecognised context %s" (car lcon)))
+    (otherwise
+     (error "Unrecognised context %s" (car lcon)))
 
-      )))
+    ))
 
 (defun sgml-indent-line ()
   "Indent the current line as SGML."
@@ -1310,9 +1337,11 @@
 	    (back-to-indentation)
 	    (if (>= (point) savep) (setq savep nil))
 	    (sgml-calculate-indent))))
-    (if savep
-	(save-excursion (indent-line-to indent-col))
-      (indent-line-to indent-col))))
+    (if (null indent-col)
+	'noindent
+      (if savep
+	  (save-excursion (indent-line-to indent-col))
+	(indent-line-to indent-col)))))
 
 (defun sgml-guess-indent ()
   "Guess an appropriate value for `sgml-basic-offset'.