changeset 14159:93175ed23e01

Also load for .sgm and .dtd files. (sgml-specials, sgml-name-8bit-mode, sgml-char-names) (sgml-font-lock-keywords, sgml-face-tag-alist, sgml-tag-face-alist) (sgml-display-text, sgml-tag-alist, sgml-tag-help) (sgml-auto-attributes): New variables. (sgml-mode-common): New function. (sgml-mode): Most code moved to it. (sgml-name-char, sgml-name-self, sgml-maybe-name-self) (sgml-name-8bit-mode, sgml-tag, sgml-attributes, sgml-auto-attributes) (sgml-tag-help, sgml-maybe-end-tag, sgml-skip-tag-backward) (sgml-skip-tag-forward, sgml-tags-invisible): New commands. (sgml-beginning-of-tag, sgml-value): New functions.
author Richard M. Stallman <rms@gnu.org>
date Fri, 12 Jan 1996 21:14:51 +0000
parents 9d42246240c3
children d85151c5699d
files lisp/textmodes/sgml-mode.el
diffstat 1 files changed, 1096 insertions(+), 150 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/textmodes/sgml-mode.el	Fri Jan 12 19:52:53 1996 +0000
+++ b/lisp/textmodes/sgml-mode.el	Fri Jan 12 21:14:51 1996 +0000
@@ -1,10 +1,10 @@
-;;; sgml-mode.el --- SGML-editing mode
+;;; sgml-mode.el --- SGML- and HTML-editing modes
 
-;; Copyright (C) 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1995, 1996 Free Software Foundation, Inc.
 
 ;; Author: James Clark <jjc@clark.com>
-;; Adapted-By: ESR
-;; Keywords: wp
+;; Adapted-By: ESR; Daniel.Pfeiffer@Informatik.START.dbp.de
+;; Keywords: wp, hypermedia, comm, languages
 
 ;; This file is part of GNU Emacs.
 
@@ -24,20 +24,145 @@
 
 ;;; Commentary:
 
-;; Major mode for editing the SGML document-markup language.
+;; Configurable major mode for editing document in the SGML standard general
+;; markup language.  As an example contains a mode for editing the derived
+;; HTML hypertext markup language.
 
 ;;; Code:
 
