;; 2023-06-25
(defvar xah-html-selfclose-tags nil "List of HTML5 self-closing tag name. " )
(setq
xah-html-selfclose-tags
'(
"!doctype"
"area"
"base"
"br"
"col"
"command"
"embed"
"hr"
"img"
"input"
"keygen"
"link"
"meta"
"param"
"source"
"track"
"wbr"
))
(defun xah-html--tag-self-closing-p (TagName)
"Return t if the tag is a self-closing tag such as img, hr, br."
(member TagName xah-html-selfclose-tags))
(defun xah-html--get-tag-name (TagBegin)
"Return the tag name.
TagBegin must be on the left of angle bracket: ▮< , can be opening or closing tag.
The function returns the element name immediately to the right.
Version: 2021-06-22 2022-01-20 2023-06-27"
(when (not (char-equal (char-after TagBegin) ?<)) (error "TagBegin not on the left of <"))
(save-excursion
(let (xp1 xp2)
(goto-char TagBegin)
(forward-char 1)
(when (looking-at "/") (forward-char 1))
(setq xp1 (point))
(skip-chars-forward "!A-Za-z0-9")
(setq xp2 (point))
(buffer-substring-no-properties xp1 xp2))))
(defun xah-html--opening-tag-p (TagBegin)
"Return t if tag is opening tag or self closing, else nil.
TagBegin must be on the left of angle bracket: ▮< , can be opening or closing tag.
Version: 2021-06-22 2021-08-16 2021-08-30"
(when (not (char-equal (char-after TagBegin) ?<)) (error "TagBegin not on the left of <" ))
(not (char-equal (char-after (1+ TagBegin)) ?/)))
;; HHHH---------------------------------------------------
;; each tag must have a opening tag and a closing tag.
;; except if the tag ends in /> then it is a self closing. in xml.
;; tag sequence occure in 2 ways
;; one is nested, like so
;; ( ( 3c9de ( 530 ) toh ) )
;; the other situation is sequential
;; ( ) () ()
;; algorithm outline
;; move cursor to beginning, then, search for the next occurance of angle bracket. then, grab it, it is a tag, may be opening tag or closing, push it onto stack.
(defun xah-validate-xml-buffer ()
"validator for xml, check well-formedness
Version 2023-06-25 2023-06-27"
(interactive)
(let (xp1 xp2 xtag xisOpen xtagname
(xstack nil)
;; xstack is a list, each element is [xp1 xp2 xtagname xisOpen]
)
;; get current pos, find next angle brack pos, grab text in between, that's one tag, push it to stack
(goto-char (point-min))
(while (re-search-forward "[<>]" nil t)
(progn
(if (eq (char-before) ?<)
(setq xp1 (1- (point)))
(error "error found at position %s" (point)))
(re-search-forward "[<>]" nil t)
(if (eq (char-before) ?>)
(setq xp2 (point))
(error "error found at position %s" (point)))
(setq xtagname (xah-html--get-tag-name xp1))
(if (xah-html--tag-self-closing-p xtagname)
nil
(progn
(setq xisOpen (xah-html--opening-tag-p xp1))
(if xisOpen
(push (vector xp1 xp2 xtagname xisOpen) xstack)
(progn
;; if the current tag is closing, then, check the last item on the stack, if it's opening, and tagname match, then remove that on stack
(if
(and
(string-equal xtagname (aref (car xstack) 2))
(aref (car xstack) 3))
(pop xstack)
(push (vector xp1 xp2 xtagname xisOpen) xstack))))))))
(if (eq 0 (length xstack))
(progn
(message "done. You code is PERFECT!")
t
)
(progn
(goto-char (aref (car xstack) 0))
(message "error. here's the stack:")
(print xstack)
nil
))))