summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard M. Stallman <rms@gnu.org>1996-01-12 21:14:51 +0000
committerRichard M. Stallman <rms@gnu.org>1996-01-12 21:14:51 +0000
commit2799bc443f31463bb02019afbf55ccdf1ba9950e (patch)
treef589800fafa772975e4ca13b63b4d7e044e05aef
parent14275958580e908c5a638e3968db8fb341b62517 (diff)
downloademacs-2799bc443f31463bb02019afbf55ccdf1ba9950e.tar.gz
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.
-rw-r--r--lisp/textmodes/sgml-mode.el1224
1 files changed, 1085 insertions, 139 deletions
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index 3dabd2214ee..16f87b23a9d 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -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.")
+
+
+(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'.")
+
+
+(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'.")
+
-;;; 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-name-8bit-mode nil
+ "*When non-`nil' insert 8 bit characters with their names.")
-(defconst sgml-validate-command
- "sgmls -s"
+(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 @@ separated by a space.")
(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))
+;;; 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 ()
- "Major mode for editing SGML.
-Makes > display the matching <. Makes / display matching /.
-Use \\[sgml-validate] to validate your document with an SGML parser."
- (interactive)
+(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-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)
+ ...)
+
+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.")
+
+(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-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)
- (use-local-map sgml-mode-map)
- (setq mode-name "SGML")
- (setq major-mode 'sgml-mode)
+ (set-syntax-table sgml-mode-syntax-table)
+ (make-local-variable 'indent-line-function)
(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]*")
+ (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))
+
+;;;###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 (char-after (1- (point))) ?!)
+ (not (and (eq (preceding-char) ?!)
(eq (char-after (- (point) 2)) ?<))))
(progn
(skip-chars-backward " \t")
(max comment-column (1+ (current-column))))
0))
-(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.")
-
-(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))
-
-(defconst sgml-angle-distance 4000
- "*If non-nil, is the maximum distance to search for matching <.")
-
-(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)))))))))))
-
-;;; 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 /.")
(defun sgml-slash (arg)
"Insert / and display any previous matching /.
@@ -237,6 +400,296 @@ start tag, and the second / is the corresponding null end tag."
(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 @@ and move to the line in the SGML document that caused it."
(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