;;; psgml-html.el --- HTML mode in conjunction with PSGML ;; Copyright (C) 1994 Nelson Minar. ;; Copyright (C) 1995 Nelson Minar and Ulrik Dickow. ;; Copyright (C) 1996 Ben Wing. ;; This file is part of XEmacs. ;; XEmacs is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; XEmacs is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with XEmacs; see the file COPYING. If not, write to the Free ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ;;; Synched up with: FSF 19.30. ;;; Author: Ben Wing. ;;; Commentary: ; Parts were taken from html-helper-mode and from code by Alastair Burt. ; If you'd like to use the hm--html-minor-mode together with this ; mode, you have to put the following line to your ~/.emacs: ; (add-hook 'html-mode-hook 'hm--html-minor-mode) ;; Modified for Debian: use (string-match "XEmacs" emacs-version) ;; instead of running-xemacs ;;; Code: (defvar html-auto-sgml-entity-conversion nil "*Control automatic sgml entity to ISO-8859-1 conversion") (provide 'psgml-html) (require 'psgml) (require 'derived) (when html-auto-sgml-entity-conversion (require 'iso-sgml)) (require 'tempo) ;essential part of html-helper-mode (eval-when-compile (require 'browse-url) (require 'font-lock) (require 'imenu)) ;;{{{ user variables (defgroup html nil "HyperText Markup Language" :group 'sgml) (defgroup psgml-html nil "HTML mode in conjunction with PSGML" :tag "Psgml Html" :prefix "psgml-html-" :group 'html :group 'psgml) ;;;; Modified for Debian: now accomodates Emacs as well ;; Set this to be whatever signature you want on the bottom of your pages. (defcustom psgml-html-address-string (cond ((string-match "XEmacs" emacs-version) ; XEmacs/Lucid (concat "" (user-full-name) "")) (t (concat "" user-full-name ""))) "*The default author string of each file." :type 'string :group 'psgml-html) (defcustom psgml-html-htmldtd-version "\n" "*Version of HTML DTD you're using." :type 'string :group 'psgml-html) (defcustom psgml-html-do-write-file-hooks t "*If not nil, then modify `local-write-file-hooks' to do timestamps." :type 'boolean :group 'psgml-html) (defcustom psgml-html-build-new-buffer t "*If not nil, then insert `psgml-html-new-buffer-strings' for new buffers." :type 'boolean :group 'psgml-html) (defcustom psgml-html-timestamp-hook 'psgml-html-default-insert-timestamp "*Hook called for timestamp insertion. Override this for your own timestamp styles." :type 'hook :group 'psgml-html) ;; strings you might want to change (defcustom psgml-html-new-buffer-template '(psgml-html-htmldtd-version "\n" " \n" " " (p "Document Title: " title) "\n" " \n" "\n" " \n" "

" (s title) "

\n\n" p "\n\n
\n" "
" psgml-html-address-string "
\n" (psgml-html-return-created-string) psgml-html-timestamp-start psgml-html-timestamp-end "\n \n\n") "*Template for new buffers. Inserted by `psgml-html-insert-new-buffer-strings' if `psgml-html-build-new-buffer' is set to t" :type 'sexp :group 'psgml-html) (defcustom psgml-html-timestamp-start "\n" "*Start delimiter for timestamps. Everything between `psgml-html-timestamp-start' and `psgml-html-timestamp-end' will be deleted and replaced with the output of the functions `psgml-html-timestamp-hook' if `psgml-html-do-write-file-hooks' is t" :type 'string :group 'psgml-html) (defcustom psgml-html-timestamp-end "" "*End delimiter for timestamps. Everything between `psgml-html-timestamp-start' and `psgml-html-timestamp-end' will be deleted and replaced with the output of the function `psgml-html-insert-timestamp' if `psgml-html-do-write-file-hooks' is t" :type 'string :group 'psgml-html) ;; control over what types of tags to load. By default, we load all the ;; ones we know of. (defcustom psgml-html-types-to-install '(anchor header logical phys list textel entity image head form table special) "*List of tag types to install when psgml-html-mode is first loaded. If you want to not install some type of tag, override this variable. Order is significant: menus go in this order." :type '(repeat symbol) :group 'psgml-html) (defcustom psgml-html-use-expert-menu nil "*If not nil, then use the full HTML menu." :type 'boolean :group 'psgml-html) (defcustom psgml-html-user-menu nil "*Extra items to put in the HTML expert menu. The value of this symbol is appended to the beginning of the expert menu that is handed off to easymenu for definition. It should be a list of vectors or lists which themselves are vectors (for submenus)." :type 'sexp :group 'psgml-html) ;;}}} end of user variables ;;{{{ type based keymap and menu variable and function setup ;; psgml-html-mode has a concept of "type" of tags. Each type is a ;; list of tags that all go together in one keymap and one menu. ;; Types can be added to the system after psgml-html has been loaded, ;; briefly by doing psgml-html-add-type-to-alist, then ;; psgml-html-install-type, then psgml-html-add-tag (for each tag) ;; then psgml-html-rebuild-menu. See the mode documentation for more detail. (defconst psgml-html-type-alist nil "Alist: type of tag -> keymap, keybinding, menu, menu string. Add to this with `psgml-html-add-type-to-alist'.") ;;{{{ accessor functions for psgml-html-type-alist (tempo-define-template "html-skeleton" psgml-html-new-buffer-template nil "Insert a skeleton for a HTML document") (defun psgml-html-keymap-for (type) "Accessor function for alist: for type, return keymap or nil" (nth 0 (cdr-safe (assq type psgml-html-type-alist)))) (defun psgml-html-key-for (type) "Accessor function for alist: for type, return keybinding or nil" (nth 1 (cdr-safe (assq type psgml-html-type-alist)))) (defun psgml-html-menu-for (type) "Accessor function for alist: for type, return menu or nil" (nth 2 (cdr-safe (assq type psgml-html-type-alist)))) (defun psgml-html-menu-string-for (type) "Accessor function for alist: for type, return menustring or nil" (nth 3 (cdr-safe (assq type psgml-html-type-alist)))) (defun psgml-html-normalized-menu-for (type) "Helper function for building menus from submenus: add on string to menu." (cons (psgml-html-menu-string-for type) (eval (psgml-html-menu-for type)))) ;;}}} (define-derived-mode html-mode sgml-mode "HTML" "Major mode for editing HTML documents. This is based on PSGML mode, and has a sophisticated SGML parser in it. It knows how to properly indent HTML/SGML documents, and it can do a form of document validation (use \\[sgml-next-trouble-spot] to find the next error in your document). Commands beginning with C-z insert various types of HTML tags (prompting for the required information); to iconify or suspend, use C-z C-z. To literally insert special characters such as < and &, use C-c followed by the character. Use \\[sgml-insert-end-tag] to insert the proper closing tag. Use \\[sgml-edit-attributes] to edit the attributes for a tag. Use \\[sgml-show-context] to show the current HTML context. More specifically: \\{html-mode-map} " (make-local-variable 'sgml-declaration) (make-local-variable 'sgml-default-doctype-name) (if (or (not (boundp 'sgml-custom-dtd)) (not sgml-custom-dtd)) (setq sgml-custom-dtd '( ( "HTML 4.01 Strict" "" ) ( "HTML 4.01 Transitional" "") ( "HTML 4.01 Frameset" "" ) ) )) (setq sgml-declaration (expand-file-name "html.decl" sgml-data-directory) sgml-default-doctype-name "HTML" sgml-always-quote-attributes t sgml-indent-step 2 sgml-indent-data t sgml-inhibit-indent-tags '("pre") sgml-minimize-attributes nil sgml-omittag t sgml-shorttag t) ;; Added for Debian ;; menus for creating new documents ;; font-lock setup for various emacsen: XEmacs, Emacs 19.29+, Emacs <19.29. ;; By Ulrik Dickow . (Last update: 05-Sep-1995). (cond ((string-match "XEmacs" emacs-version) ; XEmacs/Lucid (put major-mode 'font-lock-keywords-case-fold-search t)) ;; XEmacs (19.13, at least) guesses the rest correctly. ;; If any older XEmacsen don't, then tell me. ;; ((string-lessp "19.28.89" emacs-version) ; Emacs 19.29 and later (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '(html-font-lock-keywords t t))) ;; (t ; Emacs 19.28 and older (make-local-variable 'font-lock-keywords-case-fold-search) (make-local-variable 'font-lock-keywords) (make-local-variable 'font-lock-no-comments) (setq font-lock-keywords-case-fold-search t) (setq font-lock-keywords html-font-lock-keywords) (setq font-lock-no-comments t))) (if psgml-html-do-write-file-hooks (add-hook 'local-write-file-hooks 'psgml-html-update-timestamp)) (if (and psgml-html-build-new-buffer (zerop (buffer-size))) (psgml-html-insert-new-buffer-strings)) (set (make-local-variable 'sgml-custom-markup) '(("" "\r"))) ;; Set up the syntax table. (modify-syntax-entry ?< "(>" html-mode-syntax-table) (modify-syntax-entry ?> ")<" html-mode-syntax-table) (modify-syntax-entry ?\" ". " html-mode-syntax-table) (modify-syntax-entry ?\\ ". " html-mode-syntax-table) (modify-syntax-entry ?' "w " html-mode-syntax-table) (tempo-use-tag-list 'psgml-html-tempo-tags psgml-html-completion-finder) (setq imenu-create-index-function 'psgml-html-imenu-index) (setq imenu-sort-function nil) ; sorting the menu defeats the purpose ; sigh ... need to call this now to get things working. (sgml-build-custom-menus) ;; (add-submenu nil sgml-html-menu "SGML") (setq sgml-menu-name "HTML") (easy-menu-add sgml-html-menu) (psgml-html-rebuild-menu) (if (string-match "XEmacs" emacs-version) (unless (featurep 'infodock) (delete-menu-item '("SGML"))))) (defvar psgml-html-imenu-regexp "\\s-*]*>\\(<[^\n<>]*>\\)*\\s-*\\([^\n<>]*\\)" "*A regular expression matching a head line to be added to the menu. The first `match-string' should be a number from 1-9. The second `match-string' matches extra tags and is ignored. The third `match-string' will be the used in the menu.") ;; Make an index for imenu (defun psgml-html-imenu-index () "Return an table of contents for an html buffer for use with Imenu." (let ((space ?\ ) ; a char (toc-index '()) toc-str) (save-excursion (goto-char (point-min)) (while (re-search-forward psgml-html-imenu-regexp nil t) (setq toc-str (concat (make-string (* 2 (- (string-to-number (match-string 1)) 1)) space) (match-string 3))) (beginning-of-line) (setq toc-index (cons (cons toc-str (point)) toc-index)) (end-of-line))) (nreverse toc-index))) (defun psgml-html-add-type-to-alist (type) "Add a type specification to the alist. The spec goes (type . (keymap-symbol keyprefix menu-symbol menu-string)). See code for an example." (setq psgml-html-type-alist (cons type psgml-html-type-alist))) ;; Here are the types provided by psgml-html-mode. (mapcar 'psgml-html-add-type-to-alist '((entity . (nil nil psgml-html-entity-menu "Insert Character Entities")) (textel . (nil nil psgml-html-textel-menu "Insert Text Elements")) (head . (psgml-html-head-map "\C-zw" psgml-html-head-menu "Insert Structural Elements")) (header . (psgml-html-base-map "\C-z" psgml-html-header-menu "Insert Headers")) (anchor . (psgml-html-base-map "\C-z" psgml-html-anchor-menu "Insert Hyperlinks")) (logical . (psgml-html-base-map "\C-z" psgml-html-logical-menu "Insert Logical Styles")) (phys . (psgml-html-base-map "\C-z" psgml-html-phys-menu "Insert Physical Styles")) (list . (psgml-html-list-map "\C-zl" psgml-html-list-menu "Insert List Elements")) (form . (psgml-html-form-map "\C-zf" psgml-html-form-menu "Insert Form Elements")) (table . (psgml-html-table-map "\C-zt" psgml-html-table-menu "Insert Table Elements")) (image . (psgml-html-image-map "\C-zm" psgml-html-image-menu "Insert Inlined Images")) (special . (psgml-html-base-map "\C-z" psgml-html-special-menu "Insert Specials")))) ;; Once psgml-html-mode is aware of a type, it can then install the ;; type: arrange for keybindings, menus, etc. (defconst psgml-html-installed-types nil "The types that have been installed (used when building menus). There is no support for removing a type once it has been installed.") (defun psgml-html-install-type (type) "Install a new tag type: add it to the keymap, menu structures, etc. For this to work, the type must first have been added to the list of types with psgml-html-add-type-to-alist." (setq psgml-html-installed-types (cons type psgml-html-installed-types)) (let ((keymap (psgml-html-keymap-for type)) (key (psgml-html-key-for type)) (menu (psgml-html-menu-for type)) (menu-string (psgml-html-menu-string-for type))) (and key (progn (set keymap nil) (define-prefix-command keymap) (define-key html-mode-map key keymap))) (and menu (progn (set menu nil))))) ;; install the default types. (mapcar 'psgml-html-install-type psgml-html-types-to-install) ;;}}} ;;{{{ psgml-html-add-tag function for building basic tags (defvar psgml-html-tempo-tags nil "List of tags used in completion.") ;; this while loop is awfully Cish ;; isn't there an emacs lisp function to do this? (defun psgml-html-string-to-symbol (input-string) "Given a string, downcase it and replace spaces with -. We use this to turn menu entries into good symbols for functions. It's not entirely successful, but fortunately emacs lisp is forgiving." (let* ((s (downcase input-string)) (l (1- (length s)))) (while (>= l 0) (if (char-equal (aref s l) ?\ ) (aset s l ?\-)) (setq l (1- l))) (concat "html-" s))) (defun psgml-html-add-tag (l) "Add a new tag to psgml-html-mode. Builds a tempo-template for the tag and puts it into the appropriate keymap if a key is requested. Format: `(psgml-html-add-tag '(type keybinding completion-tag menu-name template doc)'" (let* ((type (car l)) (keymap (psgml-html-keymap-for type)) (menu (psgml-html-menu-for type)) (key (nth 1 l)) (completer (nth 2 l)) (name (nth 3 l)) (tag (nth 4 l)) (doc (nth 5 l)) (command (tempo-define-template (psgml-html-string-to-symbol name) tag completer doc 'psgml-html-tempo-tags))) (if (null (memq type psgml-html-installed-types)) ;type loaded? t ;no, do nothing. (if (stringp key) ;bind key somewhere? (if keymap ;special keymap? (define-key (eval keymap) key command) ;t: bind to prefix (define-key html-mode-map key command)) ;nil: bind to global t) (if menu ;is there a menu? (set menu ;good, cons it in (cons (vector name command t) (eval menu)))) ))) ;;}}} ;;{{{ most of the HTML tags ;; These tags are an attempt to be HTML 3.2 compliant ;; For reference see ;; order here is significant: within a tag type, menus and mode help ;; go in the reverse order of what you see here. Sorry about that, it's ;; not easy to fix. (mapcar 'psgml-html-add-tag '( ;;entities (entity "\C-c#" "&#" "Ascii Code" ("&#" (r "Ascii: ") ";")) (entity "\C-c\"" """ "Quotation mark" (""")) (entity "\C-c$" "®" "Registered" ("®")) (entity "\C-c@" "©" "Copyright" ("©")) (entity "\C-c-" "­" "Soft Hyphen" ("­")) (entity "\C-c " " " "Nonbreaking Space" (" ")) (entity "\C-c&" "&" "Ampersand" ("&")) (entity "\C-c>" ">" "Greater Than" (">")) (entity "\C-c<" "<" "Less Than" ("<")) ;; logical styles (logical "v" "" (r "Text: ") "")) (logical "n" "
" "Center" ("
" (r "Text: ") "
")) (logical "q" "
" "Blockquote" ("
" (r "Quote: ") "
")) (logical "c" "" "Code" ("" (r "Code: ") "")) (logical "x" "" "Sample" ("" (r "Sample code") "")) (logical "r" "" "Citation" ("" (r "Citation: ") "")) (logical "k" "" "Keyboard Input" ("" (r "Keyboard: ") "")) (logical "v" "" "Variable" ("" (r "Variable: ") "")) (logical "d" "" "Definition" ("" (r "Definition: ") "")) (logical "a" "
" "Address" ("
" r "
")) (logical "e" "" "Emphasized" ("" (r "Text: ") "")) (logical "s" "" "Strong" ("" (r "Text: ") "")) (logical "p" "
"		"Preformatted"   ("
" (r "Text: ") "
")) ;;physical styles (phys "p" "" "Superscript" ("" (r "Text: ") "")) (phys "u" "" "Subscript" ("" (r "Text: ") "")) (phys "s" "" "Small" ("" (r "Text: ") "")) (phys "g" "" "Big" ("" (r "Text: ") "")) (phys "-" "" "Strikethru" ("" (r "Text: ") "")) (phys "u" "" "Underline" ("" (r "Text: ") "")) (phys "o" "" "Italic" ("" (r "Text: ") "")) (phys "b" "" "Bold" ("" (r "Text: ") "")) (phys "t" "" "Fixed" ("" (r "Text: ") "")) ;;headers (header "6" "
" "Header 6" ("
" (r "Header: ") "
")) (header "5" "
" "Header 5" ("
" (r "Header: ") "
")) (header "4" "

" "Header 4" ("

" (r "Header: ") "

")) (header "3" "

" "Header 3" ("

" (r "Header: ") "

")) (header "2" "

" "Header 2" ("

" (r "Header: ") "

")) (header "1" "

" "Header 1" ("

" (r "Header: ") "

")) ;; forms (form "o" "