-(provide 'sgml-mode)
-(require 'compile)
+;;;###autoload
+(or (rassq 'sgml-mode auto-mode-alist)
+    (setq auto-mode-alist `(("\\.sgml?\\'" . sgml-mode)
+			    ("\\.dtd\\'" . sgml-mode)
+			    ,@auto-mode-alist)))
+
+
+;; As long as Emacs' syntax can't be complemented with predicates to context
+;; sensitively confirm the syntax of characters, we have to live with this
+;; kludgy kind of tradeoff.
+(defvar sgml-specials '(?\" ?-)
+  "List of characters that have a special meaning for sgml-mode.
+This list is used when first loading the sgml-mode library.
+The supported characters and potential disadvantages are:
+
+  ?\\\"	Makes \" in text start a string.
+  ?'	Makes ' in text start a string.
+  ?-	Makes -- in text start a comment.
+
+When only one of ?\\\" or ?' are included, \"'\" or '\"' as it can be found in
+DTDs, start a string.  To partially avoid this problem this also makes these
+self insert as named entities.  <!----> must contain an even multiple of two
+(4, 8, ...) minuses, or Emacs' syntax mechanism won't recognize a comment.")
+
 
-;;; sgmls is a free SGML parser available from
-;;; ftp.uu.net:pub/text-processing/sgml
-;;; Its error messages can be parsed by next-error.
-;;; The -s option suppresses output.
+(defvar sgml-mode-map
+  (let ((map (list 'keymap (make-vector 256 nil)))
+	(menu-map (make-sparse-keymap "SGML")))
+    (define-key map "\t" 'indent-relative-maybe)
+    (define-key map "\C-c\C-i" 'sgml-tags-invisible)
+    (define-key map "/" 'sgml-slash)
+    (define-key map "&" 'sgml-name-char)
+    (define-key map "<" 'sgml-tag)
+    (define-key map "\C-c\C-a" 'sgml-attributes)
+    (define-key map "\C-c\C-b" 'sgml-skip-tag-backward)
+    (define-key map [?\C-c left] 'sgml-skip-tag-backward)
+    (define-key map "\C-c\C-f" 'sgml-skip-tag-forward)
+    (define-key map [?\C-c right] 'sgml-skip-tag-forward)
+    (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 " " 'sgml-auto-attributes)
+    (define-key map ">" 'sgml-maybe-end-tag)
+    (if (memq ?\" sgml-specials)
+	(define-key map "\"" 'sgml-name-self))
+    (if (memq ?' sgml-specials)
+	(define-key map "'" 'sgml-name-self))
+    (define-key map "\C-c8" 'sgml-name-8bit-mode)
+    (define-key map "\C-c\C-v" 'sgml-validate)
+    (let ((c 127)
+	  (map (nth 1 map)))
+      (while (< (setq c (1+ c)) 256)
+	(aset map c 'sgml-maybe-name-self)))
+    (define-key map [menu-bar sgml] (cons "SGML" menu-map))
+    (define-key menu-map [sgml-validate] '("Validate" . sgml-validate))
+    (define-key menu-map [sgml-name-8bit-mode]
+      '("Toggle 8 Bit Insertion" . sgml-name-8bit-mode))
+    (define-key menu-map [sgml-tags-invisible]
+      '("Toggle Tag Visibility" . sgml-tags-invisible))
+    (define-key menu-map [sgml-tag-help]
+      '("Describe Tag" . sgml-tag-help))
+    (define-key menu-map [sgml-delete-tag]
+      '("Delete Tag" . sgml-delete-tag))
+    (define-key menu-map [sgml-skip-tag-forward]
+      '("Forward Tag" . sgml-skip-tag-forward))
+    (define-key menu-map [sgml-skip-tag-backward]
+      '("Backward Tag" . sgml-skip-tag-backward))
+    (define-key menu-map [sgml-attributes]
+      '("Insert Attributes" . sgml-attributes))
+    (define-key menu-map [sgml-tag] '("Insert Tag" . sgml-tag))
+    map)
+  "Keymap for SGML mode.  See also `sgml-specials'.")
+
 
-(defconst sgml-validate-command
-  "sgmls -s"
+(defvar sgml-mode-syntax-table
+  (let ((table (copy-syntax-table text-mode-syntax-table)))
+    (modify-syntax-entry ?< "(>" table)
+    (modify-syntax-entry ?> ")<" table)
+    (if (memq ?- sgml-specials)
+	(modify-syntax-entry ?- "_ 1234" table))
+    (if (memq ?\" sgml-specials)
+	(modify-syntax-entry ?\" "\"\"" table))
+    (if (memq ?' sgml-specials)
+	(modify-syntax-entry ?\' "\"'" table))
+    table)
+  "Syntax table used in SGML mode.  See also `sgml-specials'.")
+
+
+(defvar sgml-name-8bit-mode nil
+  "*When non-`nil' insert 8 bit characters with their names.")
+
+(defvar sgml-char-names
+  [nil nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   "ensp" "excl" "quot" "num" "dollar" "percnt" "amp" "apos"
+   "lpar" "rpar" "ast" "plus" "comma" "hyphen" "period" "sol"
+   nil nil nil nil nil nil nil nil
+   nil nil "colon" "semi" "lt" "eq" "gt" "quest"
+   "commat" nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil "lsqb" nil "rsqb" "uarr" "lowbar"
+   "lsquo" nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil "lcub" "verbar" "rcub" "tilde" nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   nil nil nil nil nil nil nil nil
+   "nbsp" "iexcl" "cent" "pound" "curren" "yen" "brvbar" "sect"
+   "uml" "copy" "ordf" "laquo" "not" "shy" "reg" "macr"
+   "ring" "plusmn" "sup2" "sup3" "acute" "micro" "para" "middot"
+   "cedil" "sup1" "ordm" "raquo" "frac14" "half" "frac34" "iquest"
+   "Agrave" "Aacute" "Acirc" "Atilde" "Auml" "Aring" "AElig" "Ccedil"
+   "Egrave" "Eacute" "Ecirc" "Euml" "Igrave" "Iacute" "Icirc" "Iuml"
+   "ETH" "Ntilde" "Ograve" "Oacute" "Ocirc" "Otilde" "Ouml" nil
+   "Oslash" "Ugrave" "Uacute" "Ucirc" "Uuml" "Yacute" "THORN" "szlig"
+   "agrave" "aacute" "acirc" "atilde" "auml" "aring" "aelig" "ccedil"
+   "egrave" "eacute" "ecirc" "euml" "igrave" "iacute" "icirc" "iuml"
+   "eth" "ntilde" "ograve" "oacute" "ocirc" "otilde" "ouml" "divide"
+   "oslash" "ugrave" "uacute" "ucirc" "uuml" "yacute" "thorn" "yuml"]
+  "Vector of symbolic character names without `&' and `;'.")
+
+
+;; sgmls is a free SGML parser available from
+;; ftp.uu.net:pub/text-processing/sgml
+;; Its error messages can be parsed by next-error.
+;; The -s option suppresses output.
+
+(defvar sgml-validate-command "sgmls -s"
   "*The command to validate an SGML document.
 The file name of current buffer file name will be appended to this,
 separated by a space.")
@@ -45,154 +170,192 @@
 (defvar sgml-saved-validate-command nil
   "The command last used to validate in this buffer.")
 
-(defvar sgml-mode-map nil "Keymap for SGML mode")
 
-(if sgml-mode-map
-    ()
-  (setq sgml-mode-map (make-sparse-keymap))
-  (define-key sgml-mode-map ">" 'sgml-close-angle)
-  (define-key sgml-mode-map "/" 'sgml-slash)
-  (define-key sgml-mode-map "\C-c\C-v" 'sgml-validate))
-
-;;;###autoload
-(defun sgml-mode ()
-  "Major mode for editing SGML.
-Makes > display the matching <.  Makes / display matching /.
-Use \\[sgml-validate] to validate your document with an SGML parser."
-  (interactive)
-  (kill-all-local-variables)
-  (setq local-abbrev-table text-mode-abbrev-table)
-  (use-local-map sgml-mode-map)
-  (setq mode-name "SGML")
-  (setq major-mode 'sgml-mode)
-  (make-local-variable 'paragraph-start)
-  ;; A start or end tag by itself on a line separates a paragraph.
-  ;; This is desirable because SGML discards a newline that appears
-  ;; immediately after a start tag or immediately before an end tag.
-  (setq paragraph-start
-	"^[ \t\n]\\|\
-\\(</?\\([A-Za-z]\\([-.A-Za-z0-9= \t\n]\\|\"[^\"]*\"\\|'[^']*'\\)*\\)?>$\\)")
-  (make-local-variable 'paragraph-separate)
-  (setq paragraph-separate
-	"^[ \t\n]*$\\|\
-^</?\\([A-Za-z]\\([-.A-Za-z0-9= \t\n]\\|\"[^\"]*\"\\|'[^']*'\\)*\\)?>$")
-  (make-local-variable 'sgml-saved-validate-command)
-  (set-syntax-table text-mode-syntax-table)
-  (make-local-variable 'comment-start)
-  (setq comment-start "<!-- ")
-  (make-local-variable 'comment-end)
-  (setq comment-end " -->")
-  (make-local-variable 'comment-indent-function)
-  (setq comment-indent-function 'sgml-comment-indent)
-  (make-local-variable 'comment-start-skip)
-  ;; This will allow existing comments within declarations to be
-  ;; recognized.
-  (setq comment-start-skip "--[ \t]*")
-  (run-hooks 'text-mode-hook 'sgml-mode-hook))
-
-(defun sgml-comment-indent ()
-  (if (and (looking-at "--")
-	   (not (and (eq (char-after (1- (point))) ?!)
-		     (eq (char-after (- (point) 2)) ?<))))
-      (progn
-	(skip-chars-backward " \t")
-	(max comment-column (1+ (current-column))))
-    0))
+;;; I doubt that null end tags are used much for large elements,
+;;; so use a small distance here.
+(defconst sgml-slash-distance 1000
+  "*If non-nil, is the maximum distance to search for matching /.")
 
 (defconst sgml-start-tag-regex
   "<[A-Za-z]\\([-.A-Za-z0-9= \n\t]\\|\"[^\"]*\"\\|'[^']*'\\)*"
   "Regular expression that matches a non-empty start tag.
 Any terminating > or / is not matched.")
 
-(defvar sgml-mode-markup-syntax-table nil
-  "Syntax table used for scanning SGML markup.")
+
+(defvar sgml-font-lock-keywords
+  '(("<\\([!?][a-z0-9]+\\)" 1 font-lock-keyword-face)
+    ("<\\(/?[a-z0-9]+\\)" 1 font-lock-function-name-face)
+    ("[&%][-.A-Za-z0-9]+;?" . font-lock-variable-name-face))
+  "*Rules for highlighting SGML code.  See also `sgml-tag-face-alist'.")
+
+;; internal
+(defvar sgml-font-lock-keywords-1 ())
+
+(defvar sgml-face-tag-alist ()
+  "Alist of face and tag name for facemenu.")
+
+(defvar sgml-tag-face-alist ()
+  "Tag names and face or list of faces to fontify with when invisible.
+When `font-lock-maximum-decoration' is 1 this is always used for fontifying.
+When more these are fontified together with `sgml-font-lock-keywords'.")
+
+
+(defvar sgml-display-text ()
+  "Tag names as lowercase symbols, and display string when invisible.")
+
+;; internal
+(defvar sgml-tags-invisible nil)
+
+
+(defvar sgml-tag-alist
+  '(("!attlist")
+    ("!doctype")
+    ("!element")
+    ("!entity"))
+  "*Alist of tag names for completing read and insertion rules.
+This alist is made up as
+
+  ((\"tag\" . TAGRULE)
+   ...)
 
-(if sgml-mode-markup-syntax-table
-    ()
-  (setq sgml-mode-markup-syntax-table (make-syntax-table))
-  (modify-syntax-entry ?< "(>" sgml-mode-markup-syntax-table)
-  (modify-syntax-entry ?> ")<" sgml-mode-markup-syntax-table)
-  (modify-syntax-entry ?- "_ 1234" sgml-mode-markup-syntax-table)
-  (modify-syntax-entry ?\' "\"" sgml-mode-markup-syntax-table))
+TAGRULE is a list of optionally `t' (no endtag) or `\\n' (separate endtag by
+newlines) or a skeleton with `nil', `t' or `\\n' in place of the interactor
+followed by an ATTRIBUTERULE (for an always present attribute) or an
+attribute alist.
+
+The attribute alist is made up as
+
+  ((\"attribute\" . ATTRIBUTERULE)
+   ...)
+
+ATTRIBUTERULE is a list of optionally `t' (no value when no input) followed by
+an optional alist of possible values.")
 
-(defconst sgml-angle-distance 4000
-  "*If non-nil, is the maximum distance to search for matching <.")
+(defvar sgml-tag-help
+  '(("!" . "Empty declaration for comment")
+    ("![" . "Embed declarations with parser directive")
+    ("!attlist" . "Tag attributes declaration")
+    ("!doctype" . "Document type (DTD) declaration")
+    ("!element" . "Tag declaration")
+    ("!entity" . "Entity (macro) declaration"))
+  "*Alist of tag name and short description.")
+
+
+;; put read-only last to enable setting this even when read-only enabled
+(or (get 'sgml-tag 'invisible)
+    (setplist 'sgml-tag
+	      (append '(invisible t
+			rear-nonsticky t
+			point-entered sgml-point-entered
+			read-only t)
+		      (symbol-plist 'sgml-tag))))
+
+
+(defvar sgml-auto-attributes t
+  "*When non-`nil' SPC at top level of tag prompts for attributes.")
+
+
 
-(defun sgml-close-angle (arg)
-  "Insert > and display matching <."
-  (interactive "p")
-  (insert-char ?> arg)
-  (if (> arg 0)
-      (let ((oldpos (point))
-	    (blinkpos))
-	(save-excursion
-	  (save-restriction
-	    (if sgml-angle-distance
-		(narrow-to-region (max (point-min)
-				       (- (point) sgml-angle-distance))
-				  oldpos))
-	    ;; See if it's the end of a marked section.
-	    (and (> (- (point) (point-min)) 3)
-		 (eq (char-after (- (point) 2)) ?\])
-		 (eq (char-after (- (point) 3)) ?\])
-		 (re-search-backward "<!\\[\\(-?[A-Za-z0-9. \t\n&;]\\|\
---\\([^-]\\|-[^-]\\)*--\\)*\\["
-				     (point-min)
-				     t)
-		 (let ((msspos (point)))
-		   (if (and (search-forward "]]>" oldpos t)
-			    (eq (point) oldpos))
-		       (setq blinkpos msspos))))
-	    ;; This handles cases where the > ends one of the following:
-	    ;; markup declaration starting with <! (possibly including a
-	    ;; declaration subset); start tag; end tag; SGML declaration.
-	    (if blinkpos
-		()
-	      (goto-char oldpos)
-	      (condition-case ()
-		  (let ((oldtable (syntax-table))
-			(parse-sexp-ignore-comments t))
-		    (unwind-protect
-			(progn
-			  (set-syntax-table sgml-mode-markup-syntax-table)
-			  (setq blinkpos (scan-sexps oldpos -1)))
-		      (set-syntax-table oldtable)))
-		(error nil))
-	      (and blinkpos
-		   (goto-char blinkpos)
-		   (or
-		    ;; Check that it's a valid delimiter in context.
-		    (not (looking-at
-			  "<\\(\\?\\|/?[A-Za-z>]\\|!\\([[A-Za-z]\\|--\\)\\)"))
-		    ;; Check that it's not a net-enabling start tag
-		    ;; nor an unclosed start-tag.
-		    (looking-at (concat sgml-start-tag-regex "[/<]"))
-		    ;; Nor an unclosed end-tag.
-		    (looking-at "</[A-Za-z][-.A-Za-z0-9]*[ \t]*<"))
-		   (setq blinkpos nil)))
-	    (if blinkpos
-		()
-	      ;; See if it's the end of a processing instruction.
-	      (goto-char oldpos)
-	      (if (search-backward "<?" (point-min) t)
-		  (let ((pipos (point)))
-		    (if (and (search-forward ">" oldpos t)
-			     (eq (point) oldpos))
-			(setq blinkpos pipos))))))
-	  (if blinkpos
-	      (progn
-		(goto-char blinkpos)
-		(if (pos-visible-in-window-p)
-		    (sit-for 1)
-		  (message "Matches %s"
-			   (buffer-substring blinkpos
-					     (progn (end-of-line)
-						    (point)))))))))))
+(defun sgml-mode-common (sgml-tag-face-alist sgml-display-text)
+  "Common code for setting up `sgml-mode' and derived modes.
+SGML-TAG-FACE-ALIST is used for calculating `sgml-font-lock-keywords-1'.
+SGML-DISPLAY-TEXT sets up alternate text for when tags are invisible (see
+varables of same name)."
+  (kill-all-local-variables)
+  (setq local-abbrev-table text-mode-abbrev-table)
+  (set-syntax-table sgml-mode-syntax-table)
+  (make-local-variable 'indent-line-function)
+  (make-local-variable 'paragraph-start)
+  (make-local-variable 'paragraph-separate)
+  (make-local-variable 'sgml-saved-validate-command)
+  (make-local-variable 'comment-start)
+  (make-local-variable 'comment-end)
+  (make-local-variable 'comment-indent-function)
+  (make-local-variable 'comment-start-skip)
+  (make-local-variable 'comment-indent-function)
+  (make-local-variable 'sgml-tags-invisible)
+  (make-local-variable 'skeleton-transformation)
+  (make-local-variable 'skeleton-further-elements)
+  (make-local-variable 'skeleton-end-hook)
+  (make-local-variable 'font-lock-defaults)
+  (make-local-variable 'sgml-font-lock-keywords-1)
+  (make-local-variable 'facemenu-add-face-function)
+  (make-local-variable 'facemenu-end-add-face)
+  ;;(make-local-variable 'facemenu-remove-face-function)
+  (and sgml-tag-face-alist
+       (not (assq 1 sgml-tag-face-alist))
+       (nconc sgml-tag-face-alist
+	      `((1 (,(concat "<\\("
+			     (mapconcat 'car sgml-tag-face-alist "\\|")
+			     "\\)\\([ \t].+\\)?>\\(.+\\)</\\1>")
+		    3 (cdr (assoc (match-string 1) ',sgml-tag-face-alist)))))))
+  (setq indent-line-function 'indent-relative-maybe
+	;; A start or end tag by itself on a line separates a paragraph.
+	;; This is desirable because SGML discards a newline that appears
+	;; immediately after a start tag or immediately before an end tag.
+	paragraph-start "^[ \t\n]\\|\
+\\(</?\\([A-Za-z]\\([-.A-Za-z0-9= \t\n]\\|\"[^\"]*\"\\|'[^']*'\\)*\\)?>$\\)"
+	paragraph-separate "^[ \t\n]*$\\|\
+^</?\\([A-Za-z]\\([-.A-Za-z0-9= \t\n]\\|\"[^\"]*\"\\|'[^']*'\\)*\\)?>$"
+	comment-start "<!-- "
+	comment-end " -->"
+	comment-indent-function 'sgml-comment-indent
+	;; This will allow existing comments within declarations to be
+	;; recognized.
+	comment-start-skip "--[ \t]*"
+	skeleton-transformation 'identity
+	skeleton-further-elements '((completion-ignore-case t))
+	skeleton-end-hook (lambda ()
+			    (or (eolp)
+				(not (or (eq v2 '\n)
+					 (eq (car-safe v2) '\n)))
+				(newline-and-indent)))
+	sgml-font-lock-keywords-1 (cdr (assq 1 sgml-tag-face-alist))
+	font-lock-defaults '((sgml-font-lock-keywords
+			      sgml-font-lock-keywords-1)
+			     nil
+			     t)
+	facemenu-add-face-function
+	  (lambda (face end)
+	    (if (setq face (cdr (assq face sgml-face-tag-alist)))
+		(progn
+		  (setq facemenu-end-add-face (concat "</" face ">"))
+		  (concat "<" face ">"))
+	      (error "Face not configured for %s mode." mode-name))))
+  (while sgml-display-text
+    (put (car (car sgml-display-text)) 'before-string
+	 (cdr (car sgml-display-text)))
+    (setq sgml-display-text (cdr sgml-display-text)))
+  (run-hooks 'text-mode-hook 'sgml-mode-hook))
 
-;;; I doubt that null end tags are used much for large elements,
-;;; so use a small distance here.
-(defconst sgml-slash-distance 1000
-  "*If non-nil, is the maximum distance to search for matching /.")
+
+;;;###autoload
+(defun sgml-mode (&optional function)
+  "Major mode for editing SGML documents.
+Makes > match <.  Makes / blink matching /.
+
+Do \\[describe-variable] sgml- SPC to see available variables.
+
+Use \\[sgml-validate] to validate your document with an SGML parser.
+\\{sgml-mode-map}"
+  (interactive)
+  (sgml-mode-common sgml-tag-face-alist sgml-display-text)
+  (use-local-map sgml-mode-map)
+  (setq mode-name "SGML"
+	major-mode 'sgml-mode))
+
+
+
+(defun sgml-comment-indent ()
+  (if (and (looking-at "--")
+	   (not (and (eq (preceding-char) ?!)
+		     (eq (char-after (- (point) 2)) ?<))))
+      (progn
+	(skip-chars-backward " \t")
+	(max comment-column (1+ (current-column))))
+    0))
+
+
 
 (defun sgml-slash (arg)
   "Insert / and display any previous matching /.
@@ -237,6 +400,296 @@
 					       (point))
 					     (1+ blinkpos))))))))))
 
+
+(defun sgml-name-char (&optional char)
+  "Insert a symbolic character name according to `sgml-char-names'.
+8 bit chars may be inserted with the meta key as in M-SPC for no break space,
+or M-- for a soft hyphen."
+  (interactive "*")
+  (insert ?&)
+  (or char
+      (setq char (read-quoted-char)))
+  (delete-backward-char 1)
+  (insert char)
+  (undo-boundary)
+  (delete-backward-char 1)
+  (insert ?&
+	  (or (aref sgml-char-names char)
+	      (format "#%d" char))
+	  ?\;))
+
+
+(defun sgml-name-self ()
+  "Insert a symbolic character name according to `sgml-char-names'."
+  (interactive "*")
+  (sgml-name-char last-command-char))
+
+
+(defun sgml-maybe-name-self ()
+  "Insert a symbolic character name according to `sgml-char-names'."
+  (interactive "*")
+  (if sgml-name-8bit-mode
+      (sgml-name-char last-command-char)
+    (self-insert-command 1)))
+
+
+(defun sgml-name-8bit-mode ()
+  "Toggle insertion of 8 bit characters."
+  (interactive)
+  (setq sgml-name-8bit-mode (not sgml-name-8bit-mode)))
+
+
+
+(define-skeleton sgml-tag
+  "Insert a tag you are prompted for, optionally with attributes.
+Completion and configuration is according to `sgml-tag-alist'.
+If you like tags and attributes in uppercase set `skeleton-transformation'
+to `upcase'."
+  (funcall skeleton-transformation
+	   (completing-read "Tag: " sgml-tag-alist))
+  ?< (setq v1 (eval str)) |
+  (("") -1 '(undo-boundary) "&lt;") |
+  (("") '(setq v2 (sgml-attributes v1 t)) ?>
+   (if (or (eq v2 t)
+	   (string-match "^[/!?]" v1))
+       ()
+     (if (symbolp v2)
+	 '(("") v2 _ v2 "</" v1 ?>)
+       (if (eq (car v2) t)
+	   (cons '("") (cdr v2))
+	 (append '(("") (car v2))
+		 (cdr v2)
+		 '(resume: (car v2) _ "</" v1 ?>)))))))
+
+(autoload 'skeleton-read "skeleton")
+
+(defun sgml-attributes (alist &optional quiet)
+  "When at toplevel of a tag, interactively insert attributes."
+  (interactive (list (save-excursion (sgml-beginning-of-tag t))))
+  (or (stringp alist) (error "Wrong context for adding attribute"))
+  (if alist
+      (let ((completion-ignore-case t)
+	    car attribute i)
+	(setq alist (cdr (assoc (downcase alist) sgml-tag-alist)))
+	(if (or (symbolp (car alist))
+		(symbolp (car (car alist))))
+	    (setq car (car alist)
+		  alist (cdr alist)))
+	(or quiet
+	    (message "No attributes configured."))
+	(if (stringp (car alist))
+	    (progn
+	      (insert (if (eq (preceding-char) ? ) "" ? ) (car alist))
+	      (sgml-value alist))
+	  (setq i (length alist))
+	  (while (> i 0)
+	    (insert ? )
+	    (insert (funcall skeleton-transformation
+			     (setq attribute
+				   (skeleton-read '(completing-read
+						    "[Attribute]: "
+						    alist)))))
+	    (if (string= "" attribute)
+		(setq i 0)
+	      (sgml-value (assoc attribute alist))
+	      (setq i (1- i))))
+	  (if (eq (preceding-char) ? )
+	      (delete-backward-char 1)))
+	car)))
+
+(defun sgml-auto-attributes (arg)
+  "Self insert, except, when at top level of tag, prompt for attributes.
+With prefix ARG, or if `sgml-auto-attributes' is `nil' only self insert."
+  (interactive "*P")
+  (let ((point (point))
+	tag)
+    (if (or arg
+	    (not sgml-auto-attributes)
+	    (not sgml-tag-alist)	; no message when nothing configured
+	    (symbolp (setq tag (save-excursion (sgml-beginning-of-tag t))))
+	    (eq (aref tag 0) ?/))
+	(self-insert-command (prefix-numeric-value arg))
+      (sgml-attributes tag)
+      (setq last-command-char ? )
+      (or (> (point) point)
+	  (self-insert-command 1)))))
+
+
+(defun sgml-tag-help (&optional tag)
+  "Display description of optional TAG or tag at point."
+  (interactive)
+  (or tag
+      (save-excursion
+	(if (eq (following-char) ?<)
+	    (forward-char))
+	(setq tag (sgml-beginning-of-tag))))
+  (or (stringp tag)
+      (error "No tag selected"))
+  (setq tag (downcase tag))
+  (message (or (cdr (assoc tag sgml-tag-help))
+	       (and (eq (aref tag 0) ?/)
+		    (cdr (assoc (substring tag 1) sgml-tag-help)))
+	       "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-skip-tag-backward (arg)
+  "Skip to beginning of tag or matching opening tag if present.
+With prefix ARG, repeat that many times."
+  (interactive "p")
+  (while (>= arg 1)
+    (search-backward "<" nil t)
+    (if (looking-at "</\\([^ \n\t>]+\\)")
+	;; end tag, skip any nested pairs
+	(let ((case-fold-search t)
+	      (re (concat "</?" (regexp-quote (match-string 1)))))
+	  (while (and (re-search-backward re nil t)
+		      (eq (char-after (1+ (point))) ?/))
+	    (forward-char 1)
+	    (sgml-skip-tag-backward 1))))
+    (setq arg (1- arg))))
+
+(defun sgml-skip-tag-forward (arg &optional return)
+  "Skip to end of tag or matching closing tag if present.
+With prefix ARG, repeat that many times.
+Return t iff after a closing tag."
+  (interactive "p")
+  (setq return t)
+  (while (>= arg 1)
+    (skip-chars-forward "^<>")
+    (if (eq (following-char) ?>)
+	(up-list -1))
+    (if (looking-at "<\\([^/ \n\t>]+\\)")
+	;; start tag, skip any nested same pairs _and_ closing tag
+	(let ((case-fold-search t)
+	      (re (concat "</?" (regexp-quote (match-string 1))))
+	      point close)
+	  (forward-list 1)
+	  (setq point (point))
+	  (while (and (re-search-forward re nil t)
+		      (not (setq close
+				 (eq (char-after (1+ (match-beginning 0))) ?/)))
+		      (not (up-list -1))
+		      (sgml-skip-tag-forward 1))
+	    (setq close nil))
+	  (if close
+	      (up-list 1)
+	    (goto-char point)
+	    (setq return)))
+      (forward-list 1))
+    (setq arg (1- arg)))
+  return)
+
+(defun sgml-delete-tag (arg)
+  "Delete tag on or after cursor, and matching closing or opening tag.
+With prefix ARG, repeat that many times."
+  (interactive "p")
+  (while (>= arg 1)
+    (save-excursion
+      (let* (close open)
+	(if (looking-at "[ \t]*<")
+	    ;; just before tag
+	    (if (eq (char-after (match-end 0)) ?/)
+		;; closing tag
+		(progn
+		  (setq close (point))
+		  (goto-char (match-end 0))))
+	  ;; on tag?
+	  (or (save-excursion (setq close (sgml-beginning-of-tag)
+				    close (and (stringp close)
+					       (eq (aref close 0) ?/)
+					       (point))))
+	      ;; not on closing tag
+	      (let ((point (point)))
+		(sgml-skip-tag-backward 1)
+		(if (or (not (eq (following-char) ?<))
+			(save-excursion
+			  (forward-list 1)
+			  (<= (point) point)))
+		    (error "Not on or before tag")))))
+	(if close
+	    (progn
+	      (sgml-skip-tag-backward 1)
+	      (setq open (point))
+	      (goto-char close)
+	      (kill-sexp 1))
+	  (setq open (point))
+	  (sgml-skip-tag-forward 1)
+	  (backward-list)
+	  (forward-char)
+	  (if (eq (aref (sgml-beginning-of-tag) 0) ?/)
+	      (kill-sexp 1)))
+	(goto-char open)
+	(kill-sexp 1)))
+    (setq arg (1- arg))))
+
+
+
+(defun sgml-tags-invisible (arg)
+  "Toggle visibility of existing tags."
+  (interactive "P")
+  (let ((modified (buffer-modified-p))
+	(inhibit-read-only t)
+	(point (point-min))
+	symbol)
+    (save-excursion
+      (goto-char point)
+      (if (setq sgml-tags-invisible
+		(if arg
+		    (>= (prefix-numeric-value arg) 0)
+		  (not sgml-tags-invisible)))
+	  (while (re-search-forward "<\\([!/?A-Za-z][-A-Za-z0-9]*\\)"
+				    nil t)
+	    (setq symbol (intern-soft (downcase (match-string 1))))
+	    (goto-char (match-beginning 0))
+	    (and (get symbol 'before-string)
+		 (not (overlays-at (point)))
+		 (overlay-put (make-overlay (point)
+					    (match-beginning 1))
+			      'category symbol))
+	    (put-text-property (setq point (point)) (forward-list)
+			       'intangible (point))			
+	    (put-text-property point (point)
+			       'category 'sgml-tag))
+	(while (< (setq point (next-overlay-change point)) (point-max))
+	  (delete-overlay (car (overlays-at point))))
+	(remove-text-properties (point-min) (point-max)
+				'(category sgml-tag intangible t))))
+    (set-buffer-modified-p modified)
+    (run-hooks 'sgml-tags-invisible-hook)
+    (message "")))
+
+(defun sgml-point-entered (x y)
+  ;; Show preceding or following hidden tag, depending of cursor direction.
+  (let ((inhibit-point-motion-hooks t))
+    (save-excursion
+      (message "Invisible tag: %s"
+	       (buffer-substring
+		(point)
+		(if (or (and (> x y)
+			     (not (eq (following-char) ?<)))
+			(and (< x y)
+			     (eq (preceding-char) ?>)))
+		    (backward-list)
+		  (forward-list)))))))
+
+
+(autoload 'compile-internal "compile")
+
 (defun sgml-validate (command)
   "Validate an SGML document.
 Runs COMMAND, a shell command, in a separate process asynchronously
@@ -254,4 +707,497 @@
   (setq sgml-saved-validate-command command)
   (compile-internal command "No more errors"))
 
+
+(defun sgml-beginning-of-tag (&optional top-level)
+  "Skip to beginning of tag and return its name.
+Else `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 "<[!?/]?[[A-Za-z][A-Za-z0-9]*")
+	  (buffer-substring-no-properties
+	   (1+ (point))
+	   (match-end 0))
+	t)))
+
+(defun sgml-value (alist)
+  (setq alist (cdr alist))
+  (if (stringp (car alist))
+      (insert "=\"" (car alist) ?\")
+    (if (eq (car alist) t)
+	(if (cdr alist)
+	    (progn
+	      (insert "=\"")
+	      (setq alist (skeleton-read '(completing-read
+					   "[Value]: " (cdr alist))))
+	      (if (string< "" alist)
+		  (insert (funcall skeleton-transformation alist) ?\")
+		(delete-backward-char 2))))
+      (insert "=\"")
+      (if alist
+	  (insert (funcall skeleton-transformation
+			   (skeleton-read '(completing-read "Value: " alist)))))
+      (insert ?\"))))
+
+(provide 'sgml-mode)
+
+;;;###autoload
+(or (rassq 'html-mode auto-mode-alist)
+    (setq auto-mode-alist `(("\\.s?html?\\'" . html-mode)
+			    ,@auto-mode-alist)))
+
+(defvar html-quick-keys t
+  "Use C-c <x> combinations for quick insertion of frequent tags when non-nil.
+This takes effect when first loading the library.")
+
+(defvar html-mode-map
+  (let ((map (nconc (make-sparse-keymap) sgml-mode-map))
+	(menu-map (make-sparse-keymap "HTML")))
+    (if html-quick-keys
+	(progn
+	  (define-key map "\C-c1" 'html-headline)
+	  (define-key map "\C-c2" 'html-headline)
+	  (define-key map "\C-c3" 'html-headline)
+	  (define-key map "\C-c4" 'html-headline)
+	  (define-key map "\C-c5" 'html-headline)
+	  (define-key map "\C-c6" 'html-headline)
+	  (define-key map "\C-c-" 'html-horizontal-rule)
+	  (define-key map "\C-c\r" 'html-paragraph)
+	  (define-key map "\C-c\n" 'html-line)
+	  (define-key map "\C-co" 'html-list)
+	  (define-key map "\C-cu" 'html-list)
+	  (define-key map "\C-cr" 'html-radio-buttons)
+	  (define-key map "\C-cl" 'html-list-item)
+	  (define-key map "\C-ch" 'html-href-anchor)
+	  (define-key map "\C-cn" 'html-name-anchor)
+	  (define-key map "\C-ci" 'html-image)))
+    (define-key map "\C-c\C-s" 'html-autoview-mode)
+    (define-key map "\C-c\C-v" 'browse-url-of-buffer)
+    (define-key map [menu-bar html] (cons "HTML" menu-map))
+    (define-key menu-map [html-autoview-mode]
+      '("Toggle Autoviewing" . html-autoview-mode))
+    (define-key menu-map [browse-url-of-buffer]
+      '("View Buffer Contents" . browse-url-of-buffer))
+    (define-key menu-map [nil] '("--"))
+    ;;(define-key menu-map "6" '("Heading 6" . html-headline))
+    ;;(define-key menu-map "5" '("Heading 5" . html-headline))
+    ;;(define-key menu-map "4" '("Heading 4" . html-headline))
+    (define-key menu-map "3" '("Heading 3" . html-headline))
+    (define-key menu-map "2" '("Heading 2" . html-headline))
+    (define-key menu-map "1" '("Heading 1" . html-headline))
+    (define-key menu-map "l" '("Radio Buttons" . html-radio-buttons))
+    (define-key menu-map "l" '("List Item" . html-list-item))
+    (define-key menu-map "u" '("Unordered List" . html-list))
+    (define-key menu-map "o" '("Ordered List" . html-list))
+    (define-key menu-map "-" '("Horizontal rule" . html-horizontal-rule))
+    (define-key menu-map "\n" '("Line Break" . html-line))
+    (define-key menu-map "\r" '("Paragraph" . html-paragraph))
+    (define-key menu-map "i" '("Image" . html-image))
+    (define-key menu-map "h" '("Href Anchor" . html-href-anchor))
+    (define-key menu-map "n" '("Name Anchor" . html-name-anchor))
+    map)
+  "Keymap for commands for use in HTML mode.")
+
+
+(defvar html-face-tag-alist
+  '((bold . "b")
+    (italic . "i")
+    (underline . "u")
+    (modeline . "rev"))
+  "Value of `sgml-face-tag-alist' for HTML mode.")
+
+(defvar html-tag-face-alist
+  '(("b" . bold)
+    ("big" . bold)
+    ("blink" . highlight)
+    ("cite" . italic)
+    ("em" . italic)
+    ("h1" bold underline)
+    ("h2" bold-italic underline)
+    ("h3" italic underline)
+    ("h4" . underline)
+    ("h5" . underline)
+    ("h6" . underline)
+    ("i" . italic)
+    ("rev"  . modeline)
+    ("s" . underline)
+    ("small" . default)
+    ("strong" . bold)
+    ("title" bold underline)
+    ("tt" . default)
+    ("u" . underline)
+    ("var" . italic))
+  "Value of `sgml-tag-face-alist' for HTML mode.")
+
+
+(defvar html-display-text
+  '((img . "[/]")
+    (hr . "----------")
+    (li . "o "))
+  "Value of `sgml-display-text' for HTML mode.")
+
+
+; should code exactly HTML 3 here when that is finished
+(defvar html-tag-alist
+  (let* ((1-9 '(("8") ("9")
+		("1") ("2") ("3") ("4") ("5") ("6") ("7")))
+	 (align '(("align" ("left") ("center") ("right"))))
+	 (valign '(("top") ("middle") ("bottom") ("baseline")))
+	 (rel '(("next") ("previous") ("parent") ("subdocument") ("made")))
+	 (href '("href" ("ftp:") ("file:") ("finger:") ("gopher:") ("http:")
+		 ("mailto:") ("news:") ("rlogin:") ("telnet:") ("tn3270:")
+		 ("wais:")))
+	 (name '("name"))
+	 (link `(,href
+		 ("rel" ,@rel)
+		 ("rev" ,@rel)
+		 ("title")))
+	 (list '((nil \n
+		      ( "List item: "
+			"<li>" str \n))
+		 ("type" ("A") ("a") ("I") ("i") ("1"))))
+	 (cell `(t
+		 ,align
+		 ("valign" ,@valign)
+		 ("colspan" ,@1-9)
+		 ("rowspan" ,@1-9)
+		 ("nowrap" t))))
+    ;; put ,-expressions first, else byte-compile chokes (as of V19.29)
+    ;; and like this it's more efficient anyway
+    `(("a" ,name ,@link)
+      ("base" t ,@href)
+      ("dir" ,@list)
+      ("font" "size" ("-1") ("+1") ("-2") ("+2") ,@(cdr (cdr 1-9)))
+      ("form" \n ("action" ,@(cdr href)) ("method" ("get") ("post")))
+      ("h1" ,@align)
+      ("h2" ,@align)
+      ("h3" ,@align)
+      ("h4" ,@align)
+      ("h5" ,@align)
+      ("h6" ,@align)
+      ("hr" t ("size" ,@1-9) ("width") ("noshade" t) ,@align)
+      ("img" t ("align" ,@valign ("texttop") ("absmiddle") ("absbottom"))
+       ("src") ("alt") ("width" "1") ("height" "1")
+       ("border" "1") ("vspace" "1") ("hspace" "1") ("ismap" t))
+      ("input" t ("size" ,@1-9) ("maxlength" ,@1-9) ("checked" t) ,name
+       ("type" ("text") ("password") ("checkbox") ("radio") ("sbmit") ("reset"))
+       ("value"))
+      ("link" t ,@link)
+      ("menu" ,@list)
+      ("ol" ,@list)
+      ("p" t ,@align)
+      ("select" (nil \n
+		     ("Text: "
+		      "<option>" str \n))
+       ,name ("size" ,@1-9) ("multiple" t))
+      ("table" (nil \n
+		    ((completing-read "Cell kind: " '(("td") ("th"))
+				      nil t "t")
+		     "<tr><" str ?> _ \n))
+       ("border" t ,@1-9) ("width" "10") ("cellpadding"))
+      ("td" ,@cell)
+      ("textarea" ,name ("rows" ,@1-9) ("cols" ,@1-9))
+      ("th" ,@cell)
+      ("ul" ,@list)
+
+      ,@sgml-tag-alist
+
+      ("abbrev")
+      ("acronym")
+      ("address")
+      ("array" (nil \n
+		    ("Item: " "<item>" str \n))
+       "align")
+      ("au")
+      ("b")
+      ("big")
+      ("blink")
+      ("blockquote" \n)
+      ("body" \n ("background" ".gif") ("bgcolor" "#") ("text" "#")
+       ("link" "#") ("alink" "#") ("vlink" "#"))
+      ("box" (nil _ "<over>" _))
+      ("br" t ("clear" ("left") ("right")))
+      ("caption" ("valign" ("top") ("bottom")))
+      ("center" \n)
+      ("cite")
+      ("code" \n)
+      ("dd" t)
+      ("del")
+      ("dfn")
+      ("dl" (nil \n
+		 ( "Term: "
+		   "<dt>" str "<dd>" _ \n)))
+      ("dt" (t _ "<dd>"))
+      ("em")
+      ("fn" "id" "fn")
+      ("head" \n)
+      ("html" (\n
+	       "<head>\n"
+	       "<title>" (setq str (read-input "Title: ")) "</title>\n"
+	       "<body>\n<h1>" str "</h1>\n" _
+	       "\n<address>\n<a href=\"mailto:"
+	       (user-login-name) ?@ (system-name)
+	       "\">" (user-full-name) "</a>\n</address>"))
+      ("i")
+      ("ins")
+      ("isindex" t ("action") ("prompt"))
+      ("kbd")
+      ("lang")
+      ("li" t)
+      ("math" \n)
+      ("nobr")
+      ("option" t ("value") ("label") ("selected" t))
+      ("over" t)
+      ("person")
+      ("pre" \n)
+      ("q")
+      ("rev")
+      ("s")
+      ("samp")
+      ("small")
+      ("strong")
+      ("sub")
+      ("sup")
+      ("title")
+      ("tr" t)
+      ("tt")
+      ("u")
+      ("var")
+      ("wbr" t)))
+  "*Value of `sgml-tag-alist' for HTML mode.")
+
+(defvar html-tag-help
+  `(,@sgml-tag-help
+    ("a" . "Anchor of point or link elsewhere")
+    ("abbrev" . "?")
+    ("acronym" . "?")
+    ("address" . "Formatted mail address")
+    ("array" . "Math array")
+    ("au" . "?")
+    ("b" . "Bold face")
+    ("base" . "Base address for URLs")
+    ("big" . "Font size")
+    ("blink" . "Blinking text")
+    ("blockquote" . "Indented quotation")
+    ("body" . "Document body")
+    ("box" . "Math fraction")
+    ("br" . "Line break")
+    ("caption" . "Table caption")
+    ("center" . "Centered text")
+    ("changed" . "Change bars")
+    ("cite" . "Citation of a document")
+    ("code" . "Formatted source code")
+    ("dd" . "Definition of term")
+    ("del" . "?")
+    ("dfn" . "?")
+    ("dir" . "Directory list (obsolete)")
+    ("dl" . "Definition list")
+    ("dt" . "Term to be definined")
+    ("em" . "Emphasised") 
+    ("embed" . "Embedded data in foreign format")
+    ("fig" . "Figure")
+    ("figa" . "Figure anchor")
+    ("figd" . "Figure description")
+    ("figt" . "Figure text")
+    ("fn" . "?")
+    ("font" . "Font size")
+    ("form" . "Form with input fields")
+    ("group" . "Document grouping")
+    ("h1" . "Most important section headline")
+    ("h2" . "Important section headline")
+    ("h3" . "Section headline")
+    ("h4" . "Minor section headline")
+    ("h5" . "Unimportant section headline")
+    ("h6" . "Least important section headline")
+    ("head" . "Document header")
+    ("hr" . "Horizontal rule")
+    ("html" . "HTML Document")
+    ("i" . "Italic face")
+    ("img" . "Graphic image")
+    ("input" . "Form input field")
+    ("ins" . "?")
+    ("isindex" . "Input field for index search")
+    ("kbd" . "Keybard example face")
+    ("lang" . "Natural language")
+    ("li" . "List item")
+    ("link" . "Link relationship")
+    ("math" . "Math formula")
+    ("menu" . "Menu list (obsolete)")
+    ("mh" . "Form mail header")
+    ("nextid" . "Allocate new id")
+    ("nobr" . "Text without line break")
+    ("ol" . "Ordered list")
+    ("option" . "Selection list item")
+    ("over" . "Math fraction rule")
+    ("p" . "Paragraph start")
+    ("panel" . "Floating panel")
+    ("person" . "?")
+    ("pre" . "Preformatted fixed width text")
+    ("q" . "?")
+    ("rev" . "Reverse video")
+    ("s" . "?")
+    ("samp" . "Sample text")
+    ("select" . "Selection list")
+    ("small" . "Font size")
+    ("sp" . "Nobreak space")
+    ("strong" . "Standout text")
+    ("sub" . "Subscript")
+    ("sup" . "Superscript")
+    ("table" . "Table with rows and columns")
+    ("tb" . "Table vertical break")
+    ("td" . "Table data cell")
+    ("textarea" . "Form multiline edit area")
+    ("th" . "Table header cell")
+    ("title" . "Document title")
+    ("tr" . "Table row separator")
+    ("tt" . "Typewriter face")
+    ("u" . "Underlined text")
+    ("ul" . "Unordered list")
+    ("var" . "Math variable face")
+    ("wbr" . "Enable <br> within <nobr>"))
+"*Value of `sgml-tag-help' for HTML mode.")
+
+
+
+;;;###autoload
+(defun html-mode ()
+  "Major mode based on SGML mode for editing HTML documents.
+This allows inserting skeleton costructs used in hypertext documents via
+the command `<' with completion.  See below for an introduction to HTML.
+Use \\[browse-url-of-buffer] to see how this comes out.
+See also `sgml-mode' on which this is based.
+
+Do \\[describe-variable] html- SPC to see available variables.
+
+To write fairly well formatted pages you only need to know few things.  Most
+browsers have a function to read the source code of the page being seen, so
+you can imitate various tricks.  Here's a very short HTML primer which you
+can also view with a browser to see what happens:
+
+<title>A Title Describing Contents</title> should be on every page.  Pages can
+have <h1>Very Major Headlines</h1> through <h6>Very Minor Headlines</h6>
+<hr> Parts can be separated with horizontal rules.
+
+<p>Paragraphs only need an opening tag.  Line breaks and multiple spaces are
+ignored unless the text is <pre>preformatted.</pre>  Text can be marked as
+<b>bold</b>, <i>italic</i> or <u>underlined</u> using the normal  M-g  or
+Edit/Text Properties/Face commands.
+
+Pages can have <a name=\"SOMENAME\">named points</a> and can link other points
+to them with <a href=\"#SOMENAME\">see also somename</a>.  In the same way <a
+href=\"URL\">see also URL</a> where URL is a filename relative to current
+directory or something like http://www.cs.indiana.edu/elisp/w3/docs.html.
+
+Images in many formats can be inlined with <img src=\"URL\">.
+
+If you mainly create your own documents, `sgml-specials' might be interesting.
+But note that some HTML 2 browsers can't handle &apos;.  To work around that
+do:
+
+\(eval-after-load \"sgml-mode\" '(aset sgml-char-names ?' nil))
+\\{html-mode-map}"
+  (interactive)
+  (sgml-mode-common html-tag-face-alist html-display-text)
+  (use-local-map html-mode-map)
+  (make-local-variable 'sgml-tag-alist)
+  (make-local-variable 'sgml-face-tag-alist)
+  (make-local-variable 'sgml-tag-help)
+  (make-local-variable 'outline-regexp)
+  (make-local-variable 'outline-heading-end-regexp)
+  (make-local-variable 'outline-level)
+  (setq mode-name "HTML"
+        major-mode 'html-mode
+	sgml-tag-alist html-tag-alist
+	sgml-face-tag-alist html-face-tag-alist
+	sgml-tag-help html-tag-help
+	outline-regexp "^.*<[Hh][1-6]\\>"
+	outline-heading-end-regexp "</[Hh][1-6]>"
+	outline-level (lambda ()
+			(char-after (1- (match-end 0)))))
+  (run-hooks 'html-mode-hook))
+
+
+(define-skeleton html-href-anchor
+  "HTML anchor tag with href attribute."
+  nil
+  "<a href=\"http:" _ "\"></a>")
+
+(define-skeleton html-name-anchor
+  "HTML anchor tag with name attribute."
+  nil
+  "<a name=\"" _ "\"></a>")
+
+(define-skeleton html-headline
+  "HTML headline tags."
+  last-command-char
+  "<h" str ?> _ "</h" str ?>)
+
+(define-skeleton html-horizontal-rule
+  "HTML horizontal rule tag."
+  nil
+  "<hr>" \n)
+
+(define-skeleton html-image
+  "HTML image tag."
+  nil
+  "<img src=\"http:" _ "\">")
+
+(define-skeleton html-line
+  "HTML line break tag."
+  nil
+  "<br>" \n)
+
+(define-skeleton html-list
+  "HTML unordered/ordered list tags."
+  last-command-char
+  ?< str "l>" \n
+  "<li>" _ \n
+  "</" str "l>")
+
+(define-skeleton html-list-item
+  "HTML list item tag."
+  nil
+  (if (bolp) nil '\n)
+  "<li>")
+
+(define-skeleton html-paragraph
+  "HTML paragraph tag."
+  nil
+  (if (bolp) nil ?\n)
+  \n "<p>")
+
+(define-skeleton html-radio-buttons
+  "Group of connected radio button inputs."
+  nil
+  '(setq v1 (eval str))			; allow passing name as argument
+  ("Value & Text: "
+   "<input type=\"radio\" name=\""
+   (or v1 (setq v1 (skeleton-read "Name: ")))
+   "\" value=\"" str ?\"
+   (if v2 "" " checked") ?> str
+   (or v2 (setq v2 (if (y-or-n-p "Newline? ") "<br>" ""))) \n))
+
+
+(defun html-autoview-mode (&optional arg)
+  "Toggle automatic viewing via `html-viewer' upon saving buffer.
+With positive prefix ARG always turns viewing on, with negative ARG always off.
+Can be used as a value for `html-mode-hook'."
+  (interactive "P")
+  (if (setq arg (if arg
+		    (< (prefix-numeric-value arg) 0)
+		  (and (boundp 'after-save-hook)
+		       (memq 'browse-url-of-buffer after-save-hook))))
+      (setq after-save-hook (delq 'browse-url-of-buffer after-save-hook))
+    (make-local-hook 'after-save-hook)
+    (add-hook 'after-save-hook 'browse-url-of-buffer nil t))
+  (message "Autoviewing turned %s."
+	   (if arg "off" "on")))
+
 ;;; sgml-mode.el ends here