# HG changeset patch # User Stefan Monnier # Date 1017331598 0 # Node ID 3ea526b58b9e0d728305e9bf4448ff6a537f2c0b # Parent c157bec8fb409991223fde960f7f8d633522e4fa (sgml-make-syntax-table): New fun. (sgml-mode-syntax-table): Use it. (sgml-tag-syntax-table, sgml-tag-name-re): New const. (sgml-tags-invisible): Use it. (sgml-lexical-context): New fun. (sgml-maybe-end-tag, sgml-beginning-of-tag): Use it. (sgml-quote): Accept \n as entity reference terminator. (sgml-calculate-indent, sgml-indent-line): New funs. diff -r c157bec8fb40 -r 3ea526b58b9e lisp/textmodes/sgml-mode.el --- a/lisp/textmodes/sgml-mode.el Thu Mar 28 14:36:51 2002 +0000 +++ b/lisp/textmodes/sgml-mode.el Thu Mar 28 16:06:38 2002 +0000 @@ -132,18 +132,30 @@ "Keymap for SGML mode. See also `sgml-specials'.") -(defvar sgml-mode-syntax-table - (let ((table (copy-syntax-table text-mode-syntax-table))) +(defun sgml-make-syntax-table (specials) + (let ((table (make-syntax-table text-mode-syntax-table))) (modify-syntax-entry ?< "(>" table) (modify-syntax-entry ?> ")<" table) - (if (memq ?- sgml-specials) + (modify-syntax-entry ?: "_" table) + (modify-syntax-entry ?_ "_" table) + (modify-syntax-entry ?. "_" table) + (if (memq ?- specials) (modify-syntax-entry ?- "_ 1234" table)) - (if (memq ?\" sgml-specials) + (if (memq ?\" specials) (modify-syntax-entry ?\" "\"\"" table)) - (if (memq ?' sgml-specials) + (if (memq ?' specials) (modify-syntax-entry ?\' "\"'" table)) + table)) + +(defvar sgml-mode-syntax-table (sgml-make-syntax-table sgml-specials) + "Syntax table used in SGML mode. See also `sgml-specials'.") + +(defconst sgml-tag-syntax-table + (let ((table (sgml-make-syntax-table '(?- ?\" ?\')))) + (dolist (char '(?\( ?\) ?\{ ?\} ?\[ ?\] ?$ ?% ?& ?* ?+ ?/)) + (modify-syntax-entry char "." table)) table) - "Syntax table used in SGML mode. See also `sgml-specials'.") + "Syntax table used to parse SGML tags.") (defcustom sgml-name-8bit-mode nil @@ -225,6 +237,7 @@ :type '(choice (const nil) integer) :group 'sgml) +(defconst sgml-tag-name-re "<\\([!/?]?[[:alpha:]][-_.:[:alnum:]]*\\)") (defconst sgml-start-tag-regex "<[[:alpha:]]\\([-_.:[:alnum:]= \n\t]\\|\"[^\"]*\"\\|'[^']*'\\)*" "Regular expression that matches a non-empty start tag. @@ -235,7 +248,7 @@ (defconst sgml-font-lock-keywords-1 '(("<\\([!?][[:alpha:]][-_.:[:alnum:]]*\\)" 1 font-lock-keyword-face) ("<\\(/?[[:alpha:]][-_.:[:alnum:]]*\\)" 1 font-lock-function-name-face) - ;; FIXME: this doesn't cover the variable using a default value. + ;; FIXME: this doesn't cover the variables using a default value. ("\\([[:alpha:]][-_.:[:alnum:]]*\\)=[\"']" 1 font-lock-variable-name-face) ("[&%][[:alpha:]][-_.:[:alnum:]]*;?" . font-lock-variable-name-face))) @@ -634,20 +647,12 @@ "No description available"))) -(defun sgml-maybe-end-tag () - "Name self unless in position to end a tag." - (interactive) - (or (condition-case nil - (save-excursion (up-list -1)) - (error - (sgml-name-self) - t)) - (condition-case nil - (progn - (save-excursion (up-list 1)) - (sgml-name-self)) - (error (self-insert-command 1))))) - +(defun sgml-maybe-end-tag (&optional arg) + "Name self unless in position to end a tag or a prefix ARG is given." + (interactive "P") + (if (or arg (eq (car (sgml-lexical-context)) 'tag)) + (self-insert-command (prefix-numeric-value arg)) + (sgml-name-self))) (defun sgml-skip-tag-backward (arg) "Skip to beginning of tag or matching opening tag if present. @@ -769,8 +774,7 @@ (if arg (>= (prefix-numeric-value arg) 0) (not sgml-tags-invisible))) - (while (re-search-forward "<\\([!/?]?[[:alpha:]][-_.:[:alnum:]]*\\)" - nil t) + (while (re-search-forward sgml-tag-name-re nil t) (setq string (cdr (assq (intern-soft (downcase (match-string 1))) sgml-display-text))) @@ -829,24 +833,49 @@ (compile-internal command "No more errors")) +(defun sgml-lexical-context (&optional limit) + "Return the lexical context at point as (TYPE . START). +START is the location of the start of the lexical element. +TYPE is one of `string', `comment', `tag', `cdata', .... +Return nil if we are inside text (i.e. outside of any kind of tag). + +If non-nil LIMIT is a nearby position before point outside of any tag." + ;; As usual, it's difficult to get a reliable answer without parsing the + ;; whole buffer. We'll assume that a tag at indentation is outside of + ;; any string or tag or comment or ... + (save-excursion + (let ((pos (point)) + (state nil)) + ;; Hopefully this regexp will match something that's not inside + ;; a tag and also hopefully the match is nearby. + (when (or (and limit (goto-char limit)) + (re-search-backward "^[ \t]*<" nil t)) + (with-syntax-table sgml-tag-syntax-table + (while (< (point) pos) + ;; When entering this loop we're inside text. + (skip-chars-forward "^<" pos) + ;; We skipped text and reached a tag. Parse it. + ;; FIXME: this does not handle CDATA and funny stuff yet. + (setq state (parse-partial-sexp (point) pos 0))) + (cond + ((nth 3 state) (cons 'string (nth 8 state))) + ((nth 4 state) (cons 'comment (nth 8 state))) + ((and state (> (nth 0 state) 0)) (cons 'tag (nth 1 state))) + (t nil))))))) + (defun sgml-beginning-of-tag (&optional top-level) "Skip to beginning of tag and return its name. -If this can't be done, return t." - (or (if top-level - (condition-case nil - (up-list -1) - (error t)) - (>= (point) - (if (search-backward "<" nil t) - (save-excursion - (forward-list) - (point)) - 0))) - (if (looking-at "<[!/?]?[[:alpha:]][-_.:[:alnum:]]*") - (buffer-substring-no-properties - (1+ (point)) - (match-end 0)) - t))) +If this can't be done, return nil." + (let ((context (sgml-lexical-context))) + (if (eq (car context) 'tag) + (progn + (goto-char (cdr context)) + (when (looking-at sgml-tag-name-re) + (match-string-no-properties 1))) + (if top-level nil + (when context + (goto-char (cdr context)) + (sgml-beginning-of-tag t)))))) (defun sgml-value (alist) "Interactively insert value taken from attributerule ALIST. @@ -875,7 +904,7 @@ (goto-char end) (setq end start)) (if unquotep - (while (re-search-forward "&\\(amp\\|\\(l\\|\\(g\\)\\)t\\);" end t) + (while (re-search-forward "&\\(amp\\|\\(l\\|\\(g\\)\\)t\\)[;\n]" end t) (replace-match (if (match-end 3) ">" (if (match-end 2) "<" "&")))) (while (re-search-forward "[&<>]" end t) (replace-match (cdr (assq (char-before) '((?& . "&") @@ -883,6 +912,87 @@ (?> . ">")))))))) +(defun sgml-calculate-indent () + "Calculate the column to which this line should be indented." + (let ((lcon (sgml-lexical-context))) + ;; Indent comment-start markers inside