Add defgroup; use defcustom for user vars.
[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)
336 facemenu-add-face-function
337 (lambda (face end)
338 (if (setq face (cdr (assq face sgml-face-tag-alist)))
339 (progn
340 (setq facemenu-end-add-face (concat "</" face ">"))
341 (concat "<" face ">"))
342 (error "Face not configured for %s mode." mode-name))))
343 (while sgml-display-text
344 (put (car (car sgml-display-text)) 'before-string
345 (cdr (car sgml-display-text)))
346 (setq sgml-display-text (cdr sgml-display-text)))
72c0ae01
ER
347 (run-hooks 'text-mode-hook 'sgml-mode-hook))
348
1caf38eb
RS
349
350;;;###autoload
351(defun sgml-mode (&optional function)
352 "Major mode for editing SGML documents.
353Makes > match <. Makes / blink matching /.
fcc3195e
RS
354Keys <, &, SPC within <>, \" and ' can be electric depending on
355`sgml-quick-keys'.
1caf38eb
RS
356
357Do \\[describe-variable] sgml- SPC to see available variables.
358
359Use \\[sgml-validate] to validate your document with an SGML parser.
360\\{sgml-mode-map}"
361 (interactive)
362 (sgml-mode-common sgml-tag-face-alist sgml-display-text)
363 (use-local-map sgml-mode-map)
364 (setq mode-name "SGML"
365 major-mode 'sgml-mode))
366
367
368
72c0ae01
ER
369(defun sgml-comment-indent ()
370 (if (and (looking-at "--")
1caf38eb 371 (not (and (eq (preceding-char) ?!)
72c0ae01
ER
372 (eq (char-after (- (point) 2)) ?<))))
373 (progn
374 (skip-chars-backward " \t")
375 (max comment-column (1+ (current-column))))
376 0))
377
72c0ae01 378
72c0ae01
ER
379
380(defun sgml-slash (arg)
381 "Insert / and display any previous matching /.
382Two /s are treated as matching if the first / ends a net-enabling
383start tag, and the second / is the corresponding null end tag."
384 (interactive "p")
385 (insert-char ?/ arg)
386 (if (> arg 0)
387 (let ((oldpos (point))
388 (blinkpos)
389 (level 0))
390 (save-excursion
391 (save-restriction
392 (if sgml-slash-distance
393 (narrow-to-region (max (point-min)
394 (- (point) sgml-slash-distance))
395 oldpos))
396 (if (and (re-search-backward sgml-start-tag-regex (point-min) t)
397 (eq (match-end 0) (1- oldpos)))
398 ()
399 (goto-char (1- oldpos))
400 (while (and (not blinkpos)
401 (search-backward "/" (point-min) t))
402 (let ((tagend (save-excursion
403 (if (re-search-backward sgml-start-tag-regex
404 (point-min) t)
405 (match-end 0)
406 nil))))
407 (if (eq tagend (point))
408 (if (eq level 0)
409 (setq blinkpos (point))
410 (setq level (1- level)))
411 (setq level (1+ level)))))))
412 (if blinkpos
413 (progn
414 (goto-char blinkpos)
415 (if (pos-visible-in-window-p)
416 (sit-for 1)
417 (message "Matches %s"
418 (buffer-substring (progn
419 (beginning-of-line)
420 (point))
421 (1+ blinkpos))))))))))
422
1caf38eb
RS
423
424(defun sgml-name-char (&optional char)
425 "Insert a symbolic character name according to `sgml-char-names'.
4268 bit chars may be inserted with the meta key as in M-SPC for no break space,
427or M-- for a soft hyphen."
428 (interactive "*")
429 (insert ?&)
430 (or char
431 (setq char (read-quoted-char)))
432 (delete-backward-char 1)
433 (insert char)
434 (undo-boundary)
435 (delete-backward-char 1)
436 (insert ?&
437 (or (aref sgml-char-names char)
438 (format "#%d" char))
439 ?\;))
440
441
442(defun sgml-name-self ()
443 "Insert a symbolic character name according to `sgml-char-names'."
444 (interactive "*")
445 (sgml-name-char last-command-char))
446
447
448(defun sgml-maybe-name-self ()
449 "Insert a symbolic character name according to `sgml-char-names'."
450 (interactive "*")
451 (if sgml-name-8bit-mode
452 (sgml-name-char last-command-char)
453 (self-insert-command 1)))
454
455
456(defun sgml-name-8bit-mode ()
457 "Toggle insertion of 8 bit characters."
458 (interactive)
459 (setq sgml-name-8bit-mode (not sgml-name-8bit-mode)))
460
461
462
463(define-skeleton sgml-tag
464 "Insert a tag you are prompted for, optionally with attributes.
465Completion and configuration is according to `sgml-tag-alist'.
466If you like tags and attributes in uppercase set `skeleton-transformation'
467to `upcase'."
468 (funcall skeleton-transformation
469 (completing-read "Tag: " sgml-tag-alist))
470 ?< (setq v1 (eval str)) |
471 (("") -1 '(undo-boundary) "&lt;") |
472 (("") '(setq v2 (sgml-attributes v1 t)) ?>
fcc3195e
RS
473 (if (string= "![" v1)
474 (prog1 '(("") " [ " _ " ]]")
475 (backward-char))
476 (if (or (eq v2 t)
477 (string-match "^[/!?]" v1))
478 ()
479 (if (symbolp v2)
480 '(("") v2 _ v2 "</" v1 ?>)
481 (if (eq (car v2) t)
482 (cons '("") (cdr v2))
483 (append '(("") (car v2))
484 (cdr v2)
485 '(resume: (car v2) _ "</" v1 ?>))))))))
1caf38eb
RS
486
487(autoload 'skeleton-read "skeleton")
488
489(defun sgml-attributes (alist &optional quiet)
490 "When at toplevel of a tag, interactively insert attributes."
491 (interactive (list (save-excursion (sgml-beginning-of-tag t))))
492 (or (stringp alist) (error "Wrong context for adding attribute"))
493 (if alist
494 (let ((completion-ignore-case t)
495 car attribute i)
496 (setq alist (cdr (assoc (downcase alist) sgml-tag-alist)))
497 (if (or (symbolp (car alist))
498 (symbolp (car (car alist))))
499 (setq car (car alist)
500 alist (cdr alist)))
501 (or quiet
502 (message "No attributes configured."))
503 (if (stringp (car alist))
504 (progn
505 (insert (if (eq (preceding-char) ? ) "" ? ) (car alist))
506 (sgml-value alist))
507 (setq i (length alist))
508 (while (> i 0)
509 (insert ? )
510 (insert (funcall skeleton-transformation
511 (setq attribute
512 (skeleton-read '(completing-read
513 "[Attribute]: "
514 alist)))))
515 (if (string= "" attribute)
516 (setq i 0)
517 (sgml-value (assoc attribute alist))
518 (setq i (1- i))))
519 (if (eq (preceding-char) ? )
520 (delete-backward-char 1)))
521 car)))
522
523(defun sgml-auto-attributes (arg)
524 "Self insert, except, when at top level of tag, prompt for attributes.
fcc3195e 525With prefix ARG only self insert."
1caf38eb
RS
526 (interactive "*P")
527 (let ((point (point))
528 tag)
529 (if (or arg
1caf38eb
RS
530 (not sgml-tag-alist) ; no message when nothing configured
531 (symbolp (setq tag (save-excursion (sgml-beginning-of-tag t))))
532 (eq (aref tag 0) ?/))
533 (self-insert-command (prefix-numeric-value arg))
534 (sgml-attributes tag)
535 (setq last-command-char ? )
536 (or (> (point) point)
537 (self-insert-command 1)))))
538
539
540(defun sgml-tag-help (&optional tag)
541 "Display description of optional TAG or tag at point."
542 (interactive)
543 (or tag
544 (save-excursion
545 (if (eq (following-char) ?<)
546 (forward-char))
547 (setq tag (sgml-beginning-of-tag))))
548 (or (stringp tag)
549 (error "No tag selected"))
550 (setq tag (downcase tag))
f68f40e0
KH
551 (message "%s"
552 (or (cdr (assoc tag sgml-tag-help))
1caf38eb
RS
553 (and (eq (aref tag 0) ?/)
554 (cdr (assoc (substring tag 1) sgml-tag-help)))
555 "No description available")))
556
557
558(defun sgml-maybe-end-tag ()
559 "Name self unless in position to end a tag."
560 (interactive)
561 (or (condition-case nil
562 (save-excursion (up-list -1))
563 (error
564 (sgml-name-self)
565 t))
566 (condition-case nil
567 (progn
568 (save-excursion (up-list 1))
569 (sgml-name-self))
570 (error (self-insert-command 1)))))
571
572
573(defun sgml-skip-tag-backward (arg)
574 "Skip to beginning of tag or matching opening tag if present.
575With prefix ARG, repeat that many times."
576 (interactive "p")
577 (while (>= arg 1)
578 (search-backward "<" nil t)
579 (if (looking-at "</\\([^ \n\t>]+\\)")
580 ;; end tag, skip any nested pairs
581 (let ((case-fold-search t)
582 (re (concat "</?" (regexp-quote (match-string 1)))))
583 (while (and (re-search-backward re nil t)
584 (eq (char-after (1+ (point))) ?/))
585 (forward-char 1)
586 (sgml-skip-tag-backward 1))))
587 (setq arg (1- arg))))
588
589(defun sgml-skip-tag-forward (arg &optional return)
590 "Skip to end of tag or matching closing tag if present.
591With prefix ARG, repeat that many times.
592Return t iff after a closing tag."
593 (interactive "p")
594 (setq return t)
595 (while (>= arg 1)
596 (skip-chars-forward "^<>")
597 (if (eq (following-char) ?>)
598 (up-list -1))
599 (if (looking-at "<\\([^/ \n\t>]+\\)")
600 ;; start tag, skip any nested same pairs _and_ closing tag
601 (let ((case-fold-search t)
602 (re (concat "</?" (regexp-quote (match-string 1))))
603 point close)
604 (forward-list 1)
605 (setq point (point))
606 (while (and (re-search-forward re nil t)
607 (not (setq close
608 (eq (char-after (1+ (match-beginning 0))) ?/)))
609 (not (up-list -1))
610 (sgml-skip-tag-forward 1))
611 (setq close nil))
612 (if close
613 (up-list 1)
614 (goto-char point)
615 (setq return)))
616 (forward-list 1))
617 (setq arg (1- arg)))
618 return)
619
620(defun sgml-delete-tag (arg)
621 "Delete tag on or after cursor, and matching closing or opening tag.
622With prefix ARG, repeat that many times."
623 (interactive "p")
624 (while (>= arg 1)
625 (save-excursion
626 (let* (close open)
fcc3195e 627 (if (looking-at "[ \t\n]*<")
1caf38eb
RS
628 ;; just before tag
629 (if (eq (char-after (match-end 0)) ?/)
630 ;; closing tag
631 (progn
632 (setq close (point))
633 (goto-char (match-end 0))))
634 ;; on tag?
635 (or (save-excursion (setq close (sgml-beginning-of-tag)
636 close (and (stringp close)
637 (eq (aref close 0) ?/)
638 (point))))
639 ;; not on closing tag
640 (let ((point (point)))
641 (sgml-skip-tag-backward 1)
642 (if (or (not (eq (following-char) ?<))
643 (save-excursion
644 (forward-list 1)
645 (<= (point) point)))
646 (error "Not on or before tag")))))
647 (if close
648 (progn
649 (sgml-skip-tag-backward 1)
650 (setq open (point))
651 (goto-char close)
652 (kill-sexp 1))
653 (setq open (point))
654 (sgml-skip-tag-forward 1)
655 (backward-list)
656 (forward-char)
657 (if (eq (aref (sgml-beginning-of-tag) 0) ?/)
658 (kill-sexp 1)))
659 (goto-char open)
660 (kill-sexp 1)))
661 (setq arg (1- arg))))
662
663
664
665(defun sgml-tags-invisible (arg)
666 "Toggle visibility of existing tags."
667 (interactive "P")
668 (let ((modified (buffer-modified-p))
669 (inhibit-read-only t)
670 (point (point-min))
671 symbol)
672 (save-excursion
673 (goto-char point)
674 (if (setq sgml-tags-invisible
675 (if arg
676 (>= (prefix-numeric-value arg) 0)
677 (not sgml-tags-invisible)))
678 (while (re-search-forward "<\\([!/?A-Za-z][-A-Za-z0-9]*\\)"
679 nil t)
680 (setq symbol (intern-soft (downcase (match-string 1))))
681 (goto-char (match-beginning 0))
682 (and (get symbol 'before-string)
683 (not (overlays-at (point)))
684 (overlay-put (make-overlay (point)
685 (match-beginning 1))
686 'category symbol))
687 (put-text-property (setq point (point)) (forward-list)
688 'intangible (point))
689 (put-text-property point (point)
690 'category 'sgml-tag))
691 (while (< (setq point (next-overlay-change point)) (point-max))
692 (delete-overlay (car (overlays-at point))))
693 (remove-text-properties (point-min) (point-max)
694 '(category sgml-tag intangible t))))
695 (set-buffer-modified-p modified)
696 (run-hooks 'sgml-tags-invisible-hook)
697 (message "")))
698
699(defun sgml-point-entered (x y)
700 ;; Show preceding or following hidden tag, depending of cursor direction.
701 (let ((inhibit-point-motion-hooks t))
702 (save-excursion
703 (message "Invisible tag: %s"
704 (buffer-substring
705 (point)
706 (if (or (and (> x y)
707 (not (eq (following-char) ?<)))
708 (and (< x y)
709 (eq (preceding-char) ?>)))
710 (backward-list)
711 (forward-list)))))))
712
713
714(autoload 'compile-internal "compile")
715
72c0ae01
ER
716(defun sgml-validate (command)
717 "Validate an SGML document.
718Runs COMMAND, a shell command, in a separate process asynchronously
719with output going to the buffer *compilation*.
720You can then use the command \\[next-error] to find the next error message
721and move to the line in the SGML document that caused it."
722 (interactive
723 (list (read-string "Validate command: "
724 (or sgml-saved-validate-command
725 (concat sgml-validate-command
726 " "
727 (let ((name (buffer-file-name)))
728 (and name
729 (file-name-nondirectory name))))))))
730 (setq sgml-saved-validate-command command)
0afdd8d0
RS
731 (if (or (not compilation-ask-about-save)
732 (y-or-n-p (message "Save buffer %s? " (buffer-name))))
733 (save-buffer))
c7aa4667 734 (compile-internal command "No more errors"))
72c0ae01 735
1caf38eb
RS
736
737(defun sgml-beginning-of-tag (&optional top-level)
738 "Skip to beginning of tag and return its name.
739Else `t'."
740 (or (if top-level
741 (condition-case nil
742 (up-list -1)
743 (error t))
744 (>= (point)
745 (if (search-backward "<" nil t)
746 (save-excursion
747 (forward-list)
748 (point))
749 0)))
750 (if (looking-at "<[!?/]?[[A-Za-z][A-Za-z0-9]*")
751 (buffer-substring-no-properties
752 (1+ (point))
753 (match-end 0))
754 t)))
755
756(defun sgml-value (alist)
757 (setq alist (cdr alist))
758 (if (stringp (car alist))
759 (insert "=\"" (car alist) ?\")
760 (if (eq (car alist) t)
761 (if (cdr alist)
762 (progn
763 (insert "=\"")
764 (setq alist (skeleton-read '(completing-read
765 "[Value]: " (cdr alist))))
766 (if (string< "" alist)
767 (insert (funcall skeleton-transformation alist) ?\")
768 (delete-backward-char 2))))
769 (insert "=\"")
770 (if alist
771 (insert (funcall skeleton-transformation
772 (skeleton-read '(completing-read "Value: " alist)))))
773 (insert ?\"))))
774
775(provide 'sgml-mode)
776\f
fcc3195e 777(defvar html-quick-keys sgml-quick-keys
b1e7bb48 778 "Use C-c X combinations for quick insertion of frequent tags when non-nil.
fcc3195e 779This defaults to `sgml-quick-keys'.
1caf38eb
RS
780This takes effect when first loading the library.")
781
782(defvar html-mode-map
783 (let ((map (nconc (make-sparse-keymap) sgml-mode-map))
784 (menu-map (make-sparse-keymap "HTML")))
7e49eef2
RS
785 (define-key map "\C-c6" 'html-headline-6)
786 (define-key map "\C-c5" 'html-headline-5)
787 (define-key map "\C-c4" 'html-headline-4)
788 (define-key map "\C-c3" 'html-headline-3)
789 (define-key map "\C-c2" 'html-headline-2)
790 (define-key map "\C-c1" 'html-headline-1)
fcc3195e
RS
791 (define-key map "\C-c\r" 'html-paragraph)
792 (define-key map "\C-c\n" 'html-line)
793 (define-key map "\C-c\C-c-" 'html-horizontal-rule)
7e49eef2
RS
794 (define-key map "\C-c\C-co" 'html-ordered-list)
795 (define-key map "\C-c\C-cu" 'html-unordered-list)
fcc3195e
RS
796 (define-key map "\C-c\C-cr" 'html-radio-buttons)
797 (define-key map "\C-c\C-cc" 'html-checkboxes)
798 (define-key map "\C-c\C-cl" 'html-list-item)
799 (define-key map "\C-c\C-ch" 'html-href-anchor)
800 (define-key map "\C-c\C-cn" 'html-name-anchor)
801 (define-key map "\C-c\C-ci" 'html-image)
1caf38eb
RS
802 (if html-quick-keys
803 (progn
1caf38eb 804 (define-key map "\C-c-" 'html-horizontal-rule)
7e49eef2
RS
805 (define-key map "\C-co" 'html-ordered-list)
806 (define-key map "\C-cu" 'html-unordered-list)
1caf38eb 807 (define-key map "\C-cr" 'html-radio-buttons)
fcc3195e 808 (define-key map "\C-cc" 'html-checkboxes)
1caf38eb
RS
809 (define-key map "\C-cl" 'html-list-item)
810 (define-key map "\C-ch" 'html-href-anchor)
811 (define-key map "\C-cn" 'html-name-anchor)
812 (define-key map "\C-ci" 'html-image)))
813 (define-key map "\C-c\C-s" 'html-autoview-mode)
814 (define-key map "\C-c\C-v" 'browse-url-of-buffer)
815 (define-key map [menu-bar html] (cons "HTML" menu-map))
816 (define-key menu-map [html-autoview-mode]
817 '("Toggle Autoviewing" . html-autoview-mode))
818 (define-key menu-map [browse-url-of-buffer]
819 '("View Buffer Contents" . browse-url-of-buffer))
820 (define-key menu-map [nil] '("--"))
7e49eef2
RS
821 ;;(define-key menu-map "6" '("Heading 6" . html-headline-6))
822 ;;(define-key menu-map "5" '("Heading 5" . html-headline-5))
823 ;;(define-key menu-map "4" '("Heading 4" . html-headline-4))
824 (define-key menu-map "3" '("Heading 3" . html-headline-3))
825 (define-key menu-map "2" '("Heading 2" . html-headline-2))
826 (define-key menu-map "1" '("Heading 1" . html-headline-1))
1caf38eb 827 (define-key menu-map "l" '("Radio Buttons" . html-radio-buttons))
fcc3195e 828 (define-key menu-map "c" '("Checkboxes" . html-checkboxes))
1caf38eb 829 (define-key menu-map "l" '("List Item" . html-list-item))
7e49eef2
RS
830 (define-key menu-map "u" '("Unordered List" . html-unordered-list))
831 (define-key menu-map "o" '("Ordered List" . html-ordered-list))
fcc3195e 832 (define-key menu-map "-" '("Horizontal Rule" . html-horizontal-rule))
1caf38eb
RS
833 (define-key menu-map "\n" '("Line Break" . html-line))
834 (define-key menu-map "\r" '("Paragraph" . html-paragraph))
835 (define-key menu-map "i" '("Image" . html-image))
836 (define-key menu-map "h" '("Href Anchor" . html-href-anchor))
837 (define-key menu-map "n" '("Name Anchor" . html-name-anchor))
838 map)
839 "Keymap for commands for use in HTML mode.")
840
841
842(defvar html-face-tag-alist
843 '((bold . "b")
844 (italic . "i")
845 (underline . "u")
846 (modeline . "rev"))
847 "Value of `sgml-face-tag-alist' for HTML mode.")
848
849(defvar html-tag-face-alist
850 '(("b" . bold)
851 ("big" . bold)
852 ("blink" . highlight)
853 ("cite" . italic)
854 ("em" . italic)
855 ("h1" bold underline)
856 ("h2" bold-italic underline)
857 ("h3" italic underline)
858 ("h4" . underline)
859 ("h5" . underline)
860 ("h6" . underline)
861 ("i" . italic)
862 ("rev" . modeline)
863 ("s" . underline)
864 ("small" . default)
865 ("strong" . bold)
866 ("title" bold underline)
867 ("tt" . default)
868 ("u" . underline)
869 ("var" . italic))
870 "Value of `sgml-tag-face-alist' for HTML mode.")
871
872
873(defvar html-display-text
874 '((img . "[/]")
875 (hr . "----------")
876 (li . "o "))
877 "Value of `sgml-display-text' for HTML mode.")
878
879
880; should code exactly HTML 3 here when that is finished
881(defvar html-tag-alist
882 (let* ((1-9 '(("8") ("9")
883 ("1") ("2") ("3") ("4") ("5") ("6") ("7")))
884 (align '(("align" ("left") ("center") ("right"))))
885 (valign '(("top") ("middle") ("bottom") ("baseline")))
886 (rel '(("next") ("previous") ("parent") ("subdocument") ("made")))
887 (href '("href" ("ftp:") ("file:") ("finger:") ("gopher:") ("http:")
888 ("mailto:") ("news:") ("rlogin:") ("telnet:") ("tn3270:")
fcc3195e 889 ("wais:") ("/cgi-bin/")))
1caf38eb
RS
890 (name '("name"))
891 (link `(,href
892 ("rel" ,@rel)
893 ("rev" ,@rel)
894 ("title")))
895 (list '((nil \n
896 ( "List item: "
897 "<li>" str \n))
898 ("type" ("A") ("a") ("I") ("i") ("1"))))
899 (cell `(t
900 ,align
901 ("valign" ,@valign)
902 ("colspan" ,@1-9)
903 ("rowspan" ,@1-9)
904 ("nowrap" t))))
905 ;; put ,-expressions first, else byte-compile chokes (as of V19.29)
906 ;; and like this it's more efficient anyway
907 `(("a" ,name ,@link)
908 ("base" t ,@href)
909 ("dir" ,@list)
5e70d78d 910 ("font" ("size" ("-1") ("+1") ("-2") ("+2") ,@(cdr (cdr 1-9))))
fcc3195e
RS
911 ("form" (\n _ \n "<input type=\"submit\" value=\"\">")
912 ("action" ,@(cdr href)) ("method" ("get") ("post")))
1caf38eb
RS
913 ("h1" ,@align)
914 ("h2" ,@align)
915 ("h3" ,@align)
916 ("h4" ,@align)
917 ("h5" ,@align)
918 ("h6" ,@align)
919 ("hr" t ("size" ,@1-9) ("width") ("noshade" t) ,@align)
920 ("img" t ("align" ,@valign ("texttop") ("absmiddle") ("absbottom"))
921 ("src") ("alt") ("width" "1") ("height" "1")
922 ("border" "1") ("vspace" "1") ("hspace" "1") ("ismap" t))
923 ("input" t ("size" ,@1-9) ("maxlength" ,@1-9) ("checked" t) ,name
fcc3195e
RS
924 ("type" ("text") ("password") ("checkbox") ("radio")
925 ("submit") ("reset"))
1caf38eb
RS
926 ("value"))
927 ("link" t ,@link)
928 ("menu" ,@list)
929 ("ol" ,@list)
930 ("p" t ,@align)
931 ("select" (nil \n
932 ("Text: "
933 "<option>" str \n))
934 ,name ("size" ,@1-9) ("multiple" t))
935 ("table" (nil \n
936 ((completing-read "Cell kind: " '(("td") ("th"))
937 nil t "t")
938 "<tr><" str ?> _ \n))
939 ("border" t ,@1-9) ("width" "10") ("cellpadding"))
940 ("td" ,@cell)
941 ("textarea" ,name ("rows" ,@1-9) ("cols" ,@1-9))
942 ("th" ,@cell)
943 ("ul" ,@list)
944
945 ,@sgml-tag-alist
946
947 ("abbrev")
948 ("acronym")
949 ("address")
950 ("array" (nil \n
951 ("Item: " "<item>" str \n))
952 "align")
953 ("au")
954 ("b")
955 ("big")
956 ("blink")
957 ("blockquote" \n)
958 ("body" \n ("background" ".gif") ("bgcolor" "#") ("text" "#")
959 ("link" "#") ("alink" "#") ("vlink" "#"))
960 ("box" (nil _ "<over>" _))
961 ("br" t ("clear" ("left") ("right")))
962 ("caption" ("valign" ("top") ("bottom")))
963 ("center" \n)
964 ("cite")
965 ("code" \n)
966 ("dd" t)
967 ("del")
968 ("dfn")
969 ("dl" (nil \n
970 ( "Term: "
971 "<dt>" str "<dd>" _ \n)))
972 ("dt" (t _ "<dd>"))
973 ("em")
974 ("fn" "id" "fn")
975 ("head" \n)
976 ("html" (\n
977 "<head>\n"
978 "<title>" (setq str (read-input "Title: ")) "</title>\n"
979 "<body>\n<h1>" str "</h1>\n" _
980 "\n<address>\n<a href=\"mailto:"
be047262 981 user-mail-address
1caf38eb
RS
982 "\">" (user-full-name) "</a>\n</address>"))
983 ("i")
984 ("ins")
985 ("isindex" t ("action") ("prompt"))
986 ("kbd")
987 ("lang")
988 ("li" t)
989 ("math" \n)
990 ("nobr")
991 ("option" t ("value") ("label") ("selected" t))
992 ("over" t)
993 ("person")
994 ("pre" \n)
995 ("q")
996 ("rev")
997 ("s")
998 ("samp")
999 ("small")
1000 ("strong")
1001 ("sub")
1002 ("sup")
1003 ("title")
1004 ("tr" t)
1005 ("tt")
1006 ("u")
1007 ("var")
1008 ("wbr" t)))
1009 "*Value of `sgml-tag-alist' for HTML mode.")
1010
1011(defvar html-tag-help
1012 `(,@sgml-tag-help
1013 ("a" . "Anchor of point or link elsewhere")
1014 ("abbrev" . "?")
1015 ("acronym" . "?")
1016 ("address" . "Formatted mail address")
1017 ("array" . "Math array")
1018 ("au" . "?")
1019 ("b" . "Bold face")
1020 ("base" . "Base address for URLs")
1021 ("big" . "Font size")
1022 ("blink" . "Blinking text")
1023 ("blockquote" . "Indented quotation")
1024 ("body" . "Document body")
1025 ("box" . "Math fraction")
1026 ("br" . "Line break")
1027 ("caption" . "Table caption")
1028 ("center" . "Centered text")
1029 ("changed" . "Change bars")
1030 ("cite" . "Citation of a document")
1031 ("code" . "Formatted source code")
1032 ("dd" . "Definition of term")
1033 ("del" . "?")
1034 ("dfn" . "?")
1035 ("dir" . "Directory list (obsolete)")
1036 ("dl" . "Definition list")
1037 ("dt" . "Term to be definined")
1038 ("em" . "Emphasised")
1039 ("embed" . "Embedded data in foreign format")
1040 ("fig" . "Figure")
1041 ("figa" . "Figure anchor")
1042 ("figd" . "Figure description")
1043 ("figt" . "Figure text")
1044 ("fn" . "?")
1045 ("font" . "Font size")
1046 ("form" . "Form with input fields")
1047 ("group" . "Document grouping")
1048 ("h1" . "Most important section headline")
1049 ("h2" . "Important section headline")
1050 ("h3" . "Section headline")
1051 ("h4" . "Minor section headline")
1052 ("h5" . "Unimportant section headline")
1053 ("h6" . "Least important section headline")
1054 ("head" . "Document header")
1055 ("hr" . "Horizontal rule")
1056 ("html" . "HTML Document")
1057 ("i" . "Italic face")
1058 ("img" . "Graphic image")
1059 ("input" . "Form input field")
1060 ("ins" . "?")
1061 ("isindex" . "Input field for index search")
1062 ("kbd" . "Keybard example face")
1063 ("lang" . "Natural language")
1064 ("li" . "List item")
1065 ("link" . "Link relationship")
1066 ("math" . "Math formula")
1067 ("menu" . "Menu list (obsolete)")
1068 ("mh" . "Form mail header")
1069 ("nextid" . "Allocate new id")
1070 ("nobr" . "Text without line break")
1071 ("ol" . "Ordered list")
1072 ("option" . "Selection list item")
1073 ("over" . "Math fraction rule")
1074 ("p" . "Paragraph start")
1075 ("panel" . "Floating panel")
1076 ("person" . "?")
1077 ("pre" . "Preformatted fixed width text")
1078 ("q" . "?")
1079 ("rev" . "Reverse video")
1080 ("s" . "?")
1081 ("samp" . "Sample text")
1082 ("select" . "Selection list")
1083 ("small" . "Font size")
1084 ("sp" . "Nobreak space")
1085 ("strong" . "Standout text")
1086 ("sub" . "Subscript")
1087 ("sup" . "Superscript")
1088 ("table" . "Table with rows and columns")
1089 ("tb" . "Table vertical break")
1090 ("td" . "Table data cell")
1091 ("textarea" . "Form multiline edit area")
1092 ("th" . "Table header cell")
1093 ("title" . "Document title")
1094 ("tr" . "Table row separator")
1095 ("tt" . "Typewriter face")
1096 ("u" . "Underlined text")
1097 ("ul" . "Unordered list")
1098 ("var" . "Math variable face")
1099 ("wbr" . "Enable <br> within <nobr>"))
1100"*Value of `sgml-tag-help' for HTML mode.")
1101
1102
1103
1104;;;###autoload
1105(defun html-mode ()
1106 "Major mode based on SGML mode for editing HTML documents.
fcc3195e
RS
1107This allows inserting skeleton costructs used in hypertext documents with
1108completion. See below for an introduction to HTML. Use
1109\\[browse-url-of-buffer] to see how this comes out. See also `sgml-mode' on
1110which this is based.
1caf38eb 1111
fcc3195e 1112Do \\[describe-variable] html- SPC and \\[describe-variable] sgml- SPC to see available variables.
1caf38eb
RS
1113
1114To write fairly well formatted pages you only need to know few things. Most
1115browsers have a function to read the source code of the page being seen, so
1116you can imitate various tricks. Here's a very short HTML primer which you
1117can also view with a browser to see what happens:
1118
1119<title>A Title Describing Contents</title> should be on every page. Pages can
1120have <h1>Very Major Headlines</h1> through <h6>Very Minor Headlines</h6>
1121<hr> Parts can be separated with horizontal rules.
1122
1123<p>Paragraphs only need an opening tag. Line breaks and multiple spaces are
1124ignored unless the text is <pre>preformatted.</pre> Text can be marked as
1125<b>bold</b>, <i>italic</i> or <u>underlined</u> using the normal M-g or
1126Edit/Text Properties/Face commands.
1127
1128Pages can have <a name=\"SOMENAME\">named points</a> and can link other points
1129to them with <a href=\"#SOMENAME\">see also somename</a>. In the same way <a
1130href=\"URL\">see also URL</a> where URL is a filename relative to current
1131directory or something like http://www.cs.indiana.edu/elisp/w3/docs.html.
1132
1133Images in many formats can be inlined with <img src=\"URL\">.
1134
1135If you mainly create your own documents, `sgml-specials' might be interesting.
1136But note that some HTML 2 browsers can't handle &apos;. To work around that
1137do:
1138
1139\(eval-after-load \"sgml-mode\" '(aset sgml-char-names ?' nil))
1140\\{html-mode-map}"
1141 (interactive)
1142 (sgml-mode-common html-tag-face-alist html-display-text)
1143 (use-local-map html-mode-map)
1144 (make-local-variable 'sgml-tag-alist)
1145 (make-local-variable 'sgml-face-tag-alist)
1146 (make-local-variable 'sgml-tag-help)
1147 (make-local-variable 'outline-regexp)
1148 (make-local-variable 'outline-heading-end-regexp)
1149 (make-local-variable 'outline-level)
da84bdc4
RS
1150 (make-local-variable 'sentence-end)
1151 (setq sentence-end
1152 "[.?!][]\"')}]*\\(<[^>]*>\\)*\\($\\| $\\|\t\\| \\)[ \t\n]*")
1caf38eb
RS
1153 (setq mode-name "HTML"
1154 major-mode 'html-mode
1155 sgml-tag-alist html-tag-alist
1156 sgml-face-tag-alist html-face-tag-alist
1157 sgml-tag-help html-tag-help
1158 outline-regexp "^.*<[Hh][1-6]\\>"
1159 outline-heading-end-regexp "</[Hh][1-6]>"
1160 outline-level (lambda ()
1161 (char-after (1- (match-end 0)))))
1162 (run-hooks 'html-mode-hook))
1163
1164
1165(define-skeleton html-href-anchor
1166 "HTML anchor tag with href attribute."
1167 nil
1168 "<a href=\"http:" _ "\"></a>")
1169
1170(define-skeleton html-name-anchor
1171 "HTML anchor tag with name attribute."
1172 nil
1173 "<a name=\"" _ "\"></a>")
1174
7e49eef2
RS
1175(define-skeleton html-headline-1
1176 "HTML level 1 headline tags."
1177 nil
1178 "<h1>" _ "</h1>")
1179
1180(define-skeleton html-headline-2
1181 "HTML level 2 headline tags."
1182 nil
1183 "<h2>" _ "</h2>")
1184
1185(define-skeleton html-headline-3
1186 "HTML level 3 headline tags."
1187 nil
1188 "<h3>" _ "</h3>")
1189
1190(define-skeleton html-headline-4
1191 "HTML level 4 headline tags."
1192 nil
1193 "<h4>" _ "</h4>")
1194
1195(define-skeleton html-headline-5
1196 "HTML level 5 headline tags."
1197 nil
1198 "<h5>" _ "</h5>")
1199
1200(define-skeleton html-headline-6
1201 "HTML level 6 headline tags."
1202 nil
1203 "<h6>" _ "</h6>")
1caf38eb
RS
1204
1205(define-skeleton html-horizontal-rule
1206 "HTML horizontal rule tag."
1207 nil
1208 "<hr>" \n)
1209
1210(define-skeleton html-image
1211 "HTML image tag."
1212 nil
1213 "<img src=\"http:" _ "\">")
1214
1215(define-skeleton html-line
1216 "HTML line break tag."
1217 nil
1218 "<br>" \n)
1219
7e49eef2
RS
1220(define-skeleton html-ordered-list
1221 "HTML ordered list tags."
1222 nil
1223 ?< "ol>" \n
1224 "<li>" _ \n
1225 "</ol>")
1226
1227(define-skeleton html-unordered-list
1228 "HTML unordered list tags."
1229 nil
1230 ?< "ul>" \n
1caf38eb 1231 "<li>" _ \n
7e49eef2 1232 "</ul>")
1caf38eb
RS
1233
1234(define-skeleton html-list-item
1235 "HTML list item tag."
1236 nil
1237 (if (bolp) nil '\n)
1238 "<li>")
1239
1240(define-skeleton html-paragraph
1241 "HTML paragraph tag."
1242 nil
1243 (if (bolp) nil ?\n)
1244 \n "<p>")
1245
fcc3195e
RS
1246(define-skeleton html-checkboxes
1247 "Group of connected checkbox inputs."
1248 nil
1249 '(setq v1 (eval str)) ; allow passing name as argument
1250 ("Value & Text: "
1251 "<input type=\"checkbox\" name=\""
1252 (or v1 (setq v1 (skeleton-read "Name: ")))
1253 "\" value=\"" str ?\"
1254 (if v2 "" " checked") ?> str
1255 (or v2 (setq v2 (if (y-or-n-p "Newline? ") "<br>" ""))) \n))
1256
1caf38eb
RS
1257(define-skeleton html-radio-buttons
1258 "Group of connected radio button inputs."
1259 nil
1260 '(setq v1 (eval str)) ; allow passing name as argument
1261 ("Value & Text: "
1262 "<input type=\"radio\" name=\""
1263 (or v1 (setq v1 (skeleton-read "Name: ")))
1264 "\" value=\"" str ?\"
1265 (if v2 "" " checked") ?> str
1266 (or v2 (setq v2 (if (y-or-n-p "Newline? ") "<br>" ""))) \n))
1267
1268
1269(defun html-autoview-mode (&optional arg)
1270 "Toggle automatic viewing via `html-viewer' upon saving buffer.
1271With positive prefix ARG always turns viewing on, with negative ARG always off.
1272Can be used as a value for `html-mode-hook'."
1273 (interactive "P")
1274 (if (setq arg (if arg
1275 (< (prefix-numeric-value arg) 0)
1276 (and (boundp 'after-save-hook)
1277 (memq 'browse-url-of-buffer after-save-hook))))
1278 (setq after-save-hook (delq 'browse-url-of-buffer after-save-hook))
1279 (make-local-hook 'after-save-hook)
1280 (add-hook 'after-save-hook 'browse-url-of-buffer nil t))
1281 (message "Autoviewing turned %s."
1282 (if arg "off" "on")))
1283
72c0ae01 1284;;; sgml-mode.el ends here