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