aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2002-03-29 19:38:22 +0000
committerStefan Monnier2002-03-29 19:38:22 +0000
commit5f3d924defda210f55bde63c5d5f67f55e633dee (patch)
treeef3232650a14815c2abaf72a847920c371040a43
parent544bf8ad9dbcb3269952e930bc5acf4e929b9ffd (diff)
downloademacs-5f3d924defda210f55bde63c5d5f67f55e633dee.tar.gz
emacs-5f3d924defda210f55bde63c5d5f67f55e633dee.zip
(sgml-basic-offset): New var.
(sgml-name-re, sgml-attrs-re): New consts. (sgml-tag-name-re, sgml-start-tag-regex, sgml-font-lock-keywords-1) (sgml-mode): Use them. (sgml-lexical-context): Default to (point-min) if nothing else works. (sgml-calculate-indent): Indent slightly differently. (sgml-indent-line): Use back-to-indentation. (sgml-parse-dtd): New function. (sgml-unclosed-tags): New var. (html-mode): Set it.
-rw-r--r--lisp/textmodes/sgml-mode.el105
1 files changed, 66 insertions, 39 deletions
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index 415d69eb800..9ebf2d3be78 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -41,6 +41,11 @@
41 "SGML editing mode" 41 "SGML editing mode"
42 :group 'languages) 42 :group 'languages)
43 43
44(defcustom sgml-basic-offset 2
45 "*Specifies the basic indentation level for `sgml-indent-line'."
46 :type 'integer
47 :group 'sgml)
48
44(defcustom sgml-transformation 'identity 49(defcustom sgml-transformation 'identity
45 "*Default value for `skeleton-transformation' (which see) in SGML mode." 50 "*Default value for `skeleton-transformation' (which see) in SGML mode."
46 :type 'function 51 :type 'function
@@ -237,20 +242,21 @@ separated by a space."
237 :type '(choice (const nil) integer) 242 :type '(choice (const nil) integer)
238 :group 'sgml) 243 :group 'sgml)
239 244
240(defconst sgml-tag-name-re "<\\([!/?]?[[:alpha:]][-_.:[:alnum:]]*\\)") 245(defconst sgml-name-re "[_:[:alpha:]][-_.:[:alnum:]]*")
241(defconst sgml-start-tag-regex 246(defconst sgml-tag-name-re (concat "<\\([!/?]?" sgml-name-re "\\)"))
242 "<[[:alpha:]]\\([-_.:[:alnum:]= \n\t]\\|\"[^\"]*\"\\|'[^']*'\\)*" 247(defconst sgml-attrs-re "\\(?:[^\"'/><]\\|\"[^\"]*\"\\|'[^']*'\\)*")
248(defconst sgml-start-tag-regex (concat "<" sgml-name-re sgml-attrs-re)
243 "Regular expression that matches a non-empty start tag. 249 "Regular expression that matches a non-empty start tag.
244Any terminating `>' or `/' is not matched.") 250Any terminating `>' or `/' is not matched.")
245 251
246 252
247;; internal 253;; internal
248(defconst sgml-font-lock-keywords-1 254(defconst sgml-font-lock-keywords-1
249 '(("<\\([!?][[:alpha:]][-_.:[:alnum:]]*\\)" 1 font-lock-keyword-face) 255 `((,(concat "<\\([!?]" sgml-name-re "\\)") 1 font-lock-keyword-face)
250 ("<\\(/?[[:alpha:]][-_.:[:alnum:]]*\\)" 1 font-lock-function-name-face) 256 (,(concat "<\\(/?" sgml-name-re"\\)") 1 font-lock-function-name-face)
251 ;; FIXME: this doesn't cover the variables using a default value. 257 ;; FIXME: this doesn't cover the variables using a default value.
252 ("\\([[:alpha:]][-_.:[:alnum:]]*\\)=[\"']" 1 font-lock-variable-name-face) 258 (,(concat "\\(" sgml-name-re "\\)=[\"']") 1 font-lock-variable-name-face)
253 ("[&%][[:alpha:]][-_.:[:alnum:]]*;?" . font-lock-variable-name-face))) 259 (,(concat "[&%]" sgml-name-re ";?") . font-lock-variable-name-face)))
254 260
255(defconst sgml-font-lock-keywords-2 261(defconst sgml-font-lock-keywords-2
256 (append 262 (append
@@ -344,6 +350,9 @@ Otherwise, it is set to be buffer-local when the file has
344(defvar sgml-empty-tags nil 350(defvar sgml-empty-tags nil
345 "List of tags whose !ELEMENT definition says EMPTY.") 351 "List of tags whose !ELEMENT definition says EMPTY.")
346 352
353(defvar sgml-unclosed-tags nil
354 "List of tags whose !ELEMENT definition says the end-tag is optional.")
355
347(defun sgml-xml-guess () 356(defun sgml-xml-guess ()
348 "Guess whether the current buffer is XML." 357 "Guess whether the current buffer is XML."
349 (save-excursion 358 (save-excursion
@@ -396,10 +405,10 @@ Do \\[describe-key] on the following bindings to discover what they do.
396 ;; A start or end tag by itself on a line separates a paragraph. 405 ;; A start or end tag by itself on a line separates a paragraph.
397 ;; This is desirable because SGML discards a newline that appears 406 ;; This is desirable because SGML discards a newline that appears
398 ;; immediately after a start tag or immediately before an end tag. 407 ;; immediately after a start tag or immediately before an end tag.
399 (set (make-local-variable 'paragraph-separate) "[ \t]*$\\|\ 408 (set (make-local-variable 'paragraph-start) (concat "[ \t]*$\\|\
400\[ \t]*</?\\([[:alpha:]]\\([-_.:[:alnum:]= \t\n]\\|\"[^\"]*\"\\|'[^']*'\\)*\\)?>$") 409\[ \t]*</?\\(" sgml-name-re sgml-attrs-re "\\)?>"))
401 (set (make-local-variable 'paragraph-start) "[ \t]*$\\|\ 410 (set (make-local-variable 'paragraph-separate)
402\[ \t]*</?\\([[:alpha:]]\\([-_.:[:alnum:]= \t\n]\\|\"[^\"]*\"\\|'[^']*'\\)*\\)?>") 411 (concat paragraph-start "$"))
403 (set (make-local-variable 'adaptive-fill-regexp) "[ \t]*") 412 (set (make-local-variable 'adaptive-fill-regexp) "[ \t]*")
404 (set (make-local-variable 'comment-start) "<!-- ") 413 (set (make-local-variable 'comment-start) "<!-- ")
405 (set (make-local-variable 'comment-end) " -->") 414 (set (make-local-variable 'comment-end) " -->")
@@ -430,7 +439,8 @@ Do \\[describe-key] on the following bindings to discover what they do.
430 (set (make-local-variable 'comment-end-skip) "[ \t]*--\\([ \t\n]*>\\)?") 439 (set (make-local-variable 'comment-end-skip) "[ \t]*--\\([ \t\n]*>\\)?")
431 ;; This definition probably is not useful in derived modes. 440 ;; This definition probably is not useful in derived modes.
432 (set (make-local-variable 'imenu-generic-expression) 441 (set (make-local-variable 'imenu-generic-expression)
433 "<!\\(element\\|entity\\)[ \t\n]+%?[ \t\n]*\\([[:alpha:]][-_.:[:alnum:]]*\\)")) 442 (concat "<!\\(element\\|entity\\)[ \t\n]+%?[ \t\n]*\\("
443 sgml-name-re "\\)")))
434 444
435 445
436(defun sgml-comment-indent () 446(defun sgml-comment-indent ()
@@ -846,22 +856,22 @@ If non-nil LIMIT is a nearby position before point outside of any tag."
846 (save-excursion 856 (save-excursion
847 (let ((pos (point)) 857 (let ((pos (point))
848 (state nil)) 858 (state nil))
849 ;; Hopefully this regexp will match something that's not inside 859 (if limit (goto-char limit)
850 ;; a tag and also hopefully the match is nearby. 860 ;; Hopefully this regexp will match something that's not inside
851 (when (or (and limit (goto-char limit)) 861 ;; a tag and also hopefully the match is nearby.
852 (re-search-backward "^[ \t]*<" nil t)) 862 (re-search-backward "^[ \t]*<[_:[:alpha:]/%!?#]" nil 'move))
853 (with-syntax-table sgml-tag-syntax-table 863 (with-syntax-table sgml-tag-syntax-table
854 (while (< (point) pos) 864 (while (< (point) pos)
855 ;; When entering this loop we're inside text. 865 ;; When entering this loop we're inside text.
856 (skip-chars-forward "^<" pos) 866 (skip-chars-forward "^<" pos)
857 ;; We skipped text and reached a tag. Parse it. 867 ;; We skipped text and reached a tag. Parse it.
858 ;; FIXME: this does not handle CDATA and funny stuff yet. 868 ;; FIXME: this does not handle CDATA and funny stuff yet.
859 (setq state (parse-partial-sexp (point) pos 0))) 869 (setq state (parse-partial-sexp (point) pos 0)))
860 (cond 870 (cond
861 ((nth 3 state) (cons 'string (nth 8 state))) 871 ((nth 3 state) (cons 'string (nth 8 state)))
862 ((nth 4 state) (cons 'comment (nth 8 state))) 872 ((nth 4 state) (cons 'comment (nth 8 state)))
863 ((and state (> (nth 0 state) 0)) (cons 'tag (nth 1 state))) 873 ((and state (> (nth 0 state) 0)) (cons 'tag (nth 1 state)))
864 (t nil))))))) 874 (t nil))))))
865 875
866(defun sgml-beginning-of-tag (&optional top-level) 876(defun sgml-beginning-of-tag (&optional top-level)
867 "Skip to beginning of tag and return its name. 877 "Skip to beginning of tag and return its name.
@@ -965,18 +975,15 @@ With prefix argument, unquote the region."
965 (let ((context (xml-lite-get-context))) 975 (let ((context (xml-lite-get-context)))
966 (cond 976 (cond
967 ((null context) 0) ; no context 977 ((null context) 0) ; no context
968 ;; Align closing tag with the opening one.
969 ;; ((and (eq (length context) 1) (looking-at "</"))
970 ;; (goto-char (xml-lite-tag-start (car context)))
971 ;; (current-column))
972 (t 978 (t
973 (let ((here (point))) 979 (let ((here (point)))
974 (goto-char (xml-lite-tag-end (car context))) 980 (goto-char (xml-lite-tag-end (car context)))
975 (skip-chars-forward " \t\n") 981 (skip-chars-forward " \t\n")
976 (if (< (point) here) 982 (if (and (< (point) here) (xml-lite-at-indentation-p))
977 (current-column) 983 (current-column)
978 (goto-char (xml-lite-tag-start (car context))) 984 (goto-char (xml-lite-tag-start (car context)))
979 (+ (current-column) sgml-basic-offset)))))))))) 985 (+ (current-column)
986 (* sgml-basic-offset (length context))))))))))))
980 987
981(defun sgml-indent-line () 988(defun sgml-indent-line ()
982 "Indent the current line as SGML." 989 "Indent the current line as SGML."
@@ -984,15 +991,29 @@ With prefix argument, unquote the region."
984 (let* ((savep (point)) 991 (let* ((savep (point))
985 (indent-col 992 (indent-col
986 (save-excursion 993 (save-excursion
987 (beginning-of-line) 994 (back-to-indentation)
988 (skip-chars-forward " \t")
989 (if (>= (point) savep) (setq savep nil)) 995 (if (>= (point) savep) (setq savep nil))
990 ;; calculate basic indent
991 (sgml-calculate-indent)))) 996 (sgml-calculate-indent))))
992 (if savep 997 (if savep
993 (save-excursion (indent-line-to indent-col)) 998 (save-excursion (indent-line-to indent-col))
994 (indent-line-to indent-col)))) 999 (indent-line-to indent-col))))
995 1000
1001(defun sgml-parse-dtd ()
1002 "Simplistic parse of the current buffer as a DTD.
1003Currently just returns (EMPTY-TAGS UNCLOSED-TAGS)."
1004 (goto-char (point-min))
1005 (let ((empty nil)
1006 (unclosed nil))
1007 (while (re-search-forward "<!ELEMENT[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+[-O][ \t\n]+\\([-O]\\)[ \t\n]+\\([^ \t\n]+\\)" nil t)
1008 (cond
1009 ((string= (match-string 3) "EMPTY")
1010 (push (match-string-no-properties 1) empty))
1011 ((string= (match-string 2) "O")
1012 (push (match-string-no-properties 1) unclosed))))
1013 (setq empty (sort (mapcar 'downcase empty) 'string<))
1014 (setq unclosed (sort (mapcar 'downcase unclosed) 'string<))
1015 (list empty unclosed)))
1016
996;;; HTML mode 1017;;; HTML mode
997 1018
998(defcustom html-mode-hook nil 1019(defcustom html-mode-hook nil
@@ -1404,8 +1425,14 @@ To work around that, do:
1404 (setq imenu-create-index-function 'html-imenu-index) 1425 (setq imenu-create-index-function 'html-imenu-index)
1405 (when sgml-xml-mode (setq mode-name "XHTML")) 1426 (when sgml-xml-mode (setq mode-name "XHTML"))
1406 (set (make-local-variable 'sgml-empty-tags) 1427 (set (make-local-variable 'sgml-empty-tags)
1407 '("br" "hr" "img" "input" "area" "link" "param" "col" 1428 ;; From HTML-4.01's loose.dtd, parsed with `sgml-parse-dtd',
1408 "base" "meta" "basefont" "frame" "isindex" "wbr")) 1429 ;; plus manual addition of "wbr".
1430 '("area" "base" "basefont" "br" "col" "frame" "hr" "img" "input"
1431 "isindex" "link" "meta" "param" "wbr"))
1432 (set (make-local-variable 'sgml-unclosed-tags)
1433 ;; From HTML-4.01's loose.dtd, parsed with `sgml-parse-dtd'.
1434 '("body" "colgroup" "dd" "dt" "head" "html" "li" "option"
1435 "p" "tbody" "td" "tfoot" "th" "thead" "tr"))
1409 ;; It's for the user to decide if it defeats it or not -stef 1436 ;; It's for the user to decide if it defeats it or not -stef
1410 ;; (make-local-variable 'imenu-sort-function) 1437 ;; (make-local-variable 'imenu-sort-function)
1411 ;; (setq imenu-sort-function nil) ; sorting the menu defeats the purpose 1438 ;; (setq imenu-sort-function nil) ; sorting the menu defeats the purpose