Commit 59444a9c authored by Stefan Monnier's avatar Stefan Monnier
Browse files

(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.
parent a5303820
......@@ -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'.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment