;;; 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)
;;; Code:
(defvar html-auto-sgml-entity-conversion nil
"*Control automatic sgml entity to ISO-8859-1 conversion")
(require 'psgml)
(require 'derived)
(when html-auto-sgml-entity-conversion
(require 'iso-sgml))
(require 'tempo) ;essential part of html-helper-mode
;;{{{ user variables
(defgroup html nil
"HyperText Markup Language"
:group 'sgml)
(defgroup psgml-html nil
"HTML mode in conjunction with PSGML"
:tag "Psgml Html"
:prefix "html-helper-"
:group 'html
:group 'psgml)
;; Set this to be whatever signature you want on the bottom of your pages.
(defcustom html-helper-address-string
(concat ""
(user-full-name) "")
"*The default author string of each file."
:type 'string
:group 'psgml-html)
(defcustom html-helper-htmldtd-version "\n"
"*Version of HTML DTD you're using."
:type 'string
:group 'psgml-html)
(defcustom html-helper-do-write-file-hooks t
"*If not nil, then modify `local-write-file-hooks' to do timestamps."
:type 'boolean
:group 'psgml-html)
(defcustom html-helper-build-new-buffer t
"*If not nil, then insert `html-helper-new-buffer-strings' for new buffers."
:type 'boolean
:group 'psgml-html)
(defcustom html-helper-timestamp-hook 'html-helper-default-insert-timestamp
"*Hook called for timestamp insertion.
Override this for your own timestamp styles."
:type 'boolean
:group 'psgml-html)
;; strings you might want to change
(defcustom html-helper-new-buffer-template
'(html-helper-htmldtd-version
"\n"
"
\n\n"
p
"\n\n \n"
" " html-helper-address-string "\n"
(html-helper-return-created-string)
html-helper-timestamp-start
html-helper-timestamp-end
"\n \n\n")
"*Template for new buffers.
Inserted by `html-helper-insert-new-buffer-strings' if
`html-helper-build-new-buffer' is set to t"
:type 'sexp
:group 'psgml-html)
(defcustom html-helper-timestamp-start "\n"
"*Start delimiter for timestamps.
Everything between `html-helper-timestamp-start' and
`html-helper-timestamp-end' will be deleted and replaced with the output
of the functions `html-helper-timestamp-hook' if
`html-helper-do-write-file-hooks' is t"
:type 'string
:group 'psgml-html)
(defcustom html-helper-timestamp-end ""
"*End delimiter for timestamps.
Everything between `html-helper-timestamp-start' and
`html-helper-timestamp-end' will be deleted and replaced with the output
of the function `html-helper-insert-timestamp' if
`html-helper-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 html-helper-types-to-install
'(anchor header logical phys list textel entity image head form)
"*List of tag types to install when html-helper-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)
;;}}} end of user variables
;;{{{ type based keymap and menu variable and function setup
;; html-helper-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 html-helper has been loaded,
;; briefly by doing html-helper-add-type-to-alist, then
;; html-helper-install-type, then html-helper-add-tag (for each tag)
;; then html-helper-rebuild-menu. See the mode documentation for more detail.
(defconst html-helper-type-alist nil
"Alist: type of tag -> keymap, keybinding, menu, menu string.
Add to this with `html-helper-add-type-to-alist'.")
;;{{{ accessor functions for html-helper-type-alist
(defun html-helper-keymap-for (type)
"Accessor function for alist: for type, return keymap or nil"
(nth 0 (cdr-safe (assq type html-helper-type-alist))))
(defun html-helper-key-for (type)
"Accessor function for alist: for type, return keybinding or nil"
(nth 1 (cdr-safe (assq type html-helper-type-alist))))
(defun html-helper-menu-for (type)
"Accessor function for alist: for type, return menu or nil"
(nth 2 (cdr-safe (assq type html-helper-type-alist))))
(defun html-helper-menu-string-for (type)
"Accessor function for alist: for type, return menustring or nil"
(nth 3 (cdr-safe (assq type html-helper-type-alist))))
(defun html-helper-normalized-menu-for (type)
"Helper function for building menus from submenus: add on string to menu."
(cons (html-helper-menu-string-for type)
(eval (html-helper-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)
(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-shortag t)
;; 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\\|Lucid" (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 html-helper-do-write-file-hooks
(add-hook 'local-write-file-hooks 'html-helper-update-timestamp))
(if (and html-helper-build-new-buffer (zerop (buffer-size)))
(html-helper-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)
; sigh ... need to call this now to get things working.
(sgml-build-custom-menus)
(add-submenu nil sgml-html-menu "SGML")
(delete-menu-item '("SGML")))
(defun html-helper-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 html-helper-type-alist (cons type html-helper-type-alist)))
;; Here are the types provided by html-helper-mode.
(mapcar 'html-helper-add-type-to-alist
'((entity . (nil nil html-helper-entity-menu "Insert Character Entities"))
(textel . (nil nil html-helper-textel-menu "Insert Text Elements"))
(head . (html-helper-head-map "\C-zb" html-helper-head-menu "Insert Structural Elements"))
(header . (html-helper-base-map "\C-z" html-helper-header-menu "Insert Headers"))
(anchor . (html-helper-base-map "\C-z" html-helper-anchor-menu "Insert Hyperlinks"))
(logical . (html-helper-base-map "\C-z" html-helper-logical-menu "Insert Logical Styles"))
(phys . (html-helper-base-map "\C-z" html-helper-phys-menu "Insert Physical Styles"))
(list . (html-helper-list-map "\C-zl" html-helper-list-menu "Insert List Elements"))
(form . (html-helper-form-map "\C-zf" html-helper-form-menu "Insert Form Elements"))
(image . (html-helper-image-map "\C-zm" html-helper-image-menu "Insert Inlined Images"))))
;; Once html-helper-mode is aware of a type, it can then install the
;; type: arrange for keybindings, menus, etc.
(defconst html-helper-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 html-helper-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 html-helper-add-type-to-alist."
(setq html-helper-installed-types (cons type html-helper-installed-types))
(let ((keymap (html-helper-keymap-for type))
(key (html-helper-key-for type))
(menu (html-helper-menu-for type))
(menu-string (html-helper-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 'html-helper-install-type html-helper-types-to-install)
;;}}}
;;{{{ html-helper-add-tag function for building basic tags
(defvar html-helper-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 html-helper-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 (copy-sequence input-string))
(l (1- (length s))))
(while (> l 0)
(if (char-equal (aref s l) ?\ )
(aset s l ?\-))
(setq l (1- l)))
(concat "html-" (downcase s))))
(defun html-helper-add-tag (l)
"Add a new tag to html-helper-mode.
Builds a tempo-template for the tag and puts it into the
appropriate keymap if a key is requested. Format:
`(html-helper-add-tag '(type keybinding completion-tag menu-name template doc)'"
(let* ((type (car l))
(keymap (html-helper-keymap-for type))
(menu (html-helper-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 (html-helper-string-to-symbol name)
tag completer doc
'html-helper-tempo-tags)))
(if (null (memq type html-helper-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/2.0 compliant, with the exception
;; of container