* lisp/files.el (auto-mode-alist): Use html-mode for *.xhtml.
[bpt/emacs.git] / lisp / textmodes / sgml-mode.el
index 67ed335..47d2f7a 100644 (file)
@@ -1,7 +1,7 @@
-;;; sgml-mode.el --- SGML- and HTML-editing modes -*- coding: iso-2022-7bit -*-
+;;; sgml-mode.el --- SGML- and HTML-editing modes -*- coding: utf-8 -*-
 
 ;; Copyright (C) 1992, 1995, 1996, 1998, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;;   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: James Clark <jjc@jclark.com>
 ;; Maintainer: FSF
@@ -100,7 +100,13 @@ This takes effect when first loading the `sgml-mode' library.")
     (define-key map "\C-c\C-d" 'sgml-delete-tag)
     (define-key map "\C-c\^?" 'sgml-delete-tag)
     (define-key map "\C-c?" 'sgml-tag-help)
+    (define-key map "\C-c]" 'sgml-close-tag)
     (define-key map "\C-c/" 'sgml-close-tag)
+
+    ;; Redundant keybindings, for consistency with TeX mode.
+    (define-key map "\C-c\C-o" 'sgml-tag)
+    (define-key map "\C-c\C-e" 'sgml-close-tag)
+
     (define-key map "\C-c8" 'sgml-name-8bit-mode)
     (define-key map "\C-c\C-v" 'sgml-validate)
     (when sgml-quick-keys
@@ -158,6 +164,9 @@ This takes effect when first loading the `sgml-mode' library.")
   (let ((table (sgml-make-syntax-table sgml-specials)))
     (dolist (char '(?\( ?\) ?\{ ?\} ?\[ ?\] ?$ ?% ?& ?* ?+ ?/))
       (modify-syntax-entry char "." table))
+    (unless (memq ?' sgml-specials)
+      ;; Avoid that skipping a tag backwards skips any "'" prefixing it.
+      (modify-syntax-entry ?' "w" table))
     table)
   "Syntax table used to parse SGML tags.")
 
@@ -285,11 +294,12 @@ Any terminating `>' or `/' is not matched.")
 (defvar sgml-font-lock-keywords sgml-font-lock-keywords-1
   "*Rules for highlighting SGML code.  See also `sgml-tag-face-alist'.")
 
-(defvar sgml-font-lock-syntactic-keywords
+(defconst sgml-syntax-propertize-function
+  (syntax-propertize-rules
   ;; Use the `b' style of comments to avoid interference with the -- ... --
   ;; comments recognized when `sgml-specials' includes ?-.
   ;; FIXME: beware of <!--> blabla <!--> !!
-  '(("\\(<\\)!--" (1 "< b"))
+   ("\\(<\\)!--" (1 "< b"))
     ("--[ \t\n]*\\(>\\)" (1 "> b"))
     ;; Double quotes outside of tags should not introduce strings.
     ;; Be careful to call `syntax-ppss' on a position before the one we're
@@ -394,12 +404,24 @@ a DOCTYPE or an XML declaration."
     (comment-indent-new-line soft)))
 
 (defun sgml-mode-facemenu-add-face-function (face end)
-  (if (setq face (cdr (assq face sgml-face-tag-alist)))
-      (progn
-       (setq face (funcall skeleton-transformation-function face))
-       (setq facemenu-end-add-face (concat "</" face ">"))
-       (concat "<" face ">"))
-    (error "Face not configured for %s mode" (format-mode-line mode-name))))
+  (let ((tag-face (cdr (assq face sgml-face-tag-alist))))
+    (cond (tag-face
+          (setq tag-face (funcall skeleton-transformation-function tag-face))
+          (setq facemenu-end-add-face (concat "</" tag-face ">"))
+          (concat "<" tag-face ">"))
+         ((and (consp face)
+               (consp (car face))
+               (null  (cdr face))
+               (memq (caar face) '(:foreground :background)))
+          (setq facemenu-end-add-face "</span>")
+          (format "<span style=\"%s:%s\">"
+                  (if (eq (caar face) :foreground)
+                      "color"
+                    "background-color")
+                  (cadr (car face))))
+         (t
+          (error "Face not configured for %s mode"
+                 (format-mode-line mode-name))))))
 
 (defun sgml-fill-nobreak ()
   ;; Don't break between a tag name and its first argument.
@@ -457,9 +479,9 @@ Do \\[describe-key] on the following bindings to discover what they do.
        '((sgml-font-lock-keywords
           sgml-font-lock-keywords-1
           sgml-font-lock-keywords-2)
-         nil t nil nil
-         (font-lock-syntactic-keywords
-          . sgml-font-lock-syntactic-keywords)))
+         nil t))
+  (set (make-local-variable 'syntax-propertize-function)
+       sgml-syntax-propertize-function)
   (set (make-local-variable 'facemenu-add-face-function)
        'sgml-mode-facemenu-add-face-function)
   (set (make-local-variable 'sgml-xml-mode) (sgml-xml-guess))
@@ -492,11 +514,6 @@ Do \\[describe-key] on the following bindings to discover what they do.
                    "\\)\\(" sgml-name-re "\\)\\1")
           2))))
 
-;; Some programs (such as Glade 2) generate XML which has
-;; -*- mode: xml -*-.
-;;;###autoload
-(defalias 'xml-mode 'sgml-mode)
-
 (defun sgml-comment-indent ()
   (if (looking-at "--") comment-column 0))
 
@@ -511,7 +528,7 @@ Behaves electrically if `sgml-quick-keys' is non-nil."
     (insert-char ?/ 1)
     (indent-according-to-mode))
    ((eq sgml-quick-keys 'close)
-    (delete-backward-char 1)
+    (delete-char -1)
     (sgml-close-tag))
    (t
     (sgml-slash-matching arg))))
@@ -568,7 +585,7 @@ encoded keyboard operation."
   (insert ?&)
   (or char
       (setq char (read-quoted-char "Enter char or octal number")))
-  (delete-backward-char 1)
+  (delete-char -1)
   (insert char)
   (undo-boundary)
   (sgml-namify-char))
@@ -586,23 +603,19 @@ Uses `sgml-char-names'."
           ((encode-char char 'ucs)))))
     (if (not name)
        (error "Don't know the name of `%c'" char)
-      (delete-backward-char 1)
+      (delete-char -1)
       (insert (format (if (numberp name) "&#%d;" "&%s;") name)))))
 
 (defun sgml-name-self ()
   "Insert a symbolic character name according to `sgml-char-names'."
   (interactive "*")
-  (sgml-name-char last-command-char))
+  (sgml-name-char last-command-event))
 
 (defun sgml-maybe-name-self ()
   "Insert a symbolic character name according to `sgml-char-names'."
   (interactive "*")
   (if sgml-name-8bit-mode
-      (let ((mc last-command-char))
-       (if (< mc 256)
-           (setq mc (unibyte-char-to-multibyte mc)))
-       (or mc (setq mc last-command-char))
-       (sgml-name-char mc))
+      (sgml-name-char last-command-event)
     (self-insert-command 1)))
 
 (defun sgml-name-8bit-mode ()
@@ -696,7 +709,7 @@ If QUIET, do not print a message when there are no attributes for TAG."
              (sgml-value (assoc (downcase attribute) alist))
              (setq i (1- i))))
          (if (eq (preceding-char) ?\s)
-             (delete-backward-char 1)))
+             (delete-char -1)))
        car)))
 
 (defun sgml-auto-attributes (arg)
@@ -711,14 +724,22 @@ With prefix argument, only self insert."
            (eq (aref tag 0) ?/))
        (self-insert-command (prefix-numeric-value arg))
       (sgml-attributes tag)
