Typo.
[bpt/emacs.git] / lisp / textmodes / sgml-mode.el
CommitLineData
1caf38eb 1;;; sgml-mode.el --- SGML- and HTML-editing modes
72c0ae01 2
0fda8eff 3;; Copyright (C) 1992,95,96,98,2001,2002 Free Software Foundation, Inc.
6d74b528 4
64ae0c23 5;; Author: James Clark <jjc@jclark.com>
0fda8eff 6;; Maintainer: FSF
3e910376 7;; Adapted-By: ESR, Daniel Pfeiffer <occitan@esperanto.org>,
a391b179 8;; F.Potorti@cnuce.cnr.it
1caf38eb 9;; Keywords: wp, hypermedia, comm, languages
72c0ae01 10
72c0ae01
ER
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
7c938215 15;; the Free Software Foundation; either version 2, or (at your option)
72c0ae01
ER
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
b578f267
EN
24;; along with GNU Emacs; see the file COPYING. If not, write to the
25;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26;; Boston, MA 02111-1307, USA.
72c0ae01
ER
27
28;;; Commentary:
29
1caf38eb
RS
30;; Configurable major mode for editing document in the SGML standard general
31;; markup language. As an example contains a mode for editing the derived
32;; HTML hypertext markup language.
72c0ae01
ER
33
34;;; Code:
35
d4c89075
DL
36(eval-when-compile
37 (require 'skeleton)
38 (require 'outline))
b0a377e6 39
64ae0c23
RS
40(defgroup sgml nil
41 "SGML editing mode"
42 :group 'languages)
43
5f3d924d
SM
44(defcustom sgml-basic-offset 2
45 "*Specifies the basic indentation level for `sgml-indent-line'."
46 :type 'integer
47 :group 'sgml)
48
d10447ba 49(defcustom sgml-transformation 'identity
a391b179
RS
50 "*Default value for `skeleton-transformation' (which see) in SGML mode."
51 :type 'function
c60e7b0d 52 :group 'sgml)
a391b179
RS
53
54(put 'sgml-transformation 'variable-interactive
55 "aTransformation function: ")
56
d4c89075
DL
57(defcustom sgml-mode-hook nil
58 "Hook run by command `sgml-mode'.
59`text-mode-hook' is run first."
60 :group 'sgml
61 :type 'hook)
62
1caf38eb
RS
63;; As long as Emacs' syntax can't be complemented with predicates to context
64;; sensitively confirm the syntax of characters, we have to live with this
65;; kludgy kind of tradeoff.
21a6f23c 66(defvar sgml-specials '(?\")
f788776c 67 "List of characters that have a special meaning for SGML mode.
140d71ba 68This list is used when first loading the `sgml-mode' library.
1caf38eb
RS
69The supported characters and potential disadvantages are:
70
71 ?\\\" Makes \" in text start a string.
72 ?' Makes ' in text start a string.
73 ?- Makes -- in text start a comment.
74
4fa91cfe 75When only one of ?\\\" or ?' are included, \"'\" or '\"', as can be found in
1caf38eb 76DTDs, start a string. To partially avoid this problem this also makes these
21a6f23c
RS
77self insert as named entities depending on `sgml-quick-keys'.
78
79Including ?- has the problem of affecting dashes that have nothing to do
80with comments, so we normally turn it off.")
fcc3195e
RS
81
82(defvar sgml-quick-keys nil
f788776c 83 "Use <, >, &, SPC and `sgml-specials' keys \"electrically\" when non-nil.
140d71ba 84This takes effect when first loading the `sgml-mode' library.")
1caf38eb
RS
85
86
87(defvar sgml-mode-map
e1940c83 88 (let ((map (make-keymap)) ;`sparse' doesn't allow binding to charsets.
1caf38eb 89 (menu-map (make-sparse-keymap "SGML")))
1caf38eb
RS
90 (define-key map "\C-c\C-i" 'sgml-tags-invisible)
91 (define-key map "/" 'sgml-slash)
fcc3195e
RS
92 (define-key map "\C-c\C-n" 'sgml-name-char)
93 (define-key map "\C-c\C-t" 'sgml-tag)
1caf38eb
RS
94 (define-key map "\C-c\C-a" 'sgml-attributes)
95 (define-key map "\C-c\C-b" 'sgml-skip-tag-backward)
96 (define-key map [?\C-c left] 'sgml-skip-tag-backward)
97 (define-key map "\C-c\C-f" 'sgml-skip-tag-forward)
98 (define-key map [?\C-c right] 'sgml-skip-tag-forward)
99 (define-key map "\C-c\C-d" 'sgml-delete-tag)
100 (define-key map "\C-c\^?" 'sgml-delete-tag)
101 (define-key map "\C-c?" 'sgml-tag-help)
1caf38eb
RS
102 (define-key map "\C-c8" 'sgml-name-8bit-mode)
103 (define-key map "\C-c\C-v" 'sgml-validate)
b4f05c38
SS
104 (when sgml-quick-keys
105 (define-key map "&" 'sgml-name-char)
106 (define-key map "<" 'sgml-tag)
107 (define-key map " " 'sgml-auto-attributes)
108 (define-key map ">" 'sgml-maybe-end-tag)
109 (when (memq ?\" sgml-specials)
110 (define-key map "\"" 'sgml-name-self))
111 (when (memq ?' sgml-specials)
112 (define-key map "'" 'sgml-name-self)))
f7ac3e28
SM
113 (define-key map (vector (make-char 'latin-iso8859-1))
114 'sgml-maybe-name-self)
2840d653
EZ
115 (let ((c 127)
116 (map (nth 1 map)))
117 (while (< (setq c (1+ c)) 256)
118 (aset map c 'sgml-maybe-name-self)))
1caf38eb
RS
119 (define-key map [menu-bar sgml] (cons "SGML" menu-map))
120 (define-key menu-map [sgml-validate] '("Validate" . sgml-validate))
121 (define-key menu-map [sgml-name-8bit-mode]
122 '("Toggle 8 Bit Insertion" . sgml-name-8bit-mode))
123 (define-key menu-map [sgml-tags-invisible]
124 '("Toggle Tag Visibility" . sgml-tags-invisible))
125 (define-key menu-map [sgml-tag-help]
126 '("Describe Tag" . sgml-tag-help))
127 (define-key menu-map [sgml-delete-tag]
128 '("Delete Tag" . sgml-delete-tag))
129 (define-key menu-map [sgml-skip-tag-forward]
130 '("Forward Tag" . sgml-skip-tag-forward))
131 (define-key menu-map [sgml-skip-tag-backward]
132 '("Backward Tag" . sgml-skip-tag-backward))
133 (define-key menu-map [sgml-attributes]
134 '("Insert Attributes" . sgml-attributes))
135 (define-key menu-map [sgml-tag] '("Insert Tag" . sgml-tag))
136 map)
137 "Keymap for SGML mode. See also `sgml-specials'.")
138
139
1c1d2eb6
SM
140(defun sgml-make-syntax-table (specials)
141 (let ((table (make-syntax-table text-mode-syntax-table)))
1caf38eb
RS
142 (modify-syntax-entry ?< "(>" table)
143 (modify-syntax-entry ?> ")<" table)
1c1d2eb6
SM
144 (modify-syntax-entry ?: "_" table)
145 (modify-syntax-entry ?_ "_" table)
146 (modify-syntax-entry ?. "_" table)
147 (if (memq ?- specials)
1caf38eb 148 (modify-syntax-entry ?- "_ 1234" table))
1c1d2eb6 149 (if (memq ?\" specials)
1caf38eb 150 (modify-syntax-entry ?\" "\"\"" table))
1c1d2eb6 151 (if (memq ?' specials)
1caf38eb 152 (modify-syntax-entry ?\' "\"'" table))
1c1d2eb6
SM
153 table))
154
155(defvar sgml-mode-syntax-table (sgml-make-syntax-table sgml-specials)
1caf38eb
RS
156 "Syntax table used in SGML mode. See also `sgml-specials'.")
157
1c1d2eb6
SM
158(defconst sgml-tag-syntax-table
159 (let ((table (sgml-make-syntax-table '(?- ?\" ?\'))))
160 (dolist (char '(?\( ?\) ?\{ ?\} ?\[ ?\] ?$ ?% ?& ?* ?+ ?/))
161 (modify-syntax-entry char "." table))
162 table)
163 "Syntax table used to parse SGML tags.")
164
72c0ae01 165
64ae0c23 166(defcustom sgml-name-8bit-mode nil
2840d653 167 "*When non-nil, insert non-ASCII characters as named entities."
64ae0c23
RS
168 :type 'boolean
169 :group 'sgml)
72c0ae01 170
1caf38eb
RS
171(defvar sgml-char-names
172 [nil nil nil nil nil nil nil nil
173 nil nil nil nil nil nil nil nil
174 nil nil nil nil nil nil nil nil
175 nil nil nil nil nil nil nil nil
a391b179 176 "nbsp" "excl" "quot" "num" "dollar" "percnt" "amp" "apos"
1caf38eb
RS
177 "lpar" "rpar" "ast" "plus" "comma" "hyphen" "period" "sol"
178 nil nil nil nil nil nil nil nil
179 nil nil "colon" "semi" "lt" "eq" "gt" "quest"
180 "commat" nil nil nil nil nil nil nil
181 nil nil nil nil nil nil nil nil
182 nil nil nil nil nil nil nil nil
183 nil nil nil "lsqb" nil "rsqb" "uarr" "lowbar"
184 "lsquo" nil nil nil nil nil nil nil
185 nil nil nil nil nil nil nil nil
186 nil nil nil nil nil nil nil nil
187 nil nil nil "lcub" "verbar" "rcub" "tilde" nil
188 nil nil nil nil nil nil nil nil
189 nil nil nil nil nil nil nil nil
190 nil nil nil nil nil nil nil nil
191 nil nil nil nil nil nil nil nil
192 "nbsp" "iexcl" "cent" "pound" "curren" "yen" "brvbar" "sect"
193 "uml" "copy" "ordf" "laquo" "not" "shy" "reg" "macr"
194 "ring" "plusmn" "sup2" "sup3" "acute" "micro" "para" "middot"
e79ad8a1 195 "cedil" "sup1" "ordm" "raquo" "frac14" "frac12" "frac34" "iquest"
1caf38eb
RS
196 "Agrave" "Aacute" "Acirc" "Atilde" "Auml" "Aring" "AElig" "Ccedil"
197 "Egrave" "Eacute" "Ecirc" "Euml" "Igrave" "Iacute" "Icirc" "Iuml"
198 "ETH" "Ntilde" "Ograve" "Oacute" "Ocirc" "Otilde" "Ouml" nil
199 "Oslash" "Ugrave" "Uacute" "Ucirc" "Uuml" "Yacute" "THORN" "szlig"
200 "agrave" "aacute" "acirc" "atilde" "auml" "aring" "aelig" "ccedil"
201 "egrave" "eacute" "ecirc" "euml" "igrave" "iacute" "icirc" "iuml"
202 "eth" "ntilde" "ograve" "oacute" "ocirc" "otilde" "ouml" "divide"
203 "oslash" "ugrave" "uacute" "ucirc" "uuml" "yacute" "thorn" "yuml"]
204 "Vector of symbolic character names without `&' and `;'.")
205
2840d653
EZ
206(put 'sgml-table 'char-table-extra-slots 0)
207
208(defvar sgml-char-names-table
209 (let ((table (make-char-table 'sgml-table))
210 (i 32)
211 elt)
212 (while (< i 256)
213 (setq elt (aref sgml-char-names i))
214 (if elt (aset table (make-char 'latin-iso8859-1 i) elt))
215 (setq i (1+ i)))
216 table)
217 "A table for mapping non-ASCII characters into SGML entity names.
218Currently, only Latin-1 characters are supported.")
219
1caf38eb 220
5121371d
DL
221;; nsgmls is a free SGML parser in the SP suite available from
222;; ftp.jclark.com and otherwise packaged for GNU systems.
1caf38eb
RS
223;; Its error messages can be parsed by next-error.
224;; The -s option suppresses output.
225
5121371d 226(defcustom sgml-validate-command "nsgmls -s" ; replaced old `sgmls'
72c0ae01
ER
227 "*The command to validate an SGML document.
228The file name of current buffer file name will be appended to this,
64ae0c23
RS
229separated by a space."
230 :type 'string
d4c89075 231 :version "21.1"
64ae0c23 232 :group 'sgml)
72c0ae01
ER
233
234(defvar sgml-saved-validate-command nil
235 "The command last used to validate in this buffer.")
236
72c0ae01 237
e1940c83
SM
238;; I doubt that null end tags are used much for large elements,
239;; so use a small distance here.
64ae0c23 240(defcustom sgml-slash-distance 1000
f788776c 241 "*If non-nil, is the maximum distance to search for matching `/'."
64ae0c23
RS
242 :type '(choice (const nil) integer)
243 :group 'sgml)
72c0ae01 244
5f3d924d
SM
245(defconst sgml-name-re "[_:[:alpha:]][-_.:[:alnum:]]*")
246(defconst sgml-tag-name-re (concat "<\\([!/?]?" sgml-name-re "\\)"))
247(defconst sgml-attrs-re "\\(?:[^\"'/><]\\|\"[^\"]*\"\\|'[^']*'\\)*")
248(defconst sgml-start-tag-regex (concat "<" sgml-name-re sgml-attrs-re)
1caf38eb 249 "Regular expression that matches a non-empty start tag.
f788776c 250Any terminating `>' or `/' is not matched.")
1caf38eb
RS
251
252
c6a63534
RS
253;; internal
254(defconst sgml-font-lock-keywords-1
5f3d924d
SM
255 `((,(concat "<\\([!?]" sgml-name-re "\\)") 1 font-lock-keyword-face)
256 (,(concat "<\\(/?" sgml-name-re"\\)") 1 font-lock-function-name-face)
1c1d2eb6 257 ;; FIXME: this doesn't cover the variables using a default value.
5f3d924d
SM
258 (,(concat "\\(" sgml-name-re "\\)=[\"']") 1 font-lock-variable-name-face)
259 (,(concat "[&%]" sgml-name-re ";?") . font-lock-variable-name-face)))
64367655
SM
260
261(defconst sgml-font-lock-keywords-2
262 (append
263 sgml-font-lock-keywords-1
264 '((eval
265 . (cons (concat "<"
266 (regexp-opt (mapcar 'car sgml-tag-face-alist) t)
267 "\\([ \t][^>]*\\)?>\\([^<]+\\)</\\1>")
268 '(3 (cdr (assoc (downcase (match-string 1))
269 sgml-tag-face-alist))))))))
c6a63534
RS
270
271;; for font-lock, but must be defvar'ed after
272;; sgml-font-lock-keywords-1 and sgml-font-lock-keywords-2 above
273(defvar sgml-font-lock-keywords sgml-font-lock-keywords-1
1caf38eb
RS
274 "*Rules for highlighting SGML code. See also `sgml-tag-face-alist'.")
275
64367655
SM
276(defvar sgml-font-lock-syntactic-keywords
277 ;; Use the `b' style of comments to avoid interference with the -- ... --
278 ;; comments recognized when `sgml-specials' includes ?-.
279 ;; FIXME: beware of <!--> blabla <!--> !!
280 '(("\\(<\\)!--" (1 "< b"))
281 ("--[ \t\n]*\\(>\\)" (1 "> b")))
282 "Syntactic keywords for `sgml-mode'.")
283
1caf38eb 284;; internal
1caf38eb
RS
285(defvar sgml-face-tag-alist ()
286 "Alist of face and tag name for facemenu.")
287
288(defvar sgml-tag-face-alist ()
289 "Tag names and face or list of faces to fontify with when invisible.
290When `font-lock-maximum-decoration' is 1 this is always used for fontifying.
291When more these are fontified together with `sgml-font-lock-keywords'.")
292
293
294(defvar sgml-display-text ()
295 "Tag names as lowercase symbols, and display string when invisible.")
296
297;; internal
298(defvar sgml-tags-invisible nil)
299
300
64ae0c23 301(defcustom sgml-tag-alist
fcc3195e
RS
302 '(("![" ("ignore" t) ("include" t))
303 ("!attlist")
1caf38eb
RS
304 ("!doctype")
305 ("!element")
306 ("!entity"))
307 "*Alist of tag names for completing read and insertion rules.
308This alist is made up as
309
310 ((\"tag\" . TAGRULE)
311 ...)
312
313TAGRULE is a list of optionally `t' (no endtag) or `\\n' (separate endtag by
314newlines) or a skeleton with `nil', `t' or `\\n' in place of the interactor
315followed by an ATTRIBUTERULE (for an always present attribute) or an
316attribute alist.
317
318The attribute alist is made up as
319
320 ((\"attribute\" . ATTRIBUTERULE)
321 ...)
322
323ATTRIBUTERULE is a list of optionally `t' (no value when no input) followed by
64ae0c23
RS
324an optional alist of possible values."
325 :type '(repeat (cons (string :tag "Tag Name")
326 (repeat :tag "Tag Rule" sexp)))
327 :group 'sgml)
1caf38eb 328
64ae0c23 329(defcustom sgml-tag-help
1caf38eb
RS
330 '(("!" . "Empty declaration for comment")
331 ("![" . "Embed declarations with parser directive")
332 ("!attlist" . "Tag attributes declaration")
333 ("!doctype" . "Document type (DTD) declaration")
334 ("!element" . "Tag declaration")
335 ("!entity" . "Entity (macro) declaration"))
64ae0c23
RS
336 "*Alist of tag name and short description."
337 :type '(repeat (cons (string :tag "Tag Name")
338 (string :tag "Description")))
339 :group 'sgml)
1caf38eb 340
a3ec4ba0 341(defcustom sgml-xml-mode nil
c77c3a73
SS
342 "*When non-nil, tag insertion functions will be XML-compliant.
343If this variable is customized, the custom value is used always.
344Otherwise, it is set to be buffer-local when the file has
345 a DOCTYPE or an XML declaration."
346 :type 'boolean
347 :version "21.2"
348 :group 'sgml)
349
73d25e52
SM
350(defvar sgml-empty-tags nil
351 "List of tags whose !ELEMENT definition says EMPTY.")
352
5f3d924d
SM
353(defvar sgml-unclosed-tags nil
354 "List of tags whose !ELEMENT definition says the end-tag is optional.")
355
c77c3a73
SS
356(defun sgml-xml-guess ()
357 "Guess whether the current buffer is XML."
358 (save-excursion
359 (goto-char (point-min))
a3ec4ba0
SM
360 (when (or (string= "xml" (file-name-extension (or buffer-file-name "")))
361 (looking-at "\\s-*<\\?xml")
362 (when (re-search-forward
363 (eval-when-compile
364 (mapconcat 'identity
365 '("<!DOCTYPE" "\\(\\w+\\)" "\\(\\w+\\)"
366 "\"\\([^\"]+\\)\"" "\"\\([^\"]+\\)\"")
367 "\\s-+"))
368 nil t)
369 (string-match "X\\(HT\\)?ML" (match-string 3))))
370 (set (make-local-variable 'sgml-xml-mode) t))))
c77c3a73 371
b0a377e6
DL
372(defvar v2) ; free for skeleton
373
a3ec4ba0
SM
374(defun sgml-mode-facemenu-add-face-function (face end)
375 (if (setq face (cdr (assq face sgml-face-tag-alist)))
376 (progn
377 (setq face (funcall skeleton-transformation face))
378 (setq facemenu-end-add-face (concat "</" face ">"))
379 (concat "<" face ">"))
380 (error "Face not configured for %s mode" mode-name)))
381
382
383;;;###autoload
384(define-derived-mode sgml-mode text-mode "SGML"
385 "Major mode for editing SGML documents.
386Makes > match <.
387Keys <, &, SPC within <>, \" and ' can be electric depending on
388`sgml-quick-keys'.
389
390An argument of N to a tag-inserting command means to wrap it around
391the next N words. In Transient Mark mode, when the mark is active,
392N defaults to -1, which means to wrap it around the current region.
393
394If you like upcased tags, put (setq sgml-transformation 'upcase) in
395your `.emacs' file.
396
397Use \\[sgml-validate] to validate your document with an SGML parser.
398
399Do \\[describe-variable] sgml- SPC to see available variables.
400Do \\[describe-key] on the following bindings to discover what they do.
401\\{sgml-mode-map}"
72c0ae01 402 (make-local-variable 'sgml-saved-validate-command)
1caf38eb
RS
403 (make-local-variable 'facemenu-end-add-face)
404 ;;(make-local-variable 'facemenu-remove-face-function)
c77c3a73
SS
405 ;; A start or end tag by itself on a line separates a paragraph.
406 ;; This is desirable because SGML discards a newline that appears
407 ;; immediately after a start tag or immediately before an end tag.
5f3d924d
SM
408 (set (make-local-variable 'paragraph-start) (concat "[ \t]*$\\|\
409\[ \t]*</?\\(" sgml-name-re sgml-attrs-re "\\)?>"))
410 (set (make-local-variable 'paragraph-separate)
411 (concat paragraph-start "$"))
c77c3a73 412 (set (make-local-variable 'adaptive-fill-regexp) "[ \t]*")
9c599518 413 (set (make-local-variable 'indent-line-function) 'sgml-indent-line)
c77c3a73
SS
414 (set (make-local-variable 'comment-start) "<!-- ")
415 (set (make-local-variable 'comment-end) " -->")
416 (set (make-local-variable 'comment-indent-function) 'sgml-comment-indent)
c77c3a73
SS
417 (set (make-local-variable 'skeleton-further-elements)
418 '((completion-ignore-case t)))
419 (set (make-local-variable 'skeleton-end-hook)
420 (lambda ()
421 (or (eolp)
422 (not (or (eq v2 '\n) (eq (car-safe v2) '\n)))
423 (newline-and-indent))))
424 (set (make-local-variable 'font-lock-defaults)
425 '((sgml-font-lock-keywords
426 sgml-font-lock-keywords-1
427 sgml-font-lock-keywords-2)
428 nil t nil nil
429 (font-lock-syntactic-keywords
430 . sgml-font-lock-syntactic-keywords)))
431 (set (make-local-variable 'facemenu-add-face-function)
432 'sgml-mode-facemenu-add-face-function)
a3ec4ba0
SM
433 (sgml-xml-guess)
434 (if sgml-xml-mode
435 (setq mode-name "XML")
436 (set (make-local-variable 'skeleton-transformation) sgml-transformation))
4afa094d
SM
437 ;; This will allow existing comments within declarations to be
438 ;; recognized.
439 (set (make-local-variable 'comment-start-skip) "\\(?:<!\\)?--[ \t]*")
a3ec4ba0
SM
440 (set (make-local-variable 'comment-end-skip) "[ \t]*--\\([ \t\n]*>\\)?")
441 ;; This definition probably is not useful in derived modes.
c77c3a73 442 (set (make-local-variable 'imenu-generic-expression)
5f3d924d
SM
443 (concat "<!\\(element\\|entity\\)[ \t\n]+%?[ \t\n]*\\("
444 sgml-name-re "\\)")))
1caf38eb
RS
445
446
72c0ae01 447(defun sgml-comment-indent ()
4afa094d 448 (if (looking-at "--") comment-column 0))
72c0ae01 449
72c0ae01 450
72c0ae01
ER
451
452(defun sgml-slash (arg)
f788776c
RS
453 "Insert `/' and display any previous matching `/'.
454Two `/'s are treated as matching if the first `/' ends a net-enabling
455start tag, and the second `/' is the corresponding null end tag."
72c0ae01
ER
456 (interactive "p")
457 (insert-char ?/ arg)
458 (if (> arg 0)
459 (let ((oldpos (point))
460 (blinkpos)
461 (level 0))
462 (save-excursion
463 (save-restriction
464 (if sgml-slash-distance
465 (narrow-to-region (max (point-min)
466 (- (point) sgml-slash-distance))
467 oldpos))
468 (if (and (re-search-backward sgml-start-tag-regex (point-min) t)
469 (eq (match-end 0) (1- oldpos)))
470 ()
471 (goto-char (1- oldpos))
472 (while (and (not blinkpos)
473 (search-backward "/" (point-min) t))
474 (let ((tagend (save-excursion
475 (if (re-search-backward sgml-start-tag-regex
476 (point-min) t)
477 (match-end 0)
478 nil))))
479 (if (eq tagend (point))
480 (if (eq level 0)
481 (setq blinkpos (point))
482 (setq level (1- level)))
483 (setq level (1+ level)))))))
5950e029
SS
484 (when blinkpos
485 (goto-char blinkpos)
486 (if (pos-visible-in-window-p)
487 (sit-for 1)
488 (message "Matches %s"
489 (buffer-substring (line-beginning-position)
490 (1+ blinkpos)))))))))
72c0ae01 491
1caf38eb 492
0fda8eff
SM
493;; Why doesn't this use the iso-cvt table or, preferably, generate the
494;; inverse of the extensive table in the SGML Quail input method? -- fx
495;; I guess that's moot since it only works with Latin-1 anyhow.
1caf38eb
RS
496(defun sgml-name-char (&optional char)
497 "Insert a symbolic character name according to `sgml-char-names'.
2840d653
EZ
498Non-ASCII chars may be inserted either with the meta key, as in M-SPC for
499no-break space or M-- for a soft hyphen; or via an input method or
500encoded keyboard operation."
1caf38eb
RS
501 (interactive "*")
502 (insert ?&)
503 (or char
9b0ffdac 504 (setq char (read-quoted-char "Enter char or octal number")))
1caf38eb
RS
505 (delete-backward-char 1)
506 (insert char)
507 (undo-boundary)
508 (delete-backward-char 1)
2840d653
EZ
509 (cond
510 ((< char 256)
511 (insert ?&
512 (or (aref sgml-char-names char)
513 (format "#%d" char))
514 ?\;))
515 ((aref sgml-char-names-table char)
516 (insert ?& (aref sgml-char-names-table char) ?\;))
0fda8eff
SM
517 ((let ((c (encode-char char 'ucs)))
518 (when c
519 (insert (format "&#%d;" c))
520 t)))
521 (t ; should be an error? -- fx
2840d653 522 (insert char))))
1caf38eb
RS
523
524(defun sgml-name-self ()
525 "Insert a symbolic character name according to `sgml-char-names'."
526 (interactive "*")
527 (sgml-name-char last-command-char))
528
1caf38eb
RS
529(defun sgml-maybe-name-self ()
530 "Insert a symbolic character name according to `sgml-char-names'."
531 (interactive "*")
532 (if sgml-name-8bit-mode
2840d653
EZ
533 (let ((mc last-command-char))
534 (if (< mc 256)
535 (setq mc (unibyte-char-to-multibyte mc)))
536 (or mc (setq mc last-command-char))
537 (sgml-name-char mc))
1caf38eb
RS
538 (self-insert-command 1)))
539
1caf38eb 540(defun sgml-name-8bit-mode ()
0fda8eff
SM
541 "Toggle whether to insert named entities instead of non-ASCII characters.
542This only works for Latin-1 input."
1caf38eb 543 (interactive)
d10447ba 544 (setq sgml-name-8bit-mode (not sgml-name-8bit-mode))
2840d653 545 (message "sgml name entity mode is now %s"
d10447ba 546 (if sgml-name-8bit-mode "ON" "OFF")))
1caf38eb 547
f788776c
RS
548;; When an element of a skeleton is a string "str", it is passed
549;; through skeleton-transformation and inserted. If "str" is to be
550;; inserted literally, one should obtain it as the return value of a
551;; function, e.g. (identity "str").
1caf38eb
RS
552
553(define-skeleton sgml-tag
f788776c
RS
554 "Prompt for a tag and insert it, optionally with attributes.
555Completion and configuration are done according to `sgml-tag-alist'.
d10447ba 556If you like tags and attributes in uppercase do \\[set-variable]
f788776c
RS
557skeleton-transformation RET upcase RET, or put this in your `.emacs':
558 (setq sgml-transformation 'upcase)"
5f5c9e79
SM
559 (funcall skeleton-transformation
560 (completing-read "Tag: " sgml-tag-alist))
4afa094d 561 ?< str |
d10447ba 562 (("") -1 '(undo-boundary) (identity "&lt;")) | ; see comment above
73d25e52
SM
563 `(("") '(setq v2 (sgml-attributes ,str t)) ?>
564 (cond
565 ((string= "![" ,str)
566 (backward-char)
567 '(("") " [ " _ " ]]"))
a3ec4ba0 568 ((and (eq v2 t) sgml-xml-mode (member ,str sgml-empty-tags))
73d25e52 569 '(("") -1 "/>"))
a3ec4ba0 570 ((or (and (eq v2 t) (not sgml-xml-mode)) (string-match "^[/!?]" ,str))
73d25e52
SM
571 nil)
572 ((symbolp v2)
573 ;; Make sure we don't fall into an infinite loop.
574 ;; For xhtml's `tr' tag, we should maybe use \n instead.
575 (if (eq v2 t) (setq v2 nil))
576 ;; We use `identity' to prevent skeleton from passing
577 ;; `str' through skeleton-transformation a second time.
578 '(("") v2 _ v2 "</" (identity ',str) ?>))
579 ((eq (car v2) t)
580 (cons '("") (cdr v2)))
581 (t
582 (append '(("") (car v2))
583 (cdr v2)
584 '(resume: (car v2) _ "</" (identity ',str) ?>))))))
1caf38eb
RS
585
586(autoload 'skeleton-read "skeleton")
587
d10447ba 588(defun sgml-attributes (tag &optional quiet)
f788776c 589 "When at top level of a tag, interactively insert attributes.
d10447ba 590
f788776c
RS
591Completion and configuration of TAG are done according to `sgml-tag-alist'.
592If QUIET, do not print a message when there are no attributes for TAG."
1caf38eb 593 (interactive (list (save-excursion (sgml-beginning-of-tag t))))
d10447ba
RS
594 (or (stringp tag) (error "Wrong context for adding attribute"))
595 (if tag
1caf38eb 596 (let ((completion-ignore-case t)
d10447ba 597 (alist (cdr (assoc (downcase tag) sgml-tag-alist)))
1caf38eb 598 car attribute i)
1caf38eb
RS
599 (if (or (symbolp (car alist))
600 (symbolp (car (car alist))))
601 (setq car (car alist)
602 alist (cdr alist)))
603 (or quiet
604 (message "No attributes configured."))
605 (if (stringp (car alist))
606 (progn
d10447ba
RS
607 (insert (if (eq (preceding-char) ? ) "" ? )
608 (funcall skeleton-transformation (car alist)))
1caf38eb
RS
609 (sgml-value alist))
610 (setq i (length alist))
611 (while (> i 0)
612 (insert ? )
613 (insert (funcall skeleton-transformation
614 (setq attribute
615 (skeleton-read '(completing-read
d10447ba 616 "Attribute: "
1caf38eb
RS
617 alist)))))
618 (if (string= "" attribute)
619 (setq i 0)
aa7a8f0e 620 (sgml-value (assoc (downcase attribute) alist))
1caf38eb
RS
621 (setq i (1- i))))
622 (if (eq (preceding-char) ? )
623 (delete-backward-char 1)))
624 car)))
625
626(defun sgml-auto-attributes (arg)
f788776c
RS
627 "Self insert the character typed; at top level of tag, prompt for attributes.
628With prefix argument, only self insert."
1caf38eb
RS
629 (interactive "*P")
630 (let ((point (point))
631 tag)
632 (if (or arg
1caf38eb
RS
633 (not sgml-tag-alist) ; no message when nothing configured
634 (symbolp (setq tag (save-excursion (sgml-beginning-of-tag t))))
635 (eq (aref tag 0) ?/))
636 (self-insert-command (prefix-numeric-value arg))
637 (sgml-attributes tag)
638 (setq last-command-char ? )
639 (or (> (point) point)
640 (self-insert-command 1)))))
641
642
643(defun sgml-tag-help (&optional tag)
f788776c 644 "Display description of tag TAG. If TAG is omitted, use the tag at point."
1caf38eb
RS
645 (interactive)
646 (or tag
647 (save-excursion
648 (if (eq (following-char) ?<)
649 (forward-char))
650 (setq tag (sgml-beginning-of-tag))))
651 (or (stringp tag)
652 (error "No tag selected"))
653 (setq tag (downcase tag))
f68f40e0 654 (message "%s"
aa7a8f0e 655 (or (cdr (assoc (downcase tag) sgml-tag-help))
1caf38eb 656 (and (eq (aref tag 0) ?/)
aa7a8f0e 657 (cdr (assoc (downcase (substring tag 1)) sgml-tag-help)))
1caf38eb
RS
658 "No description available")))
659
660
1c1d2eb6
SM
661(defun sgml-maybe-end-tag (&optional arg)
662 "Name self unless in position to end a tag or a prefix ARG is given."
663 (interactive "P")
664 (if (or arg (eq (car (sgml-lexical-context)) 'tag))
665 (self-insert-command (prefix-numeric-value arg))
666 (sgml-name-self)))
1caf38eb
RS
667
668(defun sgml-skip-tag-backward (arg)
669 "Skip to beginning of tag or matching opening tag if present.
f788776c 670With prefix argument ARG, repeat this ARG times."
1caf38eb
RS
671 (interactive "p")
672 (while (>= arg 1)
673 (search-backward "<" nil t)
674 (if (looking-at "</\\([^ \n\t>]+\\)")
675 ;; end tag, skip any nested pairs
676 (let ((case-fold-search t)
677 (re (concat "</?" (regexp-quote (match-string 1)))))
678 (while (and (re-search-backward re nil t)
679 (eq (char-after (1+ (point))) ?/))
680 (forward-char 1)
681 (sgml-skip-tag-backward 1))))
682 (setq arg (1- arg))))
683
684(defun sgml-skip-tag-forward (arg &optional return)
685 "Skip to end of tag or matching closing tag if present.
f788776c 686With prefix argument ARG, repeat this ARG times.
1caf38eb
RS
687Return t iff after a closing tag."
688 (interactive "p")
689 (setq return t)
690 (while (>= arg 1)
691 (skip-chars-forward "^<>")
692 (if (eq (following-char) ?>)
693 (up-list -1))
694 (if (looking-at "<\\([^/ \n\t>]+\\)")
695 ;; start tag, skip any nested same pairs _and_ closing tag
696 (let ((case-fold-search t)
697 (re (concat "</?" (regexp-quote (match-string 1))))
698 point close)
699 (forward-list 1)
700 (setq point (point))
701 (while (and (re-search-forward re nil t)
702 (not (setq close
703 (eq (char-after (1+ (match-beginning 0))) ?/)))
704 (not (up-list -1))
705 (sgml-skip-tag-forward 1))
706 (setq close nil))
707 (if close
708 (up-list 1)
709 (goto-char point)
710 (setq return)))
711 (forward-list 1))
712 (setq arg (1- arg)))
713 return)
714
715(defun sgml-delete-tag (arg)
716 "Delete tag on or after cursor, and matching closing or opening tag.
f788776c 717With prefix argument ARG, repeat this ARG times."
1caf38eb
RS
718 (interactive "p")
719 (while (>= arg 1)
720 (save-excursion
721 (let* (close open)
fcc3195e 722 (if (looking-at "[ \t\n]*<")
1caf38eb
RS
723 ;; just before tag
724 (if (eq (char-after (match-end 0)) ?/)
725 ;; closing tag
726 (progn
727 (setq close (point))
728 (goto-char (match-end 0))))
729 ;; on tag?
730 (or (save-excursion (setq close (sgml-beginning-of-tag)
731 close (and (stringp close)
732 (eq (aref close 0) ?/)
733 (point))))
734 ;; not on closing tag
735 (let ((point (point)))
736 (sgml-skip-tag-backward 1)
737 (if (or (not (eq (following-char) ?<))
738 (save-excursion
739 (forward-list 1)
740 (<= (point) point)))
741 (error "Not on or before tag")))))
742 (if close
743 (progn
744 (sgml-skip-tag-backward 1)
745 (setq open (point))
746 (goto-char close)
747 (kill-sexp 1))
748 (setq open (point))
749 (sgml-skip-tag-forward 1)
750 (backward-list)
751 (forward-char)
752 (if (eq (aref (sgml-beginning-of-tag) 0) ?/)
753 (kill-sexp 1)))
754 (goto-char open)
755 (kill-sexp 1)))
756 (setq arg (1- arg))))
a391b179
RS
757\f
758;; Put read-only last to enable setting this even when read-only enabled.
759(or (get 'sgml-tag 'invisible)
760 (setplist 'sgml-tag
761 (append '(invisible t
762 intangible t
763 point-entered sgml-point-entered
764 rear-nonsticky t
765 read-only t)
766 (symbol-plist 'sgml-tag))))
1caf38eb
RS
767
768(defun sgml-tags-invisible (arg)
769 "Toggle visibility of existing tags."
770 (interactive "P")
771 (let ((modified (buffer-modified-p))
772 (inhibit-read-only t)
e1940c83
SM
773 (inhibit-modification-hooks t)
774 ;; Avoid spurious the `file-locked' checks.
775 (buffer-file-name nil)
a391b179
RS
776 ;; This is needed in case font lock gets called,
777 ;; since it moves point and might call sgml-point-entered.
64367655 778 ;; How could it get called? -stef
a391b179 779 (inhibit-point-motion-hooks t)
64367655 780 string)
e1940c83
SM
781 (unwind-protect
782 (save-excursion
783 (goto-char (point-min))
73d25e52
SM
784 (if (set (make-local-variable 'sgml-tags-invisible)
785 (if arg
786 (>= (prefix-numeric-value arg) 0)
787 (not sgml-tags-invisible)))
1c1d2eb6 788 (while (re-search-forward sgml-tag-name-re nil t)
64367655
SM
789 (setq string
790 (cdr (assq (intern-soft (downcase (match-string 1)))
791 sgml-display-text)))
e1940c83 792 (goto-char (match-beginning 0))
64367655 793 (and (stringp string)
e1940c83 794 (not (overlays-at (point)))
73d25e52
SM
795 (let ((ol (make-overlay (point) (match-beginning 1))))
796 (overlay-put ol 'before-string string)
797 (overlay-put ol 'sgml-tag t)))
e1940c83
SM
798 (put-text-property (point)
799 (progn (forward-list) (point))
800 'category 'sgml-tag))
64367655 801 (let ((pos (point-min)))
e1940c83 802 (while (< (setq pos (next-overlay-change pos)) (point-max))
73d25e52 803 (dolist (ol (overlays-at pos))
b2e8c203 804 (if (overlay-get ol 'sgml-tag)
73d25e52 805 (delete-overlay ol)))))
64367655 806 (remove-text-properties (point-min) (point-max) '(category nil))))
e1940c83 807 (restore-buffer-modified-p modified))
1caf38eb
RS
808 (run-hooks 'sgml-tags-invisible-hook)
809 (message "")))
810
811(defun sgml-point-entered (x y)
812 ;; Show preceding or following hidden tag, depending of cursor direction.
813 (let ((inhibit-point-motion-hooks t))
814 (save-excursion
815 (message "Invisible tag: %s"
e1940c83
SM
816 ;; Strip properties, otherwise, the text is invisible.
817 (buffer-substring-no-properties
1caf38eb
RS
818 (point)
819 (if (or (and (> x y)
820 (not (eq (following-char) ?<)))
821 (and (< x y)
822 (eq (preceding-char) ?>)))
823 (backward-list)
824 (forward-list)))))))
a391b179 825\f
1caf38eb
RS
826(autoload 'compile-internal "compile")
827
72c0ae01
ER
828(defun sgml-validate (command)
829 "Validate an SGML document.
830Runs COMMAND, a shell command, in a separate process asynchronously
f788776c 831with output going to the buffer `*compilation*'.
72c0ae01
ER
832You can then use the command \\[next-error] to find the next error message
833and move to the line in the SGML document that caused it."
834 (interactive
835 (list (read-string "Validate command: "
836 (or sgml-saved-validate-command
837 (concat sgml-validate-command
838 " "
839 (let ((name (buffer-file-name)))
840 (and name
841 (file-name-nondirectory name))))))))
842 (setq sgml-saved-validate-command command)
b7cd1746 843 (save-some-buffers (not compilation-ask-about-save) nil)
c7aa4667 844 (compile-internal command "No more errors"))
72c0ae01 845
1caf38eb 846
1c1d2eb6
SM
847(defun sgml-lexical-context (&optional limit)
848 "Return the lexical context at point as (TYPE . START).
849START is the location of the start of the lexical element.
3fb819e5 850TYPE is one of `string', `comment', `tag', or `text'.
1c1d2eb6
SM
851
852If non-nil LIMIT is a nearby position before point outside of any tag."
853 ;; As usual, it's difficult to get a reliable answer without parsing the
854 ;; whole buffer. We'll assume that a tag at indentation is outside of
855 ;; any string or tag or comment or ...
856 (save-excursion
857 (let ((pos (point))
3fb819e5
SM
858 (state nil)
859 textstart)
5f3d924d
SM
860 (if limit (goto-char limit)
861 ;; Hopefully this regexp will match something that's not inside
862 ;; a tag and also hopefully the match is nearby.
863 (re-search-backward "^[ \t]*<[_:[:alpha:]/%!?#]" nil 'move))
3fb819e5 864 (setq textstart (point))
5f3d924d
SM
865 (with-syntax-table sgml-tag-syntax-table
866 (while (< (point) pos)
867 ;; When entering this loop we're inside text.
3fb819e5 868 (setq textstart (point))
5f3d924d
SM
869 (skip-chars-forward "^<" pos)
870 ;; We skipped text and reached a tag. Parse it.
3fb819e5 871 ;; FIXME: Handle net-enabling start-tags and <![CDATA[ ...]]>.
5f3d924d
SM
872 (setq state (parse-partial-sexp (point) pos 0)))
873 (cond
874 ((nth 3 state) (cons 'string (nth 8 state)))
875 ((nth 4 state) (cons 'comment (nth 8 state)))
876 ((and state (> (nth 0 state) 0)) (cons 'tag (nth 1 state)))
3fb819e5 877 (t (cons 'text textstart)))))))
1c1d2eb6 878
1caf38eb
RS
879(defun sgml-beginning-of-tag (&optional top-level)
880 "Skip to beginning of tag and return its name.
1c1d2eb6
SM
881If this can't be done, return nil."
882 (let ((context (sgml-lexical-context)))
883 (if (eq (car context) 'tag)
884 (progn
885 (goto-char (cdr context))
886 (when (looking-at sgml-tag-name-re)
887 (match-string-no-properties 1)))
888 (if top-level nil
3fb819e5 889 (when (not (eq (car context) 'text))
1c1d2eb6
SM
890 (goto-char (cdr context))
891 (sgml-beginning-of-tag t))))))
1caf38eb
RS
892
893(defun sgml-value (alist)
347ea557 894 "Interactively insert value taken from attribute-rule ALIST.
5950e029 895See `sgml-tag-alist' for info about attribute rules."
1caf38eb
RS
896 (setq alist (cdr alist))
897 (if (stringp (car alist))
898 (insert "=\"" (car alist) ?\")
a3ec4ba0 899 (if (and (eq (car alist) t) (not sgml-xml-mode))
5950e029 900 (when (cdr alist)
73d25e52
SM
901 (insert "=\"")
902 (setq alist (skeleton-read '(completing-read "Value: " (cdr alist))))
903 (if (string< "" alist)
904 (insert alist ?\")
905 (delete-backward-char 2)))
1caf38eb 906 (insert "=\"")
5950e029
SS
907 (when alist
908 (insert (skeleton-read '(completing-read "Value: " alist))))
1caf38eb 909 (insert ?\"))))
64367655
SM
910
911(defun sgml-quote (start end &optional unquotep)
912 "Quote SGML text in region.
913With prefix argument, unquote the region."
914 (interactive "r\np")
915 (if (< start end)
916 (goto-char start)
917 (goto-char end)
918 (setq end start))
919 (if unquotep
1c1d2eb6 920 (while (re-search-forward "&\\(amp\\|\\(l\\|\\(g\\)\\)t\\)[;\n]" end t)
64367655
SM
921 (replace-match (if (match-end 3) ">" (if (match-end 2) "<" "&"))))
922 (while (re-search-forward "[&<>]" end t)
923 (replace-match (cdr (assq (char-before) '((?& . "&amp;")
924 (?< . "&lt;")
925 (?> . "&gt;"))))))))
1caf38eb 926\f
e1940c83 927
347ea557
MW
928(defun sgml-empty-tag-p (tag-name)
929 "Return non-nil if TAG-NAME is an implicitly empty tag."
930 (and (not sgml-xml-mode)
931 (member-ignore-case tag-name sgml-empty-tags)))
932
933(defun sgml-unclosed-tag-p (tag-name)
934 "Return non-nil if TAG-NAME is a tag for which an end-tag is optional."
935 (and (not sgml-xml-mode)
936 (member-ignore-case tag-name sgml-unclosed-tags)))
937
1c1d2eb6
SM
938(defun sgml-calculate-indent ()
939 "Calculate the column to which this line should be indented."
940 (let ((lcon (sgml-lexical-context)))
347ea557 941
1c1d2eb6
SM
942 ;; Indent comment-start markers inside <!-- just like comment-end markers.
943 (if (and (eq (car lcon) 'tag)
944 (looking-at "--")
945 (save-excursion (goto-char (cdr lcon)) (looking-at "<!--")))
946 (setq lcon (cons 'comment (+ (cdr lcon) 2))))
947
948 (case (car lcon)
347ea557 949
1c1d2eb6
SM
950 (string
951 ;; Go back to previous non-empty line.
952 (while (and (> (point) (cdr lcon))
953 (zerop (forward-line -1))
954 (looking-at "[ \t]*$")))
955 (if (> (point) (cdr lcon))
956 ;; Previous line is inside the string.
957 (current-indentation)
958 (goto-char (cdr lcon))
959 (1+ (current-column))))
960
961 (comment
962 (let ((mark (looking-at "--")))
963 ;; Go back to previous non-empty line.
964 (while (and (> (point) (cdr lcon))
965 (zerop (forward-line -1))
966 (or (looking-at "[ \t]*$")
967 (if mark (not (looking-at "[ \t]*--"))))))
968 (if (> (point) (cdr lcon))
969 ;; Previous line is inside the comment.
970 (skip-chars-forward " \t")
971 (goto-char (cdr lcon)))
972 (when (and (not mark) (looking-at "--"))
973 (forward-char 2) (skip-chars-forward " \t"))
974 (current-column)))
975
976 (tag
977 (goto-char (1+ (cdr lcon)))
978 (skip-chars-forward "^ \t\n") ;Skip tag name.
979 (skip-chars-forward " \t")
980 (if (not (eolp))
981 (current-column)
982 ;; This is the first attribute: indent.
983 (goto-char (1+ (cdr lcon)))
984 (+ (current-column) sgml-basic-offset)))
985
347ea557 986 (text
1c1d2eb6
SM
987 (while (looking-at "</")
988 (forward-sexp 1)
989 (skip-chars-forward " \t"))
3fb819e5
SM
990 (let* ((here (point))
991 (unclosed (and ;; (not sgml-xml-mode)
992 (looking-at sgml-tag-name-re)
993 (member-ignore-case (match-string 1)
994 sgml-unclosed-tags)
995 (match-string 1)))
996 (context
997 ;; If possible, align on the previous non-empty text line.
998 ;; Otherwise, do a more serious parsing to find the
999 ;; tag(s) relative to which we should be indenting.
1000 (if (and (not unclosed) (skip-chars-backward " \t")
1001 (< (skip-chars-backward " \t\n") 0)
1002 (back-to-indentation)
1003 (> (point) (cdr lcon)))
1004 nil
1005 (goto-char here)
1006 (nreverse (xml-lite-get-context (if unclosed nil 'empty)))))
1007 (there (point)))
1008 ;; Ignore previous unclosed start-tag in context.
1009 (while (and context unclosed
1010 (eq t (compare-strings
1011 (xml-lite-tag-name (car context)) nil nil
1012 unclosed nil nil t)))
1013 (setq context (cdr context)))
1014 ;; Indent to reflect nesting.
1015 (if (and context
1016 (goto-char (xml-lite-tag-end (car context)))
1017 (skip-chars-forward " \t\n")
1018 (< (point) here) (xml-lite-at-indentation-p))
1019 (current-column)
1020 (goto-char there)
1021 (+ (current-column)
347ea557
MW
1022 (* sgml-basic-offset (length context))))))
1023
1024 (otherwise
1025 (error "Unrecognised context %s" (car lcon)))
1026
1027 )))
1c1d2eb6
SM
1028
1029(defun sgml-indent-line ()
1030 "Indent the current line as SGML."
1031 (interactive)
1032 (let* ((savep (point))
1033 (indent-col
1034 (save-excursion
5f3d924d 1035 (back-to-indentation)
1c1d2eb6 1036 (if (>= (point) savep) (setq savep nil))
1c1d2eb6
SM
1037 (sgml-calculate-indent))))
1038 (if savep
1039 (save-excursion (indent-line-to indent-col))
1040 (indent-line-to indent-col))))
1041
5f3d924d
SM
1042(defun sgml-parse-dtd ()
1043 "Simplistic parse of the current buffer as a DTD.
1044Currently just returns (EMPTY-TAGS UNCLOSED-TAGS)."
1045 (goto-char (point-min))
1046 (let ((empty nil)
1047 (unclosed nil))
1048 (while (re-search-forward "<!ELEMENT[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+[-O][ \t\n]+\\([-O]\\)[ \t\n]+\\([^ \t\n]+\\)" nil t)
1049 (cond
1050 ((string= (match-string 3) "EMPTY")
1051 (push (match-string-no-properties 1) empty))
1052 ((string= (match-string 2) "O")
1053 (push (match-string-no-properties 1) unclosed))))
1054 (setq empty (sort (mapcar 'downcase empty) 'string<))
1055 (setq unclosed (sort (mapcar 'downcase unclosed) 'string<))
1056 (list empty unclosed)))
1057
e1940c83
SM
1058;;; HTML mode
1059
d4c89075
DL
1060(defcustom html-mode-hook nil
1061 "Hook run by command `html-mode'.
1062`text-mode-hook' and `sgml-mode-hook' are run first."
1063 :group 'sgml
1064 :type 'hook
1065 :options '(html-autoview-mode))
1066
fcc3195e 1067(defvar html-quick-keys sgml-quick-keys
b1e7bb48 1068 "Use C-c X combinations for quick insertion of frequent tags when non-nil.
fcc3195e 1069This defaults to `sgml-quick-keys'.
1caf38eb
RS
1070This takes effect when first loading the library.")
1071
1072(defvar html-mode-map
5f5c9e79 1073 (let ((map (make-sparse-keymap))
1caf38eb 1074 (menu-map (make-sparse-keymap "HTML")))
5f5c9e79 1075 (set-keymap-parent map sgml-mode-map)
7e49eef2
RS
1076 (define-key map "\C-c6" 'html-headline-6)
1077 (define-key map "\C-c5" 'html-headline-5)
1078 (define-key map "\C-c4" 'html-headline-4)
1079 (define-key map "\C-c3" 'html-headline-3)
1080 (define-key map "\C-c2" 'html-headline-2)
1081 (define-key map "\C-c1" 'html-headline-1)
fcc3195e
RS
1082 (define-key map "\C-c\r" 'html-paragraph)
1083 (define-key map "\C-c\n" 'html-line)
1084 (define-key map "\C-c\C-c-" 'html-horizontal-rule)
7e49eef2
RS
1085 (define-key map "\C-c\C-co" 'html-ordered-list)
1086 (define-key map "\C-c\C-cu" 'html-unordered-list)
fcc3195e
RS
1087 (define-key map "\C-c\C-cr" 'html-radio-buttons)
1088 (define-key map "\C-c\C-cc" 'html-checkboxes)
1089 (define-key map "\C-c\C-cl" 'html-list-item)
1090 (define-key map "\C-c\C-ch" 'html-href-anchor)
1091 (define-key map "\C-c\C-cn" 'html-name-anchor)
1092 (define-key map "\C-c\C-ci" 'html-image)
5950e029
SS
1093 (when html-quick-keys
1094 (define-key map "\C-c-" 'html-horizontal-rule)
1095 (define-key map "\C-co" 'html-ordered-list)
1096 (define-key map "\C-cu" 'html-unordered-list)
1097 (define-key map "\C-cr" 'html-radio-buttons)
1098 (define-key map "\C-cc" 'html-checkboxes)
1099 (define-key map "\C-cl" 'html-list-item)
1100 (define-key map "\C-ch" 'html-href-anchor)
1101 (define-key map "\C-cn" 'html-name-anchor)
1102 (define-key map "\C-ci" 'html-image))
1caf38eb
RS
1103 (define-key map "\C-c\C-s" 'html-autoview-mode)
1104 (define-key map "\C-c\C-v" 'browse-url-of-buffer)
1105 (define-key map [menu-bar html] (cons "HTML" menu-map))
1106 (define-key menu-map [html-autoview-mode]
1107 '("Toggle Autoviewing" . html-autoview-mode))
1108 (define-key menu-map [browse-url-of-buffer]
1109 '("View Buffer Contents" . browse-url-of-buffer))
1110 (define-key menu-map [nil] '("--"))
7e49eef2
RS
1111 ;;(define-key menu-map "6" '("Heading 6" . html-headline-6))
1112 ;;(define-key menu-map "5" '("Heading 5" . html-headline-5))
1113 ;;(define-key menu-map "4" '("Heading 4" . html-headline-4))
1114 (define-key menu-map "3" '("Heading 3" . html-headline-3))
1115 (define-key menu-map "2" '("Heading 2" . html-headline-2))
1116 (define-key menu-map "1" '("Heading 1" . html-headline-1))
1caf38eb 1117 (define-key menu-map "l" '("Radio Buttons" . html-radio-buttons))
fcc3195e 1118 (define-key menu-map "c" '("Checkboxes" . html-checkboxes))
1caf38eb 1119 (define-key menu-map "l" '("List Item" . html-list-item))
7e49eef2
RS
1120 (define-key menu-map "u" '("Unordered List" . html-unordered-list))
1121 (define-key menu-map "o" '("Ordered List" . html-ordered-list))
fcc3195e 1122 (define-key menu-map "-" '("Horizontal Rule" . html-horizontal-rule))
1caf38eb
RS
1123 (define-key menu-map "\n" '("Line Break" . html-line))
1124 (define-key menu-map "\r" '("Paragraph" . html-paragraph))
1125 (define-key menu-map "i" '("Image" . html-image))
1126 (define-key menu-map "h" '("Href Anchor" . html-href-anchor))
1127 (define-key menu-map "n" '("Name Anchor" . html-name-anchor))
1128 map)
1129 "Keymap for commands for use in HTML mode.")
1130
1131
1132(defvar html-face-tag-alist
1133 '((bold . "b")
1134 (italic . "i")
1135 (underline . "u")
1136 (modeline . "rev"))
1137 "Value of `sgml-face-tag-alist' for HTML mode.")
1138
1139(defvar html-tag-face-alist
1140 '(("b" . bold)
1141 ("big" . bold)
1142 ("blink" . highlight)
1143 ("cite" . italic)
1144 ("em" . italic)
1145 ("h1" bold underline)
1146 ("h2" bold-italic underline)
1147 ("h3" italic underline)
1148 ("h4" . underline)
1149 ("h5" . underline)
1150 ("h6" . underline)
1151 ("i" . italic)
1152 ("rev" . modeline)
1153 ("s" . underline)
1154 ("small" . default)
1155 ("strong" . bold)
1156 ("title" bold underline)
1157 ("tt" . default)
1158 ("u" . underline)
1159 ("var" . italic))
1160 "Value of `sgml-tag-face-alist' for HTML mode.")
1161
1162
1163(defvar html-display-text
1164 '((img . "[/]")
1165 (hr . "----------")
1166 (li . "o "))
1167 "Value of `sgml-display-text' for HTML mode.")
3bf0b727 1168\f
b4f05c38 1169
3bf0b727 1170;; should code exactly HTML 3 here when that is finished
1caf38eb 1171(defvar html-tag-alist
d10447ba 1172 (let* ((1-7 '(("1") ("2") ("3") ("4") ("5") ("6") ("7")))
e1940c83 1173 (1-9 `(,@1-7 ("8") ("9")))
1caf38eb
RS
1174 (align '(("align" ("left") ("center") ("right"))))
1175 (valign '(("top") ("middle") ("bottom") ("baseline")))
1176 (rel '(("next") ("previous") ("parent") ("subdocument") ("made")))
1177 (href '("href" ("ftp:") ("file:") ("finger:") ("gopher:") ("http:")
1178 ("mailto:") ("news:") ("rlogin:") ("telnet:") ("tn3270:")
fcc3195e 1179 ("wais:") ("/cgi-bin/")))
1caf38eb
RS
1180 (name '("name"))
1181 (link `(,href
1182 ("rel" ,@rel)
1183 ("rev" ,@rel)
1184 ("title")))
b4f05c38 1185 (list '((nil \n ("List item: " "<li>" str
a3ec4ba0 1186 (if sgml-xml-mode "</li>") \n))))
1caf38eb 1187 (cell `(t
e1940c83 1188 ,@align
1caf38eb
RS
1189 ("valign" ,@valign)
1190 ("colspan" ,@1-9)
1191 ("rowspan" ,@1-9)
1192 ("nowrap" t))))
1193 ;; put ,-expressions first, else byte-compile chokes (as of V19.29)
1194 ;; and like this it's more efficient anyway
1195 `(("a" ,name ,@link)
1196 ("base" t ,@href)
1197 ("dir" ,@list)
d10447ba 1198 ("font" nil "size" ("-1") ("+1") ("-2") ("+2") ,@1-7)
73d25e52 1199 ("form" (\n _ \n "<input type=\"submit\" value=\"\""
a3ec4ba0 1200 (if sgml-xml-mode "/>" ">"))
fcc3195e 1201 ("action" ,@(cdr href)) ("method" ("get") ("post")))
1caf38eb
RS
1202 ("h1" ,@align)
1203 ("h2" ,@align)
1204 ("h3" ,@align)
1205 ("h4" ,@align)
1206 ("h5" ,@align)
1207 ("h6" ,@align)
1208 ("hr" t ("size" ,@1-9) ("width") ("noshade" t) ,@align)
1209 ("img" t ("align" ,@valign ("texttop") ("absmiddle") ("absbottom"))
1210 ("src") ("alt") ("width" "1") ("height" "1")
1211 ("border" "1") ("vspace" "1") ("hspace" "1") ("ismap" t))
1212 ("input" t ("size" ,@1-9) ("maxlength" ,@1-9) ("checked" t) ,name
fcc3195e
RS
1213 ("type" ("text") ("password") ("checkbox") ("radio")
1214 ("submit") ("reset"))
1caf38eb
RS
1215 ("value"))
1216 ("link" t ,@link)
1217 ("menu" ,@list)
d10447ba 1218 ("ol" ,@list ("type" ("A") ("a") ("I") ("i") ("1")))
1caf38eb
RS
1219 ("p" t ,@align)
1220 ("select" (nil \n
1221 ("Text: "
a3ec4ba0 1222 "<option>" str (if sgml-xml-mode "</option>") \n))
1caf38eb
RS
1223 ,name ("size" ,@1-9) ("multiple" t))
1224 ("table" (nil \n
1225 ((completing-read "Cell kind: " '(("td") ("th"))
1226 nil t "t")
73d25e52 1227 "<tr><" str ?> _
a3ec4ba0 1228 (if sgml-xml-mode (concat "<" str "></tr>")) \n))
1caf38eb
RS
1229 ("border" t ,@1-9) ("width" "10") ("cellpadding"))
1230 ("td" ,@cell)
1231 ("textarea" ,name ("rows" ,@1-9) ("cols" ,@1-9))
1232 ("th" ,@cell)
d10447ba 1233 ("ul" ,@list ("type" ("disc") ("circle") ("square")))
1caf38eb
RS
1234
1235 ,@sgml-tag-alist
1236
1237 ("abbrev")
1238 ("acronym")
1239 ("address")
1240 ("array" (nil \n
a3ec4ba0 1241 ("Item: " "<item>" str (if sgml-xml-mode "</item>") \n))
1caf38eb
RS
1242 "align")
1243 ("au")
1244 ("b")
1245 ("big")
1246 ("blink")
1247 ("blockquote" \n)
1248 ("body" \n ("background" ".gif") ("bgcolor" "#") ("text" "#")
1249 ("link" "#") ("alink" "#") ("vlink" "#"))
a3ec4ba0 1250 ("box" (nil _ "<over>" _ (if sgml-xml-mode "</over>")))
1caf38eb
RS
1251 ("br" t ("clear" ("left") ("right")))
1252 ("caption" ("valign" ("top") ("bottom")))
1253 ("center" \n)
1254 ("cite")
1255 ("code" \n)
a3ec4ba0 1256 ("dd" ,(not sgml-xml-mode))
1caf38eb
RS
1257 ("del")
1258 ("dfn")
e1940c83 1259 ("div")
1caf38eb
RS
1260 ("dl" (nil \n
1261 ( "Term: "
a3ec4ba0
SM
1262 "<dt>" str (if sgml-xml-mode "</dt>")
1263 "<dd>" _ (if sgml-xml-mode "</dd>") \n)))
1264 ("dt" (t _ (if sgml-xml-mode "</dt>")
1265 "<dd>" (if sgml-xml-mode "</dd>") \n))
1caf38eb 1266 ("em")
d10447ba 1267 ;("fn" "id" "fn") ; ???
1caf38eb
RS
1268 ("head" \n)
1269 ("html" (\n
1270 "<head>\n"
1271 "<title>" (setq str (read-input "Title: ")) "</title>\n"
5e532c5c 1272 "</head>\n"
1caf38eb
RS
1273 "<body>\n<h1>" str "</h1>\n" _
1274 "\n<address>\n<a href=\"mailto:"
be047262 1275 user-mail-address
5e532c5c
RS
1276 "\">" (user-full-name) "</a>\n</address>\n"
1277 "</body>"
1278 ))
1caf38eb
RS
1279 ("i")
1280 ("ins")
1281 ("isindex" t ("action") ("prompt"))
1282 ("kbd")
1283 ("lang")
a3ec4ba0 1284 ("li" ,(not sgml-xml-mode))
1caf38eb
RS
1285 ("math" \n)
1286 ("nobr")
1287 ("option" t ("value") ("label") ("selected" t))
1288 ("over" t)
1289 ("person")
1290 ("pre" \n)
1291 ("q")
1292 ("rev")
1293 ("s")
1294 ("samp")
1295 ("small")
64367655
SM
1296 ("span" nil
1297 ("class"
1298 ("builtin")
1299 ("comment")
1300 ("constant")
1301 ("function-name")
1302 ("keyword")
1303 ("string")
1304 ("type")
1305 ("variable-name")
1306 ("warning")))
1caf38eb
RS
1307 ("strong")
1308 ("sub")
1309 ("sup")
1310 ("title")
1311 ("tr" t)
1312 ("tt")
1313 ("u")
1314 ("var")
1315 ("wbr" t)))
1316 "*Value of `sgml-tag-alist' for HTML mode.")
1317
1318(defvar html-tag-help
1319 `(,@sgml-tag-help
1320 ("a" . "Anchor of point or link elsewhere")
1321 ("abbrev" . "?")
1322 ("acronym" . "?")
1323 ("address" . "Formatted mail address")
1324 ("array" . "Math array")
1325 ("au" . "?")
1326 ("b" . "Bold face")
1327 ("base" . "Base address for URLs")
1328 ("big" . "Font size")
1329 ("blink" . "Blinking text")
1330 ("blockquote" . "Indented quotation")
1331 ("body" . "Document body")
1332 ("box" . "Math fraction")
1333 ("br" . "Line break")
1334 ("caption" . "Table caption")
1335 ("center" . "Centered text")
1336 ("changed" . "Change bars")
1337 ("cite" . "Citation of a document")
1338 ("code" . "Formatted source code")
1339 ("dd" . "Definition of term")
1340 ("del" . "?")
1341 ("dfn" . "?")
1342 ("dir" . "Directory list (obsolete)")
1343 ("dl" . "Definition list")
1344 ("dt" . "Term to be definined")
b4f05c38 1345 ("em" . "Emphasised")
1caf38eb
RS
1346 ("embed" . "Embedded data in foreign format")
1347 ("fig" . "Figure")
1348 ("figa" . "Figure anchor")
1349 ("figd" . "Figure description")
1350 ("figt" . "Figure text")
d10447ba 1351 ;("fn" . "?") ; ???
1caf38eb
RS
1352 ("font" . "Font size")
1353 ("form" . "Form with input fields")
1354 ("group" . "Document grouping")
1355 ("h1" . "Most important section headline")
1356 ("h2" . "Important section headline")
1357 ("h3" . "Section headline")
1358 ("h4" . "Minor section headline")
1359 ("h5" . "Unimportant section headline")
1360 ("h6" . "Least important section headline")
1361 ("head" . "Document header")
1362 ("hr" . "Horizontal rule")
1363 ("html" . "HTML Document")
1364 ("i" . "Italic face")
1365 ("img" . "Graphic image")
1366 ("input" . "Form input field")
1367 ("ins" . "?")
1368 ("isindex" . "Input field for index search")
1369 ("kbd" . "Keybard example face")
1370 ("lang" . "Natural language")
1371 ("li" . "List item")
1372 ("link" . "Link relationship")
1373 ("math" . "Math formula")
1374 ("menu" . "Menu list (obsolete)")
1375 ("mh" . "Form mail header")
1376 ("nextid" . "Allocate new id")
1377 ("nobr" . "Text without line break")
1378 ("ol" . "Ordered list")
1379 ("option" . "Selection list item")
1380 ("over" . "Math fraction rule")
1381 ("p" . "Paragraph start")
1382 ("panel" . "Floating panel")
1383 ("person" . "?")
1384 ("pre" . "Preformatted fixed width text")
1385 ("q" . "?")
1386 ("rev" . "Reverse video")
1387 ("s" . "?")
1388 ("samp" . "Sample text")
1389 ("select" . "Selection list")
1390 ("small" . "Font size")
1391 ("sp" . "Nobreak space")
1392 ("strong" . "Standout text")
1393 ("sub" . "Subscript")
1394 ("sup" . "Superscript")
1395 ("table" . "Table with rows and columns")
1396 ("tb" . "Table vertical break")
1397 ("td" . "Table data cell")
1398 ("textarea" . "Form multiline edit area")
1399 ("th" . "Table header cell")
1400 ("title" . "Document title")
1401 ("tr" . "Table row separator")
1402 ("tt" . "Typewriter face")
1403 ("u" . "Underlined text")
1404 ("ul" . "Unordered list")
1405 ("var" . "Math variable face")
1406 ("wbr" . "Enable <br> within <nobr>"))
1407"*Value of `sgml-tag-help' for HTML mode.")
3bf0b727 1408\f
1caf38eb 1409;;;###autoload
64367655 1410(define-derived-mode html-mode sgml-mode "HTML"
1caf38eb 1411 "Major mode based on SGML mode for editing HTML documents.
7be38f7d 1412This allows inserting skeleton constructs used in hypertext documents with
fcc3195e
RS
1413completion. See below for an introduction to HTML. Use
1414\\[browse-url-of-buffer] to see how this comes out. See also `sgml-mode' on
1415which this is based.
1caf38eb 1416
fcc3195e 1417Do \\[describe-variable] html- SPC and \\[describe-variable] sgml- SPC to see available variables.
1caf38eb
RS
1418
1419To write fairly well formatted pages you only need to know few things. Most
1420browsers have a function to read the source code of the page being seen, so
1421you can imitate various tricks. Here's a very short HTML primer which you
1422can also view with a browser to see what happens:
1423
1424<title>A Title Describing Contents</title> should be on every page. Pages can
1425have <h1>Very Major Headlines</h1> through <h6>Very Minor Headlines</h6>
1426<hr> Parts can be separated with horizontal rules.
1427
1428<p>Paragraphs only need an opening tag. Line breaks and multiple spaces are
1429ignored unless the text is <pre>preformatted.</pre> Text can be marked as
1430<b>bold</b>, <i>italic</i> or <u>underlined</u> using the normal M-g or
1431Edit/Text Properties/Face commands.
1432
1433Pages can have <a name=\"SOMENAME\">named points</a> and can link other points
1434to them with <a href=\"#SOMENAME\">see also somename</a>. In the same way <a
1435href=\"URL\">see also URL</a> where URL is a filename relative to current
f788776c 1436directory, or absolute as in `http://www.cs.indiana.edu/elisp/w3/docs.html'.
1caf38eb
RS
1437
1438Images in many formats can be inlined with <img src=\"URL\">.
1439
f788776c
RS
1440If you mainly create your own documents, `sgml-specials' might be
1441interesting. But note that some HTML 2 browsers can't handle `&apos;'.
1442To work around that, do:
1443 (eval-after-load \"sgml-mode\" '(aset sgml-char-names ?' nil))
1caf38eb 1444
1caf38eb 1445\\{html-mode-map}"
64367655
SM
1446 (set (make-local-variable 'sgml-display-text) html-display-text)
1447 (set (make-local-variable 'sgml-tag-face-alist) html-tag-face-alist)
1caf38eb
RS
1448 (make-local-variable 'sgml-tag-alist)
1449 (make-local-variable 'sgml-face-tag-alist)
1450 (make-local-variable 'sgml-tag-help)
1451 (make-local-variable 'outline-regexp)
1452 (make-local-variable 'outline-heading-end-regexp)
1453 (make-local-variable 'outline-level)
da84bdc4
RS
1454 (make-local-variable 'sentence-end)
1455 (setq sentence-end
b8b14971
DL
1456 (if sentence-end-double-space
1457 "[.?!][]\"')}]*\\(<[^>]*>\\)*\\($\\| $\\|\t\\| \\)[ \t\n]*"
64367655 1458 "[.?!][]\"')}]*\\(<[^>]*>\\)*\\($\\|[ \t]\\)[ \t\n]*"))
a01588fc 1459 (setq sgml-tag-alist html-tag-alist
1caf38eb
RS
1460 sgml-face-tag-alist html-face-tag-alist
1461 sgml-tag-help html-tag-help
1462 outline-regexp "^.*<[Hh][1-6]\\>"
1463 outline-heading-end-regexp "</[Hh][1-6]>"
1464 outline-level (lambda ()
0fda8eff 1465 (char-before (match-end 0))))
3bf0b727 1466 (setq imenu-create-index-function 'html-imenu-index)
a3ec4ba0 1467 (when sgml-xml-mode (setq mode-name "XHTML"))
73d25e52 1468 (set (make-local-variable 'sgml-empty-tags)
5f3d924d
SM
1469 ;; From HTML-4.01's loose.dtd, parsed with `sgml-parse-dtd',
1470 ;; plus manual addition of "wbr".
1471 '("area" "base" "basefont" "br" "col" "frame" "hr" "img" "input"
1472 "isindex" "link" "meta" "param" "wbr"))
1473 (set (make-local-variable 'sgml-unclosed-tags)
1474 ;; From HTML-4.01's loose.dtd, parsed with `sgml-parse-dtd'.
1475 '("body" "colgroup" "dd" "dt" "head" "html" "li" "option"
1476 "p" "tbody" "td" "tfoot" "th" "thead" "tr"))
e1940c83
SM
1477 ;; It's for the user to decide if it defeats it or not -stef
1478 ;; (make-local-variable 'imenu-sort-function)
1479 ;; (setq imenu-sort-function nil) ; sorting the menu defeats the purpose
64367655 1480 )
3bf0b727
RS
1481\f
1482(defvar html-imenu-regexp
1483 "\\s-*<h\\([1-9]\\)[^\n<>]*>\\(<[^\n<>]*>\\)*\\s-*\\([^\n<>]*\\)"
1484 "*A regular expression matching a head line to be added to the menu.
1485The first `match-string' should be a number from 1-9.
1486The second `match-string' matches extra tags and is ignored.
1487The third `match-string' will be the used in the menu.")
1488
1489(defun html-imenu-index ()
1490 "Return an table of contents for an HTML buffer for use with Imenu."
1491 (let (toc-index)
1492 (save-excursion
1493 (goto-char (point-min))
1494 (while (re-search-forward html-imenu-regexp nil t)
1495 (setq toc-index
1496 (cons (cons (concat (make-string
1497 (* 2 (1- (string-to-number (match-string 1))))
1498 ?\ )
1499 (match-string 3))
5950e029 1500 (line-beginning-position))
3bf0b727
RS
1501 toc-index))))
1502 (nreverse toc-index)))
1caf38eb 1503
3bf0b727 1504(defun html-autoview-mode (&optional arg)
d4c89075 1505 "Toggle automatic viewing via `browse-url-of-buffer' upon saving buffer.
3bf0b727
RS
1506With positive prefix ARG always turns viewing on, with negative ARG always off.
1507Can be used as a value for `html-mode-hook'."
1508 (interactive "P")
1509 (if (setq arg (if arg
1510 (< (prefix-numeric-value arg) 0)
1511 (and (boundp 'after-save-hook)
1512 (memq 'browse-url-of-buffer after-save-hook))))
1513 (setq after-save-hook (delq 'browse-url-of-buffer after-save-hook))
3bf0b727
RS
1514 (add-hook 'after-save-hook 'browse-url-of-buffer nil t))
1515 (message "Autoviewing turned %s."
1516 (if arg "off" "on")))
1517\f
1caf38eb
RS
1518(define-skeleton html-href-anchor
1519 "HTML anchor tag with href attribute."
a391b179
RS
1520 "URL: "
1521 '(setq input "http:")
1522 "<a href=\"" str "\">" _ "</a>")
1caf38eb
RS
1523
1524(define-skeleton html-name-anchor
1525 "HTML anchor tag with name attribute."
a391b179
RS
1526 "Name: "
1527 "<a name=\"" str "\">" _ "</a>")
1caf38eb 1528
7e49eef2
RS
1529(define-skeleton html-headline-1
1530 "HTML level 1 headline tags."
1531 nil
1532 "<h1>" _ "</h1>")
1533
1534(define-skeleton html-headline-2
1535 "HTML level 2 headline tags."
1536 nil
1537 "<h2>" _ "</h2>")
1538
1539(define-skeleton html-headline-3
1540 "HTML level 3 headline tags."
1541 nil
1542 "<h3>" _ "</h3>")
1543
1544(define-skeleton html-headline-4
1545 "HTML level 4 headline tags."
1546 nil
1547 "<h4>" _ "</h4>")
1548
1549(define-skeleton html-headline-5
1550 "HTML level 5 headline tags."
1551 nil
1552 "<h5>" _ "</h5>")
1553
1554(define-skeleton html-headline-6
1555 "HTML level 6 headline tags."
1556 nil
1557 "<h6>" _ "</h6>")
1caf38eb
RS
1558
1559(define-skeleton html-horizontal-rule
1560 "HTML horizontal rule tag."
1561 nil
a3ec4ba0 1562 (if sgml-xml-mode "<hr/>" "<hr>") \n)
1caf38eb
RS
1563
1564(define-skeleton html-image
1565 "HTML image tag."
1566 nil
b4f05c38 1567 "<img src=\"" _ "\""
a3ec4ba0 1568 (if sgml-xml-mode "/>" ">"))
1caf38eb
RS
1569
1570(define-skeleton html-line
1571 "HTML line break tag."
1572 nil
a3ec4ba0 1573 (if sgml-xml-mode "<br/>" "<br>") \n)
1caf38eb 1574
7e49eef2
RS
1575(define-skeleton html-ordered-list
1576 "HTML ordered list tags."
1577 nil
a391b179 1578 "<ol>" \n
a3ec4ba0 1579 "<li>" _ (if sgml-xml-mode "</li>") \n
7e49eef2
RS
1580 "</ol>")
1581
1582(define-skeleton html-unordered-list
1583 "HTML unordered list tags."
1584 nil
a391b179 1585 "<ul>" \n
a3ec4ba0 1586 "<li>" _ (if sgml-xml-mode "</li>") \n
7e49eef2 1587 "</ul>")
1caf38eb
RS
1588
1589(define-skeleton html-list-item
1590 "HTML list item tag."
1591 nil
1592 (if (bolp) nil '\n)
a3ec4ba0 1593 "<li>" _ (if sgml-xml-mode "</li>"))
1caf38eb
RS
1594
1595(define-skeleton html-paragraph
1596 "HTML paragraph tag."
1597 nil
1598 (if (bolp) nil ?\n)
a3ec4ba0 1599 \n "<p>" _ (if sgml-xml-mode "</p>"))
1caf38eb 1600
fcc3195e
RS
1601(define-skeleton html-checkboxes
1602 "Group of connected checkbox inputs."
1603 nil
a391b179
RS
1604 '(setq v1 nil
1605 v2 nil)
1606 ("Value: "
d10447ba 1607 "<input type=\"" (identity "checkbox") ; see comment above about identity
a391b179 1608 "\" name=\"" (or v1 (setq v1 (skeleton-read "Name: ")))
fcc3195e 1609 "\" value=\"" str ?\"
b4f05c38
SS
1610 (when (y-or-n-p "Set \"checked\" attribute? ")
1611 (funcall skeleton-transformation " checked"))
a3ec4ba0 1612 (if sgml-xml-mode "/>" ">")
a391b179
RS
1613 (skeleton-read "Text: " (capitalize str))
1614 (or v2 (setq v2 (if (y-or-n-p "Newline after text? ")
b4f05c38 1615 (funcall skeleton-transformation
a3ec4ba0 1616 (if sgml-xml-mode "<br/>" "<br>"))
a391b179
RS
1617 "")))
1618 \n))
fcc3195e 1619
1caf38eb
RS
1620(define-skeleton html-radio-buttons
1621 "Group of connected radio button inputs."
1622 nil
a391b179
RS
1623 '(setq v1 nil
1624 v2 (cons nil nil))
1625 ("Value: "
d10447ba 1626 "<input type=\"" (identity "radio") ; see comment above about identity
a391b179 1627 "\" name=\"" (or (car v2) (setcar v2 (skeleton-read "Name: ")))
1caf38eb 1628 "\" value=\"" str ?\"
b4f05c38
SS
1629 (when (and (not v1) (setq v1 (y-or-n-p "Set \"checked\" attribute? ")))
1630 (funcall skeleton-transformation " checked"))
a3ec4ba0 1631 (if sgml-xml-mode "/>" ">")
a391b179
RS
1632 (skeleton-read "Text: " (capitalize str))
1633 (or (cdr v2) (setcdr v2 (if (y-or-n-p "Newline after text? ")
b4f05c38 1634 (funcall skeleton-transformation
a3ec4ba0 1635 (if sgml-xml-mode "<br/>" "<br>"))
a391b179
RS
1636 "")))
1637 \n))
1caf38eb 1638
e1940c83 1639(provide 'sgml-mode)
6a05d05f 1640
72c0ae01 1641;;; sgml-mode.el ends here