;;; html-helper-mode.el --- Major mode for composing html files.

;; Author: Nelson Minar <nelson@reed.edu>
;; Maintainer: Nelson Minar <nelson@reed.edu>
;; Created: 01 Feb 1994
;; Version: $Revision: 1.33 $
;; Keywords: HTML major-mode

;; Copyright (C) 1994 Nelson Minar

;; This program 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.

;; This program 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 GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

;;; Commentary:
;;{{{ 

;; Installation:
;;   add this line in your .emacs:
;;     (autoload 'html-helper-mode "html-helper-mode" "Yay HTML" t)
;;   to invoke html-helper-mode automatically on .html files, do this:
;;     (setq auto-mode-alist (cons '("\\.html$" . html-helper-mode) auto-mode-alist))

;; Configuration:
;;   see the "user variables" section, or the documentation on configuration
;;   in http://www.reed.edu/~nelson/tools/. There are variables you want to
;;   configure, particularly html-helper-do-write-file-hooks,
;;   html-helper-build-new-buffer, and html-helper-address-string

;; Description:
;;   html-helper-mode makes it easier to write HTML documents. This mode
;;   handles inserting HTML codes in a variety of ways (keybindings,
;;   completion in the buffer). It also supports indentation, timestamps,
;;   skeletons for new documents, hilit19 patterns, and a variety of other
;;   things. For the full skinny, see the HTML documentation that comes
;;   with the package or is at http://www.reed.edu/~nelsont/tools/

;; Thank yous:
;;   David Kågedal <davidk@lysator.liu.se> for the tempo code which
;;   forms the core of the HTML insertion, as well as the HTML+
;;   cookies.

;;   Magnus Homann <d0asta@dtek.chalmers.se> for suggestions and code
;;   for the timestamp insertion

;;   Marc Andreessen <marca@ncsa.uiuc.edu> for writing the original html-mode
;;   that inspired this one

;; To do:
;;   some general way of building menus (easymenu.el)?
;;   font-lock patterns

;; The newest version of html-helper-mode is available from
;;   http://www.reed.edu/~nelson/tools/
;;   ftp://ftp.reed.edu/pub/src/html-helper-mode.tar.Z

;; This code was writting using folding.el, a wonderful folding editor
;; minor mode for emacs. That's what the strange {{{ comments are for.

;;}}}

;;; Code:

;;{{{ user variables

;; features. I recommend you turn these on.

(defvar html-helper-do-write-file-hooks nil
  "*If not nil, then html-helper-mode will modify the local-write-file-hooks
to do timestamps.")

(defvar html-helper-build-new-buffer nil
  "*If not nil, then html-helper will insert html-helper-new-buffer-strings
when new buffers are generated")

;; (see also tempo.el)

;; variables to configure

(defvar html-helper-basic-offset 2
  "*basic indentation size used for list indentation")

(defvar html-helper-item-continue-indent 5
  "*Indentation of lines that follow a <li> item. Default is 5, the length
of things like \"<li> \" and \"<dd> \".")

(defvar html-helper-never-indent nil
  "*If t, the indentation code for html-helper is turned off.")


;; hooks (see also tempo.el)

(defvar html-helper-mode-hook nil
  "*Hook run when html-helper-mode is started.")

(defvar html-helper-load-hook nil
  "*Hook run when html-helper-mode is loaded.")

(defvar html-helper-timestamp-hook 'html-helper-default-insert-timestamp
  "*Hook called for timestamp insertion. Override this for your own
timestamp styles.")


;; strings you might want to change

(defvar html-helper-address-string ""
  "*The default author string of each file.")

(defvar html-helper-new-buffer-template
  '("<html> <head>\n"
    "<title>" p "</title>\n</head>\n\n"
    "<body>\n"
    "<h1>" p "</h1>\n\n"
    p
    "\n\n<hr>\n"
    "<address>" html-helper-address-string "</address>\n"
    html-helper-timestamp-start
    html-helper-timestamp-end
    "\n</body> </html>\n")
  "*Template for new buffers, inserted by html-helper-insert-new-buffer-strings if
html-helper-build-new-buffer is set to t")

(defvar html-helper-timestamp-start "<!-- hhmts start -->\n"
  "*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")

(defvar html-helper-timestamp-end "<!-- hhmts 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")

;; this is what the byte compiler does to see if its emacs18. You probably
;; don't need to change this.

(defvar html-helper-emacs18
  (and (boundp 'emacs-version)
       (or (and (boundp 'epoch::version) epoch::version)
	   (string-lessp emacs-version "19")))
  "I'll do minimal emacs18 support, grumble.")

;;}}}

(require 'tempo)

;;{{{ html-helper-mode-syntax-table

;; emacs doesn't really seem to be general enough to handle SGML like
;; syntax. In particular, comments are a loss. We do try this, though:
;;   give < and > matching semantics

(defvar html-helper-mode-syntax-table nil
  "Syntax table for html-helper.")

(if html-helper-mode-syntax-table
    ()
  (setq html-helper-mode-syntax-table (make-syntax-table text-mode-syntax-table))
  (modify-syntax-entry ?<  "(>  " html-helper-mode-syntax-table)
  (modify-syntax-entry ?>  ")<  " html-helper-mode-syntax-table)
  (modify-syntax-entry ?\" ".   " html-helper-mode-syntax-table)
  (modify-syntax-entry ?\\ ".   " html-helper-mode-syntax-table)
  (modify-syntax-entry ?'  "w   " html-helper-mode-syntax-table))

;;}}}
;;{{{ keymap variable and function setup

(defvar html-helper-keymap-list
  '(html-helper-head-map html-helper-header-map html-helper-anchor-map
    html-helper-logical-map html-helper-phys-map html-helper-list-map
    html-helper-note-map html-helper-form-map)
  "list of all the subkeymaps html-helper uses")

(defvar html-helper-keymap-alist
  '((head . html-helper-head-map)
    (header . html-helper-header-map)
    (anchor . html-helper-anchor-map)
    (logical . html-helper-logical-map)
    (phys . html-helper-phys-map)
    (list . html-helper-list-map)
    (note . html-helper-note-map)
    (form . html-helper-form-map))
  "alist associating cookie types with keymaps")

;; basic keymap variables (not easy to mapcar a macro)
(defvar html-helper-mode-map (make-sparse-keymap)
  "Keymap for html-helper")
(defvar html-helper-head-map nil
  "Keymap used for head info.")
(defvar html-helper-header-map nil
  "Keymap used for headers.")
(defvar html-helper-anchor-map nil
  "Keymap used for anchors.")
(defvar html-helper-logical-map nil
  "Keymap used for logical styles.")
(defvar html-helper-phys-map nil
  "Keymap used for physical styles.")
(defvar html-helper-list-map nil
  "Keymap used for lists.")
(defvar html-helper-note-map nil
  "Keymap used for notes.")
(defvar html-helper-form-map nil
  "Keymap used for forms.")

;; make keymaps into prefix commands (does this do anything useful in 18?)
(mapcar 'define-prefix-command html-helper-keymap-list)

;; if we're emacs18, we have to build the prefix maps by hand
(if html-helper-emacs18
    (mapcar (function (lambda (v) (set v (make-sparse-keymap))))
	    html-helper-keymap-list))

;; now build the mode keymap.
;; special mode keys
(mapcar
 (function (lambda (l) (define-key html-helper-mode-map (car l) (nth 1 l))))
 '(("\M-\C-f" tempo-forward-mark)
   ("\M-\C-b" tempo-backward-mark)
   ("\M-\t"   tempo-complete-tag)
   
   ("\M-\C-t" html-helper-insert-timestamp-delimiter-at-point)))
 
;; indentation keys - only rebind these if the user wants indentation
(if html-helper-never-indent
    ()
  (define-key html-helper-mode-map "\t" 'html-helper-indent-command)
  (define-key html-helper-mode-map "\C-m" 'newline-and-indent))

;; special keybindings in the prefix maps (not in the list of cookies)
(define-key html-helper-list-map "i" 'html-helper-smart-insert-item)

;; install the prefix maps themselves into the mode map
;; eval the keymap in 18 so we get the value, not the symbol
(defun html-helper-install-prefix (l)
  "Install a prefix key into the map. Special code for emacs18"
  (if html-helper-emacs18
      (define-key html-helper-mode-map (car l) (eval (nth 1 l)))
    (define-key html-helper-mode-map (car l) (nth 1 l))))

(mapcar
 'html-helper-install-prefix
 '(("\C-c\C-b" html-helper-head-map)
   ("\C-c\C-t" html-helper-header-map)
   ("\C-c\C-a" html-helper-anchor-map)
   ("\C-c\C-s" html-helper-logical-map)
   ("\C-c\C-p" html-helper-phys-map)
   ("\C-c\C-l" html-helper-list-map)
   ("\C-c\C-n" html-helper-note-map)
   ("\C-c\C-f" html-helper-form-map)))

;;}}}
;;{{{ html-helper-mode-abbrev-table

(defvar html-helper-mode-abbrev-table nil
  "Abbrev table used while in html-helper-mode.")
(define-abbrev-table 'html-helper-mode-abbrev-table ())

;;}}}

;;{{{ html-helper-add-cookie function for building basic cookies

(defvar html-helper-tempo-tags nil
  "List of tags used in completion.")

(defun html-helper-add-cookie (l)
  "Add a new cookie to html-helper-mode. Builds a tempo-template for the
cookie and puts it into the appropriate keymap if a key is
requested."
  (let* ((type (car l))
	 (keymap (cdr-safe (assq type html-helper-keymap-alist)))
	 (key (nth 1 l))
	 (tag (nth 2 l))
	 (name (nth 3 l))
	 (cookie (nth 4 l))
	 (doc (nth 5 l))
	 (command (tempo-define-template name cookie tag doc 'html-helper-tempo-tags)))

    (if (stringp key)			                  ;bind at all?
	(if keymap			                  ;special keymap?
	    (define-key (eval keymap) key command)        ;bind to prefix
	  (define-key html-helper-mode-map key command))  ;bind to global
      )))

;;}}}

;;{{{ html-helper-smart-insert-item

;; there are two different kinds of items in HTML - those in regular
;; lists <li> and those in dictionaries <dt>..<dd>
;; This command will insert the appropriate one depending on context.

(tempo-define-template "html-item" '(& "<li> " > (r . "Item: "))
		       "<li>"
		       "Insert a new record in an HTML list"
		       'html-helper-tempo-tags)

(tempo-define-template "html-definition-item"
		       '(& "<dt> " > (p . "Term: ")
			   "\n" "<dd> " > (r . "Definition: "))
		       "<dt>"
		       "Insert a new record in an HTML definition list"
		       'html-helper-tempo-tags)

(defun html-helper-smart-insert-item (&optional arg)
  "Insert a new item, either in a regular list or a dictionary."
  (interactive "*P")
  (let ((case-fold-search t))
    (if
        (save-excursion
          (re-search-backward "<li>\\|<dt>\\|<ul>\\|<ol>\\|<dd>\\|<menu>\\|<dir>\\|<dl>" nil t)
          (looking-at "<dt>\\|<dl>\\|<dd>"))
        (tempo-template-html-definition-item arg)
      (tempo-template-html-item arg))))

;;}}}
;;{{{ most of the HTML cookies and keymap

;; taken partially from the HTML quick reference and the elements document
;; http://www.ncsa.uiuc.edu/General/Internet/WWW/HTMLQuickRef.html
;; http://info.cern.ch/hypertext/WWW/MarkUp/Tags.html
;; There are also some HTML+ tokens from I don't know which reference

;; I could put documentation in for each command - would that be useful?

(mapcar
 'html-helper-add-cookie
 '(
   ;;entities
   (entity  "\C-c&"   "&amp;"		"html-ampersand"	  ("&amp;")) 
   (entity  "\C-c<"   "&lt;"		"html-less-than"	  ("&lt;"))
   (entity  "\C-c>"   "&gt;"	  	"html-greater-than"       ("&gt;"))
   (entity  "\C-c "   "&nbsp;"		"html-nonbreaking-space"  ("&nbsp;"))

   ;; logical styles
   (logical "p"       "<pre>"		"html-preformatted"   	  ("<pre>" (r . "Text: ") "</pre>"))
   (logical "b"       "<blockquote>"	"html-blockquote"     	  ("<blockquote>" (r . "Quote: ") "</blockquote>"))
   (logical "e"       "<em>"		"html-emphasized"     	  ("<em>" (r . "Text: ") "</em>"))
   (logical "s"       "<strong>"	"html-strong"         	  ("<strong>" (r . "Text: ") "</strong>"))
   (logical "c"       "<code>"		"html-code"           	  ("<code>" (r . "Code: ") "</code>"))
   (logical "x"       "<samp>"		"html-sample"         	  ("<samp>" (r . "Sample code") "</samp>"))
   (logical "r"       "<cite>"		"html-citation"       	  ("<cite>" (r . "Citation: ") "</cite>"))
   (logical "k"       "<kbd>"		"html-keyboard"       	  ("<kbd>" (r . "Keyboard: ") "</kbd>"))
   (logical "v"       "<var>"		"html-variable"       	  ("<var>" (r . "Variable: ") "</var>"))
   (logical "d"       "<dfn>"		"html-definition"     	  ("<dfn>" (r . "Definition: ") "</dfn>"))
   (logical "q"       "<q>"		"html-quote"          	  ("<q>" (r . "Quote: ") "</q>"))
   (logical "n"       "<person>"	"html-person"         	  ("<person>" (r . "Person: ") "</person>"))
   (logical "y"       "<acronym>"	"html-acronym"        	  ("<acronym>" (r . "Acronym: ") "</acronym>"))
   (logical "."       "<abbrev>"	"html-abbrev"         	  ("<abbrev>" (r . "Abbrevation: ") "</abbrev>"))
   (logical "m"       "<cmd>"		"html-cmd"            	  ("<cmd>" (r . "Command name: ") "</cmd>"))
   (logical "g"       "<arg>"		"html-arg"            	  ("<arg>" (r . "Argument: ") "</arg>"))
   (logical "l"       "<lit>"		"html-lit"            	  ("<lit>" r "</lit>"))
   (logical "a"	      "<address>"	"html-address"		  ("<address>" r "</address>"))

   ;;physical styles
   (phys    "b"	      "<b>"    		"html-bold"               ("<b>" (r . "Text: ") "</b>"))
   (phys    "i"       "<i>"		"html-italic"             ("<i>" (r . "Text: ") "</i>"))
   (phys    "u"       "<u>"		"html-underline"          ("<u>" (r . "Text: ") "</u>"))
   (phys    "f"       "<tt>"		"html-fixed"              ("<tt>" (r . "Text: ") "</tt>"))
   (phys    "x"       "<s>"		"html-strikethru"         ("<s>" (r . "Text: ") "</s>"))
   (phys    "^"       "<sup>"		"html-superscript"        ("<sup>" (r . "Text: ") "</sup>"))
   (phys    "_"       "<sub>"		"html-subscript"          ("<sub>" (r . "Text: ") "</sub>"))
   (phys    "r"       "<render"		"html-render"             ("<render tag=\"" (p . "Render: ") "\" style=\"" (p . "Render as: ") "\">"))

   ;;headers
   (header  "1"	      "<h1>"     	"html-header-1"       	  ("<h1>" (r . "Header: ") "</h1>"))
   (header  "2"       "<h2>"		"html-header-2"       	  ("<h2>" (r . "Header: ") "</h2>"))
   (header  "3"       "<h3>"		"html-header-3"       	  ("<h3>" (r . "Header: ") "</h3>"))
   (header  "4"       "<h4>"		"html-header-4"       	  ("<h4>" (r . "Header: ") "</h4>"))
   (header  "5"       "<h5>"		"html-header-5"       	  ("<h5>" (r . "Header: ") "</h5>"))
   (header  "6"       "<h6>"		"html-header-6"       	  ("<h6>" (r . "Header: ") "</h6>"))

   (note    "a"	      "<abstract>"      "html-abstract"       	  ("<abstract>\n" r "\n</abstract>\n"))
   (note    "n"	      "<note role="     "html-note"           	  ("<note role=\"" (p . "Note role:") "\">" (r . "Note text: ") "</note>"))
   (note    "f"	      "<footnote>"	"html-footnote"		  ("<footnote>" (r . "Footnote: ") "</footnote>"))
   (note    "m"	      "<margin>"	"html-margin"         	  ("<margin>" (r . "Margin note: ") "</margin>"))

   ;; forms
   (form    "f"	      "<form"           "html-form"		  ("<form action=\"" (p . "Action: ") "\">\n" r "\n</form>\n"))
   (form    "t"	      "<input"		"html-input-text"	  ("<input name=\"" (p . "Name: ") "\"" " size=\"" (p . "Size: ") "\">"))
   (form    "i"	      "<input"		"html-input-int"     	  ("<input type=\"INT\" name=\"" (p . "Name: ") "\" size=\"" (p . "Number of digits: ") "\">"))
   (form    "."	      "<input"		"html-input-float"   	  ("<input type=\"FLOAT\" name=\"" (p . "Name: ") "\" size=\"" (p . "Size: ") "\">"))
   (form    "d"	      "<input"		"html-input-date"    	  ("<input type=\"DATE\" name=\"" (p . "Name: ") "\" size=\"" (p . "Size: ") "\">"))
   (form    "u"	      "<input"		"html-input-url"     	  ("<input type=\"URL\" name=\"" (p . "Name: ") "\" size=\"" (p . "Size: ") "\">"))
   (form    "c"	      "<input"		"html-input-check"   	  ("<input type=\"CHECKBOX\" name=\"" (p . "Name: ") "\">"))
   (form    "r"	      "<input"		"html-input-radio"   	  ("<input type=\"RADIO\" name=\"" (p . "Name: ") "\">"))
   (form    "g"	      "<input"		"html-input-image"   	  ("<input type=\"IMAGE\" name=\"" (p . "Name: ") "\" src=\"" (p . "Image URL: ") "\">"))
   (form    "s"	      "<input"		"html-input-scribble" 	  ("<input type=\"SCRIBBLE\" name=\"" (p . "Name: ") "\" size=\"" (p . "Size: ") "\">"))
   (form    "a"	      "<input"		"html-input-audio"    	  ("<input type=\"AUDIO\" name=\"" (p . "Name: ") "\">"))
   (form    "b"	      "<input"		"html-input-submit"   	  ("<input type=\"SUBMIT\" value=\"" (p . "Submit button text: ") "\">"))
   (form    "x"	      "<input"		"html-input-reset"    	  ("<input type=\"RESET\" value=\"" (p . "Reset button text: ") "\">"))
   (form    "p"	      "<textarea"	"html-input-textarea"	  ("<textarea name=\"" (p . "Name: ") "\" rows=" (p . "Rows: ") " cols=" (p . "Columns: ") ">\n" r "\n</textarea>\n"))
   (form    "c"	      "<select"		"html-input-select"	  ("<select name=\"" (p . "Name: ") "\">\n" r "\n\n</select>\n")"<select")


   ;;lists
   (list    "o"	      "<ol>"		"html-ordered-list"   	  (& "<ol>" > "\n<li> " > (r . "Item: ") "\n</ol>" >))
   (list    "u"	      "<ul>"		"html-unordered-list" 	  (& "<ul>" > "\n<li> " > (r . "Item: ") "\n</ul>" >))
   (list    "r"	      "<dir>"		"html-directory"      	  (& "<dir>" > "\n<li> " > (r . "Item: ") "\n</dir>" >))
   (list    "m"	      "<menu>"		"html-menu"		  (& "<menu>" > "\n<li> " > (r . "Item: ") "\n</menu>" >))
   (list    "d"	      "<dl>"		"html-definition-list" 	  (& "<dl>" > "\n<dt> " > (p . "Term: ") "\n<dd> " > (r . "Definition: ") "\n</dl>" >))

   ;;anchors
   (anchor  "n"	      "<a name="	"html-target-anchor"	  ("<a name=\"" (p . "Anchor name: ") "\">" (r . "Anchor text: ") "</a>"))
   (anchor  "l"	      "<a href="        "html-anchor"          	  ("<a href=\"" (p . "URL: ") "\">" (r . "Anchor text: ") "</a>"))                

   ;;graphics
   (graphic "\C-c\C-i" "<img src="	"html-image"		  ("<img src=\"" (r . "Image URL: ") "\">"))

   ;;text elements
   (textel  "\e\C-m"  nil		"html-paragraph"	  ("<p>\n"))
   (textel  "\C-c-"    nil		"html-horizontal-rule"	  (& "<hr>\n"))
   (textel  "\C-c\C-m" nil		"html-break"		  ("<br>\n"))

   ;;head elements
   (head    "t"	      "<title>"		"html-title"           ("<title>" (r . "Document title: ") "</title>"))
   (head    "i"	      "<isindex>"	"html-isindex"         ("<isindex>\n"))
   (head    "n"	      "<nextid>"	"html-nextid"          ("<nextid>\n"))
   (head    "l"	      "<link"		"html-link"            ("<link href=\"" p "\">"))
   (head    "b"       "<base"		"html-base"            ("<base href=\"" r "\">"))
   ))

;;}}}

;;{{{ context guessing

;; guess where we are in indented lists based on the last list token.
;; it would be much better to try to match </ul> to <ul>, and </ol> to <ol>
;; etc, but that is pretty unwieldy and slow.

(defvar html-helper-any-list-item "<li>\\|<dt>\\|<dd>")
(defvar html-helper-any-list-start "<dl>\\|<ul>\\|<ol>\\|<menu>\\|<dir>")
(defvar html-helper-any-list-end "</dl>\\|</ul>\\|</ol>\\|</menu>\\|</dir>")
(defvar html-helper-any-list (format "\\(%s\\)\\|\\(%s\\)\\|\\(%s\\)"
                                     html-helper-any-list-item
				     html-helper-any-list-start
				     html-helper-any-list-end))
(defvar html-helper-search-limit 2000 "limit on how far back we search")

(defun html-helper-guess-context ()
  "figure out what the last list type thing before point is."
  (save-excursion
    (let* ((lim (max (point-min) (- (point) html-helper-search-limit)))
	   (context (if (re-search-backward html-helper-any-list lim t)
			(cond ((match-beginning 1) 'item)
			      ((match-beginning 2) 'start)
			      ((match-beginning 3) 'end)
			      (t 'error))
		      nil)))
      (cons context (current-indentation)))))

(defun html-helper-print-context ()
  (interactive)
  (message "%s" (html-helper-guess-context)))

;;}}}
;;{{{ indentation

(defvar html-helper-print-indent-info nil
  "If t, indent will print out information as a message.")

(defun html-helper-indent-command ()
  "Command for indenting text. Just calls html-helper-indent."
  (interactive)
  (html-helper-indent))

;; some of the ideas are borrowed from cc-mode.el.

;; lots of special cases because we're not doing true parsing, we're
;; trying to guess what to do based on what the last item cookie was.

;; this code works best if the cookies that are the beginnings of menus
;; are on the left end of the line, and are already indented.

(defun html-helper-indent ()
  "indentation workhorse function."
  (if html-helper-never-indent
      ()
    (let ((m (point-marker))
	  (bol (progn (beginning-of-line) (point))))

      ;; unindent the line
      (delete-region (point) (progn (back-to-indentation) (point)))

      (let* ((where (html-helper-guess-context))
	     (context (car where))
	     (previ (cdr where))
	     (newi (cond 
		    ((eq context 'end) previ)
		    ((eq context 'item) previ)
		    ((eq context 'start) (+ previ html-helper-basic-offset))
		    (t previ))))

	;; newi is set to the basic indentation, now adjust indentation
	;; based on what the current line is.
	(if (looking-at html-helper-any-list)
	    (cond

	     ;; list token and last line was an end?
	     ;; Probably inside a continued item - go backwards.
	     ((and (match-beginning 1) (eq context 'end))
	      (setq newi (- newi html-helper-item-continue-indent)))

	     ;; end of list and last line was an end?
	     ;; Probably inside a continued item - go backwards twice
	     ((and (match-beginning 3) (eq context 'end))
	      (setq newi (- newi html-helper-item-continue-indent html-helper-basic-offset)))

	     ;; Any other end of list?
	     ;; Indent negative
	     ((match-beginning 3)
	      (setq newi (- newi html-helper-basic-offset)))

	     ;; start of list and last line
	     ;; Beginning of continued item - go forwards
	     ((and (match-beginning 2) (eq context 'item))
	      (setq newi (+ newi html-helper-item-continue-indent))))

	  ;; we're not any sort of item, must be text.
	  (cond
	   ;; last line an item?
	   ;; Beginning of continued item - go forward
	   ((eq context 'item)
	    (setq newi (+ newi html-helper-item-continue-indent)))))

	(if html-helper-print-indent-info
	    (message "Context: %s, Previous: %s New: %s" context previ newi))

	;; just in case
	(if (< newi 0)
	    (setq newi 0))
	(indent-to newi newi)

	;; adjust point to where it was before, or at start of indentation
	(goto-char (marker-position m))
	(if (< (current-column) (current-indentation))
	    (back-to-indentation))))))

;;}}}

;;{{{ timestamps

(defun html-helper-update-timestamp ()
  "Basic function for updating timestamps. It finds the timestamp in
the buffer by looking for html-helper-timestamp-start, deletes all text
up to html-helper-timestamp-end, and runs html-helper-timestamp-hook
which will presumably insert an appropriate timestamp in the buffer."
  (save-excursion
    (goto-char (point-max))
    (if (not (search-backward html-helper-timestamp-start nil t))
	(message "timestamp delimiter start was not found")
      (let ((ts-start (+ (point) (length html-helper-timestamp-start)))
	    (ts-end (if (search-forward html-helper-timestamp-end nil t)
			(- (point) (length html-helper-timestamp-end))
		      nil)))
	(if (not ts-end)
	    (message "timestamp delimiter end was not found. Type C-c C-t to insert one.")
	  (delete-region ts-start ts-end)
	  (goto-char ts-start)
	  (run-hooks 'html-helper-timestamp-hook)))))
  nil)

(defun html-helper-default-insert-timestamp ()
  "Default timestamp insertion function"
  (insert "Last modified: "
	  (current-time-string)
	  "\n"))

(defun html-helper-insert-timestamp-delimiter-at-point ()
  "Simple function that inserts timestamp delimiters at point, useful
for adding timestamps to existing buffers."
  (interactive)
  (insert html-helper-timestamp-start)
  (insert html-helper-timestamp-end))

;;}}}
;;{{{ html-helper-insert-new-buffer-strings

(tempo-define-template "html-skeleton" html-helper-new-buffer-template
		       nil
		       "Insert a skeleton for a HTML document")

(defun html-helper-insert-new-buffer-strings ()
  "Insert html-helper-new-buffer-strings."
  (tempo-template-html-skeleton))

;;}}}

;;{{{ html-helper-mode

(defun html-helper-mode ()
  "
Mode for editing HTML documents. For more documentation and the newest
version, see http://www.reed.edu/~nelson/tools/

The main function html-helper-mode provides is a bunch of keybindings
for the HTML cookies one inserts when writing HTML documents. Typing
the key sequence for a command inserts the corresponding cookie and
places point in the right place. If a prefix argument is supplied, the
cookie is instead wrapped around the region.

There is also code for indentation, timestamps, skeletons for new
documents, and lots of other neat features.

\\{html-helper-mode-map}
Written by nelson@reed.edu, http://www.reed.edu/~nelson/
"
  (interactive)
  (kill-all-local-variables)

  (use-local-map html-helper-mode-map)
  (setq local-abbrev-table html-helper-mode-abbrev-table)
  (set-syntax-table html-helper-mode-syntax-table)

  (setq mode-name "HTML")
  (setq major-mode 'html-helper-mode)

  (make-local-variable 'comment-start)
  (make-local-variable 'comment-end)
  (make-local-variable 'comment-column)
  (make-local-variable 'comment-start-skip)
  (make-local-variable 'indent-line-function)

  (setq comment-start "<!-- "
	comment-end " -->"
	comment-start-skip "<!--[ \t]*"
	comment-column 0
	indent-line-function 'html-helper-indent)

  (tempo-use-tag-list 'html-helper-tempo-tags "\\(\\(<\\|&\\).*\\)\\=")
  
  (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))
  
  (run-hooks 'html-helper-mode-hook))

;;}}}

;;{{{ patterns for hilit19

;; Define some useful highlighting patterns for the hilit19 package.
;; These will activate only if the function hilit-set-mode-patterns
;; is already bound - ie, if hilit19 has already been loaded when this
;; mode is loaded. Nonoptimal. I could put this in the mode function,
;; but then that has other problems.

;; suggestions for highlight patterns are most welcome. I tried to
;; choose a middle ground between lots of highlighting (ugly and slow)
;; and only a little bit (not so useful).

(if (fboundp 'hilit-set-mode-patterns)
    (hilit-set-mode-patterns
     'html-helper-mode
     '(("<!--" "-->" comment)           
       ("<a\\b" ">" define)
       ("</a>" nil define)
       ("<img\\b" ">" include)
       ("<b>" "</b>" bold)
       ("<i>" "</i>" italic)
       ("<u>" "</u>" underline)
       ("&" ";" string)
       ("<" ">" keyword))
     nil 'case-insensitive)
  nil)

;;}}}

(provide 'html-helper-mode)
(run-hooks 'html-helper-load-hook)

;;; html-helper-mode.el ends here