-      (setq last-command-char ?\s)
+      (setq last-command-event ?\s)
       (or (> (point) point)
          (self-insert-command 1)))))
 
 (defun sgml-tag-help (&optional tag)
   "Display description of tag TAG.  If TAG is omitted, use the tag at point."
-  (interactive)
-  (or tag
+  (interactive
+   (list (let ((def (save-excursion
+                     (if (eq (following-char) ?<) (forward-char))
+                     (sgml-beginning-of-tag))))
+          (completing-read (if def
+                               (format "Tag (default %s): " def)
+                             "Tag: ")
+                           sgml-tag-alist nil nil nil
+                           'sgml-tag-history def))))
+  (or (and tag (> (length tag) 0))
       (save-excursion
        (if (eq (following-char) ?<)
            (forward-char))
@@ -867,6 +888,12 @@ Return t if after a closing tag."
        (setq arg (1- arg)))
       return)))
 
+(defsubst sgml-looking-back-at (str)
+  "Return t if the test before point matches STR."
+  (let ((start (- (point) (length str))))
+    (and (>= start (point-min))
+         (equal str (buffer-substring-no-properties start (point))))))
+
 (defun sgml-delete-tag (arg)
   ;; FIXME: Should be called sgml-kill-tag or should not touch the kill-ring.
   "Delete tag on or after cursor, and matching closing or opening tag.
@@ -903,7 +930,7 @@ With prefix argument ARG, repeat this ARG times."
              (kill-sexp 1))
          (setq open (point))
          (when (and (sgml-skip-tag-forward 1)
-                    (not (looking-back "/>")))
+                    (not (sgml-looking-back-at "/>")))
            (kill-sexp -1)))
        ;; Delete any resulting empty line.  If we didn't kill-sexp,
        ;; this *should* do nothing, because we're right after the tag.
@@ -1042,6 +1069,12 @@ If nil, start from a preceding tag at indentation."
                   (let ((cdata-start (point)))
                     (unless (search-forward "]]>" pos 'move)
                       (list 0 nil nil 'cdata nil nil nil nil cdata-start))))
+                ((looking-at comment-start-skip)
+                 ;; parse-partial-sexp doesn't handle <!-- comments -->,
+                 ;; or only if ?- is in sgml-specials, so match explicitly
+                 (let ((start (point)))
+                   (unless (re-search-forward comment-end-skip pos 'move)
+                     (list 0 nil nil nil t nil nil nil start))))
                  ((and sgml-xml-mode (looking-at "<\\?"))
                   ;; Processing Instructions.
                   ;; In SGML, it's basically a normal tag of the form
@@ -1086,7 +1119,7 @@ See `sgml-tag-alist' for info about attribute rules."
          (setq alist (skeleton-read '(completing-read "Value: " (cdr alist))))
          (if (string< "" alist)
              (insert alist ?\")
-           (delete-backward-char 2)))
+           (delete-char -2)))
       (insert "=\"")
       (if (cdr alist)
           (insert (skeleton-read '(completing-read "Value: " alist)))
@@ -1153,12 +1186,6 @@ You might want to turn on `auto-fill-mode' to get better results."
   (buffer-substring-no-properties
    (point) (progn (skip-syntax-forward "w_") (point))))
 
-(defsubst sgml-looking-back-at (str)
-  "Return t if the test before point matches STR."
-  (let ((start (- (point) (length str))))
-    (and (>= start (point-min))
-         (equal str (buffer-substring-no-properties start (point))))))
-
 (defun sgml-tag-text-p (start end)
   "Return non-nil if text between START and END is a tag.
 Checks among other things that the tag does not contain spurious
@@ -1752,7 +1779,7 @@ This takes effect when first loading the library.")
       ("dt" (t _ (if sgml-xml-mode "</dt>")
              "<dd>" (if sgml-xml-mode "</dd>") \n))
       ("em")
-      ;("fn" "id" "fn")  ; ???
+      ("fn" "id" "fn")  ;; Footnotes were deprecated in HTML 3.2
       ("head" \n)
       ("html" (\n
               "<head>\n"
@@ -1774,7 +1801,7 @@ This takes effect when first loading the library.")
       ("nobr")
       ("option" t ("value") ("label") ("selected" t))
       ("over" t)
-      ("person")
+      ("person") ;; Tag for person's name tag deprecated in HTML 3.2
       ("pre" \n)
       ("q")
       ("rev")
@@ -1806,11 +1833,11 @@ This takes effect when first loading the library.")
 (defvar html-tag-help
   `(,@sgml-tag-help
     ("a" . "Anchor of point or link elsewhere")
-    ("abbrev" . "?")
-    ("acronym" . "?")
+    ("abbrev" . "Abbreviation")
+    ("acronym" . "Acronym")
     ("address" . "Formatted mail address")
     ("array" . "Math array")
-    ("au" . "?")
+    ("au" . "Author")
     ("b" . "Bold face")
     ("base" . "Base address for URLs")
     ("big" . "Font size")
@@ -1825,9 +1852,10 @@ This takes effect when first loading the library.")
     ("cite" . "Citation of a document")
     ("code" . "Formatted source code")
     ("dd" . "Definition of term")
-    ("del" . "?")
-    ("dfn" . "?")
+    ("del" . "Deleted text")
+    ("dfn" . "Defining instance of a term")
     ("dir" . "Directory list (obsolete)")
+    ("div" . "Generic block-level container")
     ("dl" . "Definition list")
     ("dt" . "Term to be definined")
     ("em" . "Emphasized")
@@ -1836,7 +1864,7 @@ This takes effect when first loading the library.")
     ("figa" . "Figure anchor")
     ("figd" . "Figure description")
     ("figt" . "Figure text")
-    ;("fn" . "?")  ; ???
+    ("fn" . "Footnote")  ;; No one supports special footnote rendering.
     ("font" . "Font size")
     ("form" . "Form with input fields")
     ("group" . "Document grouping")
@@ -1852,7 +1880,7 @@ This takes effect when first loading the library.")
     ("i" . "Italic face")
     ("img" . "Graphic image")
     ("input" . "Form input field")
-    ("ins" . "?")
+    ("ins" . "Inserted text")
     ("isindex" . "Input field for index search")
     ("kbd" . "Keybard example face")
     ("lang" . "Natural language")
@@ -1868,15 +1896,16 @@ This takes effect when first loading the library.")
     ("over" . "Math fraction rule")
     ("p" . "Paragraph start")
     ("panel" . "Floating panel")
-    ("person" . "?")
+    ("person" . "Person's name")
     ("pre" . "Preformatted fixed width text")
-    ("q" . "?")
+    ("q" . "Quotation")
     ("rev" . "Reverse video")
-    ("s" . "?")
+    ("s" . "Strikeout")
     ("samp" . "Sample text")
     ("select" . "Selection list")
     ("small" . "Font size")
     ("sp" . "Nobreak space")
+    ("span" . "Generic inline container")
     ("strong" . "Standout text")
     ("sub" . "Subscript")
     ("sup" . "Superscript")
@@ -1941,7 +1970,7 @@ To work around that, do:
   (make-local-variable 'outline-heading-end-regexp)
   (make-local-variable 'outline-level)
   (make-local-variable 'sentence-end-base)
-  (setq sentence-end-base "[.?!][]\"'\e$B!I\e$,1r}\e(B)}]*\\(<[^>]*>\\)*"
+  (setq sentence-end-base "[.?!][]\"')}]*\\(<[^>]*>\\)*"
        sgml-tag-alist html-tag-alist
        sgml-face-tag-alist html-face-tag-alist
        sgml-tag-help html-tag-help