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