(sgml-parse-tag-backward): Try and detect
authorStefan Monnier <monnier@iro.umontreal.ca>
Thu, 5 Jun 2003 01:14:23 +0000 (01:14 +0000)
committerStefan Monnier <monnier@iro.umontreal.ca>
Thu, 5 Jun 2003 01:14:23 +0000 (01:14 +0000)
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.

lisp/textmodes/sgml-mode.el

index 87c9e82..d942c26 100644 (file)
@@ -368,8 +368,8 @@ Otherwise, it is set to be buffer-local when the file has
              (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 @@ You might want to turn on `auto-fill-mode' to get better results."
 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 @@ Leave point at the beginning of the tag."
     (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 @@ immediately enclosing the current position."
     ;;   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 @@ immediately enclosing the current position."
         (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 @@ the current start-tag or the current comment or the current cdata, ..."
   (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)))
-
-    ;; 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)
-
-      (string
+(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)
+
+    (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))))
+
+    (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))))
-
-      (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)))
-
-      (cdata
-       (current-column))
-
-      (tag
+        ;; 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)))
+
+    ;; We don't know how to indent it.  Let's be honest about it.
+    (cdata nil)
+
+    (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)))
-
-      (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))))))
-
-      (otherwise
-       (error "Unrecognised context %s" (car lcon)))
-
-      )))
+       (+ (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.
+       (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)))
+
+    ))
 
 (defun sgml-indent-line ()
   "Indent the current line as SGML."
@@ -1310,9 +1337,11 @@ the current start-tag or the current comment or the current cdata, ..."
            (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'.