root/trunk/tdtd/tdtd.el

Revision 6, 73.7 KB (checked in by tkg, 3 years ago)

Updated to match a later version I had lying around.

Line 
1;;;; tdtd.el --- Tony's DTD mode
2;; $Id: tdtd.el,v 1.45 2001-08-31 23:15:22-04 tkg Exp $
3;; $Name: tdtd08b1 $
4
5;; Copyright (C) 1996, 1997, 1998, 1999, 2001, 2007 Tony Graham
6
7;; Author: Tony Graham <tkg@menteith.com>
8;; Contributors: Juanma Barranquero, Adam di Carlo
9
10;;; This file is not part of GNU Emacs.
11
12;; This program is free software; you can redistribute it and/or
13;; modify it under the terms of the GNU General Public License
14;; as published by the Free Software Foundation; either version 2
15;; of the License, or (at your option) any later version.
16;;
17;; This program is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20;; GNU General Public License for more details.
21;;
22;; You should have received a copy of the GNU General Public License
23;; along with this program; if not, write to the Free Software
24;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
26
27;;;; Commentary:
28
29;; Macros for editing DTDs
30
31;; Requires tdtd-font.el
32;; Requires 'etags for `find-tag-default'
33;; Requires 'imenu for "TAGS" menu
34;; Requires 'make-regexp for tdtd-font.el
35;; Requires 'reporter for `dtd-submit-bug-report'
36;; Send bugs to tdtd-bug@menteith.com
37
38
39;;;; Code:
40(eval-and-compile
41  (require 'font-lock))
42(eval-and-compile
43  (autoload 'sgml-validate "psgml"))
44(eval-and-compile
45  (autoload 'reporter-submit-bug-report "reporter"))
46;; XEmacs users don't always have imenu.el installed, so use
47;; condition-case to cope if we cause an error by requiring imenu.
48(eval-and-compile
49  (condition-case nil
50        (require 'imenu)
51    (error nil)))
52
53;; We need etags for `find-tag-default'
54(require 'etags)
55(require 'cl)
56(require 'tdtd-font "tdtd-font")
57
58(provide 'tdtd)
59
60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
61;; Version information
62
63(defconst tdtd-version "0.8b2"
64  "Version number of tdtd.")
65
66(defun tdtd-version ()
67  "Returns the value of the variable `tdtd-version'."
68  tdtd-version)
69
70(defconst tdtd-maintainer-address "tdtd-bug@menteith.com")
71
72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
73;; Variables
74
75(defvar dtd-autodetect-type t
76  "*Non-nil enables autodetection of XML or SGML when entering `dtd-mode'.")
77
78(defvar dtd-xml-flag nil
79  "*Non-nil enables XML-specific behaviour, where implemented.")
80
81(defvar dtd-decl-flag nil
82  "*Non-nil enables SGML Declaration-specific behaviour, where implemented.")
83
84(defvar dtd-sys-decl-flag nil
85  "*Non-nil enables System Declaration-specific behaviour, where implemented.")
86
87(defun dtd-xml-p ()
88  "Returns t when XML-specific behaviour is enabled, otherwise returns nil."
89  (if dtd-xml-flag
90      t nil))
91
92(defun dtd-decl-p ()
93  "Returns t when SGML Declaration-specific behaviour is enabled, otherwise nil."
94  (if dtd-decl-flag
95      t nil))
96
97(defun dtd-sys-decl-p ()
98  "Returns t when System Declaration-specific behaviour is enabled, otherwise nil."
99  (if dtd-sys-decl-flag
100      t nil))
101
102(defvar dtd-use-after-change-functions-flag t
103  "*Non-nil enables use of after-change functions")
104
105(defvar dtd-indent-tabs-mode nil
106  "*Initial value of indent-tabs-mode on entering dtd-mode")
107
108(defvar dtd-default-filespec "*.dtd *.ent"
109  "*Inital prompt value for `dtd-etags''s FILESPEC argument.")
110
111(defvar dtd-filespec-history (list dtd-default-filespec)
112  "Minibuffer history list for `dtd-etags' and `dtd-grep''s FILESPEC argument.")
113
114(defvar dtd-grep-pattern-history nil
115  "Minibuffer history list for `dtd-grep''s PATTERN argument.")
116
117(defvar dtd-grep-case-sensitive-flag nil
118  "*Non-nil disables case insensitive searches by `dtd-grep'.")
119
120(defvar dtd-grep-command-format
121  "grep -n %s -- '%s' %s"
122  "*Format string for the grep command called by `dtd-grep'.")
123
124;; Aren't shell escapes fun!
125;; "\" -> "\\\\\\"
126;; any other significant character -> "\\" + character
127(defvar dtd-etags-regex-option
128  "--regex=/\\<\\!\\\\\\(ELEMENT\\\\\\|ENTITY\\[\\ \\\\\\t]\\+%\\\\\\|NOTATION\\\\\\|ATTLIST\\\\\\)\\[\\ \\\\\\t]\\+\\\\\\(\\[^\\ \\\\\\t]\\+\\\\\\)/\\\\\\2/"
129  "*Complete, including \"--regex=\", etags regular expression option string
130for the etags command line for extracting tags (in the Emacs sense)
131from DTDs.")
132;; Use this regex with 4NT:
133;; "--regex=\"/<!\\(ELEMENT\\|ENTITY[ \\t]+%%\\|NOTATION\\|ATTLIST\\)[\\t]+\\([^ \\t]+\\)/\\2/\""
134
135(defvar dtd-etags-program "etags"
136  "*Name (and possibly path) of the etags program")
137
138(defvar dtd-etags-output-file "TAGS"
139  "*Name of the etags output file")
140
141(defvar dtd-attribute-type-history
142  (list "CDATA" "ID" "IDREF" "IDREFS"
143        "ENTITY" "ENTITIES" "NMTOKEN" "NMTOKENS" "NOTATION ")
144  "Minibuffer history list for attribute types.")
145
146(defvar dtd-attribute-tag-history nil
147  "Minibuffer history list for attribute tags.")
148
149(defvar dtd-attribute-default-history
150  (list "#IMPLIED" "#REQUIRED")
151  "Minibuffer history list for attribute types.")
152
153(defvar dtd-default-element-type-name nil
154  "Default for element type names.")
155
156(defvar dtd-declared-element-type-names nil
157  "List of element type names recently declared.")
158
159(defvar dtd-referenced-element-type-names nil
160  "List of element type names recently entered in content models.")
161
162(defvar dtd-declared-parameter-entity-names nil
163  "List of parameter entity names recently declared.")
164
165(defvar dtd-referenced-parameter-entity-names nil
166  "List of parameter entity names recently entered in content models.")
167
168(defvar dtd-element-type-name-history nil
169  "Minibuffer history list for element type names.")
170
171(defvar dtd-parameter-entity-value-history nil
172  "Minibuffer history list for parameter entity values.")
173
174(defvar dtd-external-entity-public-history nil
175  "Minibuffer history list for external entity public identifiers.")
176
177(defvar dtd-external-entity-system-history nil
178  "Minibuffer history list for external entity system identifiers.")
179
180(defvar dtd-element-comment-history nil
181  "Minibuffer history list for element comments.")
182
183(defvar dtd-element-content-spec-history
184  (list "(#PCDATA)" "EMPTY" "ANY")
185  "Minibuffer history list for element content specifications.")
186
187(defvar dtd-upcase-name-comment-flag nil
188  "*Non-nil enables converting descriptive name comments to uppercase.")
189
190(defvar dtd-prompt-descriptive-name t
191  "*Non-nil enables prompting for descriptive names of elements, etc.")
192
193(defvar dtd-prompt-descriptive-comment t
194  "*Non-nil enables prompting for descriptive comments for elements, etc.")
195
196(defvar dtd-outdent-attribute-pe nil
197  "*Non-nil enables outdenting parameter entities used for attributes.")
198
199;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
200;; Character sequences
201
202(defvar dtd-sgml-mdo "<!"
203  "*Markup Delimiter Open (MDO) character sequence")
204
205(defvar dtd-sgml-mdc ">"
206  "*Markup Delimiter Close (MDC) character sequence")
207
208(defvar dtd-comment-start "<!--"
209  "*Comment start character sequence")
210
211(defvar dtd-comment-end "-->"
212  "*Comment end character sequence")
213
214;; SGML Syntactic Literals
215(defvar dtd-empty-literal "EMPTY"
216  "EMPTY element syntactic literal")
217
218;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
219;; Variables controlling indentation
220
221(defvar dtd-dtd-max-column 70
222  "*Rightmost column for text in the DTD")
223
224(defvar dtd-mdc-indent-column (1- dtd-dtd-max-column)
225  "*Column at which to insert a MDC")
226
227(defvar dtd-comment-start-column 25
228  "*Column for starting text in a comment")
229
230(defvar dtd-comment-max-column (- dtd-dtd-max-column 3)
231  "*Maximum column number for text in a comment")
232
233;; (defvar dtd-sgml-comment-max-column (- dtd-dtd-max-column 2))
234
235(defvar dtd-element-name-column 12
236  "*Column for element name in an element declaration")
237
238(defvar dtd-element-tag-omission-column 25
239  "*Column for inserting the omissibility indicators, if used")
240
241(defvar dtd-element-content-spec-start-column 29
242  "*Column at which to start content model")
243
244(defvar dtd-element-content-spec-continuation-column 30
245  "*Column at which to start second and subsequent lines of content model")
246
247(defvar dtd-xml-element-content-spec-start-column 25
248  "*Column at which to start XML content model")
249
250(defvar dtd-xml-element-content-spec-continuation-column
251  (1+ dtd-xml-element-content-spec-start-column)
252  "*Column at which to start second and subsequent lines of XML content model")
253
254(defvar dtd-entity-entity-value-start-column 24
255  "*Column at which to start parameter entity's entity value")
256
257(defvar dtd-entity-entity-value-continuation-column
258  (1+ dtd-entity-entity-value-start-column)
259  "*Column at which to start second and subsequent lines of parameter entity value")
260
261(defvar dtd-attribute-name-column (+ dtd-element-name-column 2)
262  "*Indent for inserting attribute names in attribute definitions")
263
264(defvar dtd-attribute-default-column (- dtd-dtd-max-column 10)
265  "*Indent for inserting attribute defaults in attribute definitions")
266
267(defvar dtd-line-comment
268  (concat
269   dtd-comment-start
270   " "
271   (make-string
272    (- dtd-dtd-max-column
273       (length (concat dtd-comment-start "  " dtd-comment-end)))
274    ?=)
275   " "
276   dtd-comment-end
277   "\n")
278  "*Separator comment line: \"<!-- ==== -->\".")
279
280(defvar dtd-init-comment-column 16)
281
282(defvar dtd-init-comment-fill-prefix
283  (concat
284   dtd-comment-start
285   (make-string (- dtd-init-comment-column (length dtd-comment-start))
286                ?\ ))
287  "*Prefix for comments making up the initial comment in a module.")
288
289(defvar dtd-design-comment-start-column (+ (length dtd-comment-start) 2)
290  "*Column for starting text in a \"Design Considerations\" comment")
291
292
293;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
294;; Functions
295
296(defun dtd-autodetect-type ()
297  "Check for initial declaration and set flags accordingly."
298  (interactive)
299  (save-excursion
300    (goto-char (point-min))
301    (skip-chars-forward "\\s-")
302    (cond
303     ((looking-at "<\\?xml")
304      (setq dtd-xml-flag t)
305      (setq dtd-decl-flag nil)
306      (setq dtd-sys-decl-flag nil))
307     ((looking-at "<!SGML")
308      (setq dtd-xml-flag nil)
309      (setq dtd-decl-flag t)
310      (setq dtd-sys-decl-flag nil))
311     ((looking-at "<!SYSTEM")
312      (setq dtd-xml-flag nil)
313      (setq dtd-decl-flag nil)
314      (setq dtd-sys-decl-flag t))
315     (t
316      (setq dtd-xml-flag nil)
317      (setq dtd-decl-flag nil)
318      (setq dtd-sys-decl-flag nil)))))
319
320;; If you want to untabify everything every time, add this to functions
321;;  (untabify (point-min) (point-max))
322;; Its easier to just not insert tabs using:
323;;  (setq indent-tabs-mode nil)
324(defun dtd-untabify-buffer ()
325  "Untabify the entire buffer"
326  (interactive)
327  (untabify (point-min) (point-max)))
328
329(defun dtd-fix-entities ()
330  "Quick and dirty addition of \";\" to entity references lacking it"
331  (interactive)
332  (save-excursion
333    (goto-char (point-min))
334    (while
335        (re-search-forward "\\([%&][-A-Za-z0-9.:_]+\\)\\([^-A-Za-z0-9.:_;]\\)" nil t)
336      (replace-match "\\1;\\2" nil nil)
337      ;; match 2 may be the start of another entity reference
338      (goto-char (match-beginning 2)))))
339
340(defun dtd-toggle-debug-on-error ()
341  "Toggle the value of debug-on-error"
342  (interactive)
343  (setq debug-on-error (not debug-on-error))
344  (message "debug-on-error: %s" debug-on-error))
345
346(defun dtd-indent-or-newline-to (target-column)
347  "Indent to TARGET COLUMN or, if at or past target, insert newline and indent."
348  (if (>= (current-column)
349          target-column)
350      (insert "\n"))
351  (indent-to target-column))
352
353(defun dtd-center-comment (comment)
354  "Center a comment on line"
355  (interactive "sComment: ")
356  (insert dtd-comment-start)
357  (indent-to (/ (- dtd-dtd-max-column (length comment)) 2))
358  (insert comment)
359  (indent-to dtd-comment-max-column)
360  (insert dtd-comment-start)
361  (insert "\n"))
362
363(defun dtd-recenter-comment ()
364  "Recenter text in a presumably modified comment line"
365  (interactive)
366  (save-excursion
367    (save-match-data
368      (beginning-of-line)
369      (if (looking-at "^<!--\\s-*\\(\\w+\\(\\W+\\w+\\)*\\)\\s-*-->$")
370          (let ((contents (match-string 1)))
371            (delete-region (match-beginning 0) (match-end 0))
372            (dtd-center-comment contents))))))
373
374(defun dtd-filled-comment (comment)
375  "Insert a comment with \"=\" in most of the whitespace"
376  (interactive "sComment: ")
377  (dtd-comment comment "="))
378
379;; TDTD house style puts all comments starting on a favourite column
380(defun dtd-comment (comment &optional fill-character)
381  "Insert COMMENT starting at the usual column.
382
383With a prefix argument, e.g. \\[universal-argument] \\[dtd-comment], insert separator comment
384lines above and below COMMENT in the manner of `dtd-big-comment'."
385  (interactive "sComment: ")
386  (if current-prefix-arg
387      (insert dtd-line-comment))
388  (insert "\n")
389  (backward-char)
390  (let ((fill-column (1- dtd-comment-max-column))
391        (fill-prefix (make-string (1- dtd-comment-start-column) ?\ ))
392        (comment-start dtd-init-comment-fill-prefix)
393        (saved-auto-fill-function auto-fill-function))
394    (auto-fill-mode 1)
395    (insert dtd-comment-start)
396    (if (and
397         (stringp fill-character)
398         (not (string-equal fill-character "")))
399        (progn
400          (insert " ")
401          (insert (make-string (- dtd-comment-start-column
402                                  (current-column)
403                                  2)
404                               (string-to-char fill-character)))))
405    (indent-to (1- dtd-comment-start-column))
406    (fill-region (point) (save-excursion
407                           (insert comment)
408                           (point))
409                 nil
410                 1
411                 1)
412    ;; The fill does the right thing, but in Emacs 19.34 it always ends with
413    ;; an extra newline, so we delete the newline.
414    (if (bolp)
415        (delete-backward-char 1))
416    (if (not saved-auto-fill-function)
417        (auto-fill-mode 0))
418    (if (and
419         (stringp fill-character)
420         (not (string-equal fill-character "")))
421        (progn
422          (insert " ")
423          (insert (make-string (- dtd-comment-max-column
424                                  (current-column)
425                                  1)
426                               (string-to-char fill-character)))))
427    (indent-to dtd-comment-max-column)
428    (insert dtd-comment-end)
429    (insert "\n")
430    (if current-prefix-arg
431        (insert dtd-line-comment))
432    (if font-lock-mode
433        (save-excursion
434          (font-lock-fontify-region
435           (dtd-font-lock-region-point-min)
436           (dtd-font-lock-region-point-max))))))
437
438(defun dtd-recomment ()
439  "Fix text position in a presumably modified comment line"
440  (interactive)
441  (save-excursion
442    (save-match-data
443      (beginning-of-line)
444      (if (looking-at "^\\(<!--*\\)?\\s-*\\([^ \t\n>]+\\([- \t]+[^- \t\n>]+\\)*\\)\\s-*\\(-->\\)?\n")
445          (let ((contents (match-string 2)))
446            (delete-region (match-beginning 0) (match-end 0))
447            (dtd-comment contents))))))
448
449(defun dtd-join-comments (mark point)
450  "Join comments by removing the \"interior\" comment delimiters."
451  (interactive "r")
452  (save-excursion
453    (save-match-data
454      (goto-char (min mark point))
455      ;; Insert a comment start if there isn't one at the beginning.
456      (if (not (looking-at dtd-comment-start))
457          (progn
458            (if (looking-at (make-string (length dtd-comment-start) ?\ ))
459                (delete-region (match-beginning 0) (match-end 0)))
460            (insert dtd-comment-start)))
461      ;; Delete the "interior" comment delimiters
462      (goto-char (min mark point))
463      (while (re-search-forward
464              (concat "[ \t\n]*" dtd-comment-end "\n" dtd-comment-start)
465              (max mark point) t)
466        (replace-match (concat "\n" (make-string
467                                     (length dtd-comment-start)
468                                     ?\ ))
469                       nil nil))))
470  ;; Insert a comment end if there isn't one at the end
471  ;;      (goto-char (- (max mark point)
472  ;;                (length dtd-comment-end)))
473  ;;      (if (not (looking-at dtd-comment-end))
474  ;;     
475  (if font-lock-mode
476      (save-excursion
477        (font-lock-fontify-region
478         (dtd-font-lock-region-point-min)
479         (dtd-font-lock-region-point-max)))))
480
481(defun dtd-declare-element (element-tag element-name element-comment content-spec)
482  "Insert an element type declaration at the current point.
483
484ELEMENT-TAG is the element type name as it appears in start- and
485end-tags.  ELEMENT-NAME is a descriptive name for the element.  It is
486output as a comment.  ELEMENT-COMMENT is a further comment about the
487element.  This comment is not output if ELEMENT-COMMENT is an empty
488string.  CONTENT-SPEC is the element type's content specification.
489
490When dtd-xml-flag in nil, the omitted tag minimization parameter is
491output as part of the element type declaration.  When CONTENT-SPEC is
492\"EMPTY\", the minimization parameter is \"- o\", otherwise it is \"- -\"
493
494When dtd-upcase-name-comment-flag is non-nil, the text of the
495descriptive element name is converted to uppercase before output.
496
497ELEMENT-NAME is not prompted for if dtd-prompt-descriptive-name is
498nil, and ELEMENT-COMMENT is not prompted for if
499dtd-prompt-descriptive-comment is nil.
500
501An example inserted element type declaration is as follows:
502
503<!--                    Element name                               -->
504<!--                    Comment about the element: what it's for,
505                        or something about its behaviour           -->
506<!ELEMENT  element-tag  - - (insert, your, content, specification,
507                             here)                                   >
508"
509  (interactive
510   ;; Hackery and fakery
511   (let ((element-tag nil))
512     (list (progn
513             ;; set element-tag to what's read from the minibuffer
514             (setq element-tag
515                   (dtd-read-from-minibuffer
516                    "Element tag: "
517                    (car dtd-referenced-element-type-names)
518                    'dtd-referenced-element-type-names))
519             ;; complain if element-tag is an empty string and there's no
520             ;; default
521             (if (string-equal element-tag "")
522                 (if default
523                     (setq element-tag default)
524                   (error "You must supply an element tag name"))
525               ;; return element-tag is we had one already
526               element-tag))
527           (if dtd-prompt-descriptive-name
528               (read-from-minibuffer (format "<%s> descriptive name: "
529                                             element-tag)
530                                     ;; use element-tag as the default for
531                                     ;; the descriptive name since we often
532                                     ;; base the descriptive name on it
533                                     element-tag nil nil nil)
534             "")
535           (if dtd-prompt-descriptive-comment
536               (read-from-minibuffer (format "<%s> comment: "
537                                             element-tag)
538                                     nil nil nil
539                                     'dtd-element-comment-history)
540             "")
541           (read-from-minibuffer (format "<%s> content spec: "
542                                         element-tag)
543                                 nil nil nil
544                                 'dtd-element-content-spec-history))))
545  (if (not
546       (string-equal element-name ""))
547      (progn
548        (if dtd-upcase-name-comment-flag
549            (setq element-name (upcase element-name)))
550        (dtd-comment element-name)))
551  (if (not
552       (string-equal element-comment ""))
553      (dtd-comment element-comment))
554  (insert "\n")
555  (backward-char)
556  (insert "<!ELEMENT  ")
557  (insert element-tag)
558  (if dtd-autodetect-type
559      (dtd-autodetect-type))
560  (if (not dtd-xml-flag)
561      (progn
562        (dtd-indent-or-newline-to (1- dtd-element-tag-omission-column))
563        (if (string-equal content-spec dtd-empty-literal)
564            (insert "- o")
565          (insert "- -"))))
566  (dtd-indent-or-newline-to
567   (if dtd-xml-flag
568       (1- dtd-xml-element-content-spec-start-column)
569     (1- dtd-element-content-spec-start-column)))
570  (let ((fill-column dtd-mdc-indent-column)
571        (fill-prefix (make-string
572                      (if dtd-xml-flag
573                          (1-
574                           dtd-xml-element-content-spec-continuation-column)
575                        (1- dtd-element-content-spec-continuation-column))
576                      ?\ ))
577        (saved-auto-fill-function auto-fill-function))
578    (auto-fill-mode 1)
579    (fill-region (point) (save-excursion
580                           (insert content-spec)
581                           (point))
582                 nil
583                 1
584                 1)
585    ;; The fill does the right thing, but in Emacs 19.34 it always ends with
586    ;; an extra newline, so we delete the newline.
587    (if (bolp)
588        (delete-backward-char 1))
589    ;;    (setq content-spec-end (point))
590    (if (not saved-auto-fill-function)
591        (auto-fill-mode 0))
592    (dtd-indent-or-newline-to dtd-mdc-indent-column)
593    (insert (concat dtd-sgml-mdc "\n"))
594    (auto-fill-mode nil)
595    (if font-lock-mode
596        (save-excursion
597          (font-lock-fontify-region
598           (dtd-font-lock-region-point-min)
599           (dtd-font-lock-region-point-max))))
600    ;; Add to the list of element type names that has already been seen
601    (add-to-list 'dtd-declared-element-type-names element-tag)
602    ;; Work out whether it's safe to remove element-tag from the list of
603    ;; element type names that's been seen referenced in content models
604    ;; and parameter entity declarations
605    ;;
606    ;; Before working anything out, need to remove element-type-name
607    ;; from the start of dtd-referenced-element-type-names (since that's
608    ;; the minibuffer history list variable)
609    (setq dtd-referenced-element-type-names
610          (cdr dtd-referenced-element-type-names))
611    (if (member element-tag dtd-referenced-element-type-names)
612        ;; There has to be an easier way to do this.
613        (setq dtd-referenced-element-type-names
614              (catch 'roll-referenced-element-type-names
615                (let ((bypassed-element-type-names nil))
616                  (while (car dtd-referenced-element-type-names)
617                    (let ((element-type-name
618                           (car dtd-referenced-element-type-names)))
619                      (setq dtd-referenced-element-type-names
620                            (cdr dtd-referenced-element-type-names))
621                      (if (equal element-tag element-type-name)
622                          (throw
623                           'roll-referenced-element-type-names
624                           (append dtd-referenced-element-type-names
625                                   bypassed-element-type-names))
626                        (setq bypassed-element-type-names
627                              (reverse
628                               (add-to-list 'bypassed-element-type-names
629                                            element-type-name))))))))))
630    (dtd-analyse-content-spec content-spec)))
631
632(defun dtd-analyse-content-spec (content-spec)
633  "Eventually, decide if need declarations for anything referenced in CONTENT-SPEC.
634
635CONTENT-SPEC is an element type's content specification as provided to
636`dtd-declare-element' or `dtd-declare-parameter-entity'."
637;;  (message "%s" content-spec)
638  (if (not (or (string-equal content-spec "ANY")
639               (string-equal content-spec "CDATA")
640               (string-equal content-spec "EMPTY")
641               (string-equal content-spec "RCDATA")))
642      (let ((match-index nil)
643            (token-list nil))
644        (save-match-data
645          (while (string-match "\\(%?[-A-Za-z0-9#:._]+;?\\)"
646                               content-spec match-index)
647            (add-to-list 'token-list (substring content-spec
648                                               (match-beginning 1)
649                                               (match-end 1)))
650            (setq match-index (match-end 0))))
651        (while token-list
652          (let* ((token (car token-list))
653                 (pe-flag (string-equal "%" (substring token 0 1))))
654            (if (not (string-equal token "#PCDATA"))
655                (progn
656;;                (message "%s" token)
657                  (if pe-flag
658                      (progn
659                        (setq token (substring token
660                                               1
661                                               (1- (length token))))
662                        (if (not (member token
663                                         dtd-declared-parameter-entity-names))
664                            (add-to-list
665                             'dtd-referenced-parameter-entity-names
666                             token)))
667                    (if (not (member token
668                                     dtd-declared-element-type-names))
669                        (add-to-list 'dtd-referenced-element-type-names
670                                     token))))))
671            (setq token-list (cdr token-list))))))
672
673;;(defun dtd-update-element-lists-with-element
674;;  "Analyse the elements definition as if it had just been declared."
675;;  (if (looking-at "\\(<!ELEMENT\\)\\(\\s-+\\)\\sw+")
676;;        (progn
677;;          (goto-char (match-end 0))
678;;          (add-to-list 'dtd-declared-element-type-names (match-string 1))
679;;          (goto-char (match-end 2))
680;;          (if (looking-at "\\([-o]\\)\\s-+\\([-o]\\)\\s-+")
681;;              (goto-char (match-end 0)))
682;;          (if (looking-at ("("))
683;;              (
684
685(defun dtd-defined-in-comment (element-tag other-module)
686  "Insert an \"ELEMENT-TAG  is defined in OTHER-MODULE\" comment.
687
688ELEMENT-TAG is the element type name as it appears in start- and
689end-tags.  OTHER-MODULE is the name of the module in which the element
690is defined.
691
692An example inserted \"defined in\" comment is as follows:
693
694<!--       ELEMENT-TAG  is defined in OTHER-MODULE                 -->
695"
696  (interactive
697   ;; Hackery and fakery
698   (let ((element-tag nil))
699     (list (progn
700             ;; set element-tag to what's read from the minibuffer
701             (setq element-tag
702                   (dtd-read-from-minibuffer
703                    "Element tag: "
704                    (car dtd-referenced-element-type-names)
705                    'dtd-referenced-element-type-names))
706             ;; complain if element-tag is an empty string and there's no
707             ;; default
708             (if (string-equal element-tag "")
709                 (if default
710                     (setq element-tag default)
711                   (error "You must supply an element tag name"))
712               ;; return element-tag is we had one already
713               element-tag))
714           (read-from-minibuffer "module name: "))))
715  (insert "\n")
716  (backward-char)
717  (insert dtd-comment-start)
718  (dtd-indent-or-newline-to (1- dtd-element-name-column))
719  (insert element-tag)
720  (dtd-indent-or-newline-to (1- dtd-comment-start-column))
721  (insert (format "is defined in %s" other-module))
722  (indent-to dtd-comment-max-column)
723  (insert dtd-comment-end)
724  (insert "\n")
725  ;; Add to the list of element type names that we've already seen
726  (add-to-list 'dtd-declared-element-type-names element-tag)
727  ;; Remove element-type-name from the start of
728  ;; dtd-referenced-element-type-names (since that's the minibuffer
729  ;; history list variable)
730  (setq dtd-referenced-element-type-names
731        (cdr dtd-referenced-element-type-names))
732  (if (member element-tag dtd-referenced-element-type-names)
733      ;; There has to be an easier way to do this.
734      (setq dtd-referenced-element-type-names
735            (catch 'roll-referenced-element-type-names
736              (let ((bypassed-element-type-names nil))
737                (while (car dtd-referenced-element-type-names)
738                  (let ((element-type-name
739                         (car dtd-referenced-element-type-names)))
740                    (setq dtd-referenced-element-type-names
741                          (cdr dtd-referenced-element-type-names))
742                    (if (equal element-tag element-type-name)
743                        (throw
744                         'roll-referenced-element-type-names
745                         (append dtd-referenced-element-type-names
746                                 bypassed-element-type-names))
747                      (setq bypassed-element-type-names
748                            (reverse
749                             (add-to-list 'bypassed-element-type-names
750                                          element-type-name))))))))))
751  (if font-lock-mode
752      (save-excursion
753        (font-lock-fontify-region
754         (dtd-font-lock-region-point-min)
755         (dtd-font-lock-region-point-max)))))
756
757(defun dtd-read-from-minibuffer (prompt default history)
758  "Read from minibuffer with default and command history."
759(let ((value nil))
760  (if (string-equal
761       ""
762       (setq value
763             (read-from-minibuffer (if default
764                                       (format
765                                        "%s(default `%s') "
766                                        prompt default)
767                                     (format "%s" prompt))
768                                   nil nil nil
769                                   history)))
770             default
771             value)))
772
773(defun dtd-declare-attribute
774  (attribute-tag attribute-comment attribute-type attribute-default)
775  "Declare an attribute.
776
777ATTRIBUTE-TAG is the attribute name.  ATTRIBUTE-NAME is a descriptive
778name for the attribute.  It is output as a comment.  ATTRIBUTE-COMMENT
779is a further comment about the attribute.  This comment is not output
780if ATTRIBUTE-COMMENT is an empty string.  ATTRIBUTE-TYPE is the
781attribute's type or enumeration declaration.  ATTRIBUTE-DEFAULT is the
782attribute's default.
783
784ATTRIBUTE-COMMENT is not prompted for if
785dtd-prompt-descriptive-comment is nil.
786
787If called interactively and not after \"<!ATTLIST\", also calls
788`dtd-declare-attribute-list' before prompting for ATTRIBUTE-TAG.
789
790`dtd-declare-attribute' takes care of inserting or moving the \">\"
791that closes the attribute list declaration."
792  (interactive
793   (let ((attribute-tag nil))
794     ;; Hackery and fakery
795     ;; If we're not after "<!ATTLIST", call dtd-declare-attribute-list.
796     ;; This is an abomination, but don't know a better way to do it.
797     ;;
798     ;; Do `save-excursion', etc. while we check.
799     (if (not (save-excursion
800                (save-match-data
801                  (re-search-backward "^<!" nil t)
802                  (looking-at "<!ATTLIST"))))
803         (dtd-declare-attribute-list
804          ;; This is largely a repeat of the (interactive) statement
805          ;; in dtd-declare-attribute-list
806          (dtd-read-from-minibuffer
807           "Element type: "
808           (save-excursion
809             (save-match-data
810               (re-search-backward "^<!ELEMENT[ \t]+\\([^ \t\n]+\\)" nil t)
811               (match-string 1)))
812           'dtd-declared-element-type-names)))
813     (list (setq attribute-tag
814                 (dtd-read-from-minibuffer "Attribute tag: "
815                                           nil
816                                           'dtd-attribute-tag-history))
817           (if dtd-prompt-descriptive-comment
818               (dtd-read-from-minibuffer (format "\"%s\" comment: "
819                                                 attribute-tag)
820                                         nil nil)
821             "")
822           (dtd-read-from-minibuffer
823            (format "\"%s\" type or enumeration: " attribute-tag)
824            (car dtd-attribute-type-history)
825            'dtd-attribute-type-history)
826           (dtd-read-from-minibuffer
827            (format "\"%s\" default: " attribute-tag)
828            (car dtd-attribute-default-history)
829            'dtd-attribute-default-history))))
830  (if (and (stringp attribute-comment)
831           (not (string-equal attribute-comment "")))
832      (save-excursion
833        (re-search-backward "^<!ATTLIST" nil t)
834        (insert dtd-comment-start)
835        (dtd-indent-or-newline-to (1- dtd-attribute-name-column))
836        (if (and
837             dtd-outdent-attribute-pe
838             (string-equal (substring attribute-tag 0 1) "%"))
839            (delete-backward-char 1))
840        (insert attribute-tag)
841        (dtd-indent-or-newline-to (1- dtd-comment-start-column))
842        (let ((fill-column dtd-mdc-indent-column)
843              (fill-prefix (make-string
844                            (1- dtd-comment-start-column) ?\ ))
845              (saved-auto-fill-function auto-fill-function))
846          ;;    (auto-fill-mode 1)
847          ;;    (setq content-spec-start (point))
848          (fill-region (point) (save-excursion
849                                 (insert attribute-comment)
850                                 (insert "\n")
851                                 (point))
852                       nil
853                       1
854                       1)
855          ;; The fill does the right thing, but in Emacs 19.34 it
856          ;; always ends with an extra newline, so we delete the newline.
857          (if (bolp)
858              (delete-backward-char 1))
859          (dtd-indent-or-newline-to dtd-comment-max-column)
860          (insert dtd-comment-end)
861          (insert "\n"))
862        ;; Now see if we need to merge with a previous comment
863        (let ((previous-comment-beginning
864               (save-excursion
865                 (re-search-backward "<!" nil t 2)
866                 (if (looking-at dtd-comment-start)
867                     (point)
868                   nil))))
869          (if previous-comment-beginning
870              (dtd-join-comments previous-comment-beginning (point))))))
871  ;; Do it all again for the actual declaration
872  (let ((fill-column dtd-mdc-indent-column)
873        (fill-prefix (make-string
874                      (1- dtd-comment-start-column) ?\ ))
875        (saved-auto-fill-function auto-fill-function))
876    ;; If we're after a declaration, delete the preceding mdc and any
877    ;; whitespace around it.
878    (if (not (save-excursion
879               (re-search-backward "^<!\\|>" nil t)
880               (looking-at "<!ATTLIST")))
881        (save-excursion
882          (delete-region (point)
883                         (save-excursion
884                           (re-search-backward "[^ \t\n\r>]" nil t)
885                           (1+ (point))))))
886    (dtd-indent-or-newline-to (1- dtd-attribute-name-column))
887    (if (string-equal (substring attribute-tag 0 1) "%")
888        (delete-backward-char 1))
889    (insert attribute-tag)
890    (dtd-indent-or-newline-to (1- dtd-comment-start-column))
891    (fill-region (point) (save-excursion
892                           (insert attribute-type)
893                           (insert "\n")
894                           (point))
895                 nil
896                 1
897                 1)
898    ;; The fill does the right thing, but in Emacs 19.34 it always ends with
899    ;; an extra newline, so we delete the newline.
900    (if (bolp)
901        (delete-backward-char 1))
902    ;; We want the default to line up on dtd-attribute-default-column
903    ;; unless it's too long, in which case we want one space between
904    ;; the end of the default and the mdc (>).
905    (if (< (length attribute-default)
906           (- dtd-dtd-max-column dtd-attribute-default-column 1))
907        (dtd-indent-or-newline-to (1- dtd-attribute-default-column))
908      (dtd-indent-or-newline-to (- dtd-dtd-max-column
909                                   (length attribute-default)
910                                   2)))
911    (insert attribute-default)
912    ;; Insert an mdc only if there isn't one between here and the
913    ;; next mdo.
914    (if (or (looking-at "<")
915            (not (save-excursion
916                   (re-search-forward "^<!\\|>" nil t)
917                   (goto-char (match-beginning 0))
918                   (looking-at ">"))))
919        (progn
920          (dtd-indent-or-newline-to dtd-mdc-indent-column)
921          (insert dtd-sgml-mdc)))
922    (insert "\n")
923    (if font-lock-mode
924        (save-excursion
925          (font-lock-fontify-region
926           (dtd-font-lock-region-point-min)
927           (dtd-font-lock-region-point-max))))))
928
929(defun dtd-declare-attribute-list (element-type-name)
930  "Declare an attribute list."
931  (interactive
932   (list (dtd-read-from-minibuffer
933          "Element type: "
934          (save-excursion
935            (save-match-data
936              (re-search-backward "^<!ELEMENT[ \t]+\\([^ \t\n]+\\)" nil t)
937              (match-string 1)))
938          'dtd-declared-element-type-names)))
939  (insert (concat "<!ATTLIST  " element-type-name "\n")))
940
941(defun dtd-declare-notation
942  (notation-tag notation-name notation-comment public-identifier system-identifier)
943  "Insert a notation declaration.
944
945NOTATION-TAG is the notation name as it appears in notation
946references.  NOTATION-NAME is a descriptive name for the notation.  It
947is output as a comment.  NOTATION-COMMENT is a further comment about
948the notation.  The comment is not output if NOTATION-COMMENT is an
949empty string.  PUBLIC-IDENTIFIER is the notation\'s public identifier
950\(without '\"' characters).  SYSTEM-IDENTIFIER is the notation's system
951identifier (without '\"' characters).
952
953NOTATION-NAME is not prompted for if dtd-prompt-descriptive-name is
954nil, and NOTATION-COMMENT is not prompted for if
955dtd-prompt-descriptive-comment is nil.
956
957An example inserted notation type declaration is as follows:
958
959<!--                    Notation name                                -->
960<!--                    Comment about the notation: what it's for,
961                        or something about its behaviour           -->
962<!NOTATION notation-tag   PUBLIC
963\"-//Complete//NOTATION Public Identifier//EN\"
964                                                                   -->
965%notation-tag;
966"
967  (interactive
968   ;; Hackery and fakery
969   (let ((notation-tag nil))
970     (list (progn
971             ;; set notation-tag to what's read from the minibuffer
972             (setq notation-tag
973                   (dtd-read-from-minibuffer
974                    "Notation tag: "
975                    (car dtd-referenced-notation-type-names)
976                    'dtd-referenced-notation-type-names))
977             ;; complain if notation-tag is an empty string and there's no
978             ;; default
979             (if (string-equal notation-tag "")
980                 (if default
981                     (setq notation-tag default)
982                   (error "You must supply an notation tag name"))
983               ;; return notation-tag is we had one already
984               notation-tag))
985           (if dtd-prompt-descriptive-name
986               (read-from-minibuffer (format "`%s' descriptive name: "
987                                             notation-tag)
988                                     ;; use notation-tag as the default for
989                                     ;; the descriptive name since we often
990                                     ;; base the descriptive name on it
991                                     notation-tag nil nil nil)
992             "")
993           (if dtd-prompt-descriptive-comment
994               (read-from-minibuffer (format "`%s' comment: "
995                                             notation-tag)
996                                     nil nil nil
997                                     'dtd-notation-comment-history)
998             "")
999           (read-from-minibuffer (format "`%s' content spec: "
1000                                         notation-tag)
1001                                 nil nil nil
1002                                 'dtd-notation-content-spec-history))))
1003  (if (not
1004       (string-equal notation-name ""))
1005      (progn
1006        (if dtd-upcase-name-comment-flag
1007            (setq notation-name (upcase notation-name)))
1008        (dtd-comment notation-name)))
1009  (if (not
1010       (string-equal notation-comment ""))
1011        (dtd-comment notation-comment))
1012;;  (insert "\n")
1013;;  (backward-char)
1014  (insert "<!NOTATION ")
1015  (insert notation-tag)
1016  (dtd-indent-or-newline-to (1- dtd-element-content-spec-start-column))
1017  (if (not
1018       (string-equal public-identifier ""))
1019      (progn
1020        (insert "PUBLIC\n")
1021        (insert (concat "\"" public-identifier "\"\n")))
1022    (insert "SYSTEM "))
1023  (if (not
1024       (string-equal system-identifier ""))
1025      (insert (concat "\"" system-identifier "\"\n")))
1026  (indent-to dtd-mdc-indent-column)
1027  (insert dtd-sgml-mdc)
1028  (insert "\n")
1029  (if font-lock-mode
1030        (save-excursion
1031          (font-lock-fontify-region
1032           (dtd-font-lock-region-point-min)
1033           (dtd-font-lock-region-point-max)))))
1034
1035(defun dtd-declare-external-entity
1036  (entity-tag entity-name entity-comment public-identifier system-identifier)
1037  "Insert an entity declaration and references for an external public entity.
1038
1039ENTITY-NAME is a descriptive name for the entity.  It is output as a
1040comment.  ENTITY-COMMENT is a further comment about the entity.  The
1041comment is not output if ENTITY-COMMENT is an empty string.
1042ENTITY-TAG is the entity name as it appears in entity references.
1043PUBLIC-IDENTIFIER is the entity's public identifier (without '\"'
1044characters).  SYSTEM-IDENTIFIER is the entity's system identifier
1045\(without '\"' characters).
1046
1047ENTITY-NAME is not prompted for if dtd-prompt-descriptive-name is nil,
1048and ENTITY-COMMENT is not prompted for if
1049dtd-prompt-descriptive-comment is nil.
1050
1051An example inserted entity type declaration is as follows:
1052
1053<!--                    Entity name                                -->
1054<!--                    Comment about the entity: what it's for,
1055                        or something about its behaviour           -->
1056<!ENTITY % entity-tag   PUBLIC
1057\"-//Complete//ENTITY Public Identifier//EN\"
1058                                                                   -->
1059%entity-tag;
1060"
1061;;  (interactive "sEntity tag: \nsEntity name: \nsEntity comment: \nsPublic Identifier: \nsSystem Identifier: ")
1062  (interactive
1063   ;; Hackery and fakery
1064   (let ((entity-tag nil))
1065     (list (setq entity-tag
1066                 (read-from-minibuffer "External entity tag: "
1067                                       nil nil nil
1068                                       'dtd-referenced-parameter-entity-names))
1069           (if dtd-prompt-descriptive-name
1070               (read-from-minibuffer (format "`%%%s;\' descriptive name: "
1071                                             entity-tag)
1072                                     nil nil nil nil)
1073             "")
1074           (if dtd-prompt-descriptive-comment
1075               (read-from-minibuffer (format "`%%%s;' comment: "
1076                                             entity-tag)
1077                                     nil nil nil nil)
1078             "")
1079           (read-from-minibuffer (format "`%%%s;' public identifier: "
1080                                         entity-tag)
1081                                 nil nil nil
1082                                 'dtd-external-entity-public-history)
1083           (read-from-minibuffer (format "`%%%s;' system identifier: "
1084                                         entity-tag)
1085                                 nil nil nil
1086                                 'dtd-external-entity-system-history))))
1087  (if (not
1088       (string-equal entity-name ""))
1089      (progn
1090        (if dtd-upcase-name-comment-flag
1091            (setq entity-name (upcase entity-name)))
1092        (dtd-comment entity-name)))
1093;;  (insert "\n")
1094  (if (not
1095       (string-equal entity-comment ""))
1096      (progn
1097        (dtd-comment entity-comment)
1098        (insert "\n")))
1099;;  (insert "\n")
1100;;  (backward-char)
1101  (insert "<!ENTITY % ")
1102  (insert entity-tag)
1103  (if dtd-autodetect-type
1104      (dtd-autodetect-type))
1105  (dtd-indent-or-newline-to
1106   (if dtd-xml-flag
1107       (1- dtd-xml-element-content-spec-start-column)
1108       (1- dtd-element-content-spec-start-column)))
1109  (if (not
1110       (string-equal public-identifier ""))
1111      (progn
1112        (insert "PUBLIC\n")
1113        (insert (concat "\"" public-identifier "\"\n")))
1114    (insert "SYSTEM "))
1115  (if (not
1116       (string-equal system-identifier ""))
1117      (insert (concat "\"" system-identifier "\"\n")))
1118  (indent-to dtd-mdc-indent-column)
1119  (insert (concat dtd-sgml-mdc "\n"))
1120  (insert (concat "%" entity-tag ";\n"))
1121  (if font-lock-mode
1122        (save-excursion
1123          (font-lock-fontify-region
1124           (dtd-font-lock-region-point-min)
1125           (dtd-font-lock-region-point-max)))))
1126
1127(defun dtd-declare-parameter-entity
1128  (entity-tag entity-name entity-comment entity-value)
1129  "Insert a parameter entity declaration at the current point.
1130
1131ENTITY-NAME is a descriptive name for the entity.  It is output as a
1132comment.  ENTITY-COMMENT is a further comment about the entity.  The
1133comment is not output if ENTITY-COMMENT is an empty string.
1134ENTITY-TAG is the entity type name as it appears in start- and
1135end-tags.  ENTITY-VALUE is the entity's value specification, without
1136the '\"' characters.
1137
1138ENTITY-NAME is not prompted for if dtd-prompt-descriptive-name is nil,
1139and ENTITY-COMMENT is not prompted for if
1140dtd-prompt-descriptive-comment is nil.
1141
1142An example inserted parameter entity declaration is as follows:
1143
1144<!--                    Entity name                               -->
1145<!--                    Comment about the entity: what it's for,
1146                        or something about its behaviour           -->
1147<!ENTITY % entity-tag   \"parameter, entity, contents\"                >
1148"
1149  (interactive
1150   ;; Hackery and fakery
1151   (let ((entity-tag nil))
1152     (list (setq entity-tag
1153                 (read-from-minibuffer "Entity tag: "
1154                                       nil nil nil
1155                                       'dtd-referenced-parameter-entity-names))
1156           (if dtd-prompt-descriptive-name
1157               (read-from-minibuffer (format "`%%%s;\' descriptive name: "
1158                                             entity-tag)
1159                                     nil nil nil nil)
1160             "")
1161           (if dtd-prompt-descriptive-comment
1162               (read-from-minibuffer (format "`%%%s;' comment: "
1163                                             entity-tag)
1164                                     nil nil nil nil)
1165             "")
1166           (read-from-minibuffer (format "`%%%s;' value: "
1167                                         entity-tag)
1168                                 nil nil nil
1169                                 'dtd-parameter-entity-value-history))))
1170  (if (not
1171       (string-equal entity-name ""))
1172      (progn
1173        (if dtd-upcase-name-comment-flag
1174            (setq entity-name (upcase entity-name)))
1175        (dtd-comment entity-name)))
1176  (if (not
1177       (string-equal entity-comment ""))
1178      (dtd-comment entity-comment))
1179  (insert "<!ENTITY % ")
1180  (insert entity-tag)
1181  (dtd-indent-or-newline-to (1- dtd-entity-entity-value-start-column))
1182  (insert "\"")
1183  (let ((fill-column dtd-mdc-indent-column)
1184        (fill-prefix (make-string
1185                      (1- dtd-entity-entity-value-continuation-column) ?\ ))
1186        (saved-auto-fill-function auto-fill-function))
1187    (auto-fill-mode 1)
1188    (fill-region (point) (save-excursion
1189                           (insert entity-value)
1190                           (insert "\"")
1191                           (point))
1192                 nil
1193                 1
1194                 1)
1195    ;; The fill does the right thing, but in Emacs 19.34 it always ends with
1196    ;; an extra newline, so we delete the newline.
1197    (if (bolp)
1198        (delete-backward-char 1))
1199    (if (not saved-auto-fill-function)
1200        (auto-fill-mode 0))
1201    (dtd-indent-or-newline-to dtd-mdc-indent-column)
1202    (insert (concat dtd-sgml-mdc "\n"))
1203    (auto-fill-mode nil)
1204    (if font-lock-mode
1205        (save-excursion
1206          (font-lock-fontify-region
1207           (dtd-font-lock-region-point-min)
1208           (dtd-font-lock-region-point-max))))
1209    (add-to-list 'dtd-declared-parameter-entity-names entity-tag)
1210    (if (member entity-tag dtd-referenced-parameter-entity-names)
1211        (setq dtd-referenced-parameter-entity-names
1212              (delete entity-tag dtd-referenced-parameter-entity-names)))
1213    (dtd-analyse-content-spec entity-value)))
1214
1215(defun dtd-insert-mdc ()
1216  "Without moving point, indent to usual column and insert MDC (>).
1217MDC = Markup Declaration Close"
1218  (interactive)
1219  (save-excursion
1220    (let ((current-point (point))
1221          (mdc-column 0)
1222          (mdc dtd-sgml-mdc))
1223      ;; Only insert a mdc if looking at an mdo or if there's a previous mdo
1224      (if (if (looking-at dtd-sgml-mdo)
1225              1
1226            (re-search-backward (concat "^" dtd-sgml-mdo) nil t))
1227          (progn
1228            (if (looking-at dtd-comment-start)
1229              (progn
1230                (setq mdc dtd-comment-end)
1231                (setq mdc-column dtd-comment-max-column))
1232              (progn
1233                (setq mdc dtd-sgml-mdc)
1234                (setq mdc-column dtd-mdc-indent-column)))
1235            (goto-char current-point)
1236            (beginning-of-line)
1237            (cond
1238             ;; The main "looking-at" regular expression doesn't handle
1239             ;; line containing just "-->" or ">" very well, so handle
1240             ;; as special case.
1241             ((looking-at "\\s-*\\(--\\)?>$")
1242              (progn
1243                ;; Delete to the end of line so we can then insert our mdc
1244               (delete-region (point) (save-excursion
1245                                        (end-of-line)
1246                                        (point)))
1247               (indent-to mdc-column)
1248               (insert mdc)))
1249             ;; Another special case: just comment start and end
1250             ((looking-at "\\(\\s-*<!--\\)\\s-+-->$")
1251              (goto-char (match-end 1))
1252              (delete-region (point) (save-excursion
1253                                       (end-of-line)
1254                                       (point)))
1255              (indent-to mdc-column)
1256              (insert mdc))
1257             ;; This is the main "looking-at" regular expression that
1258             ;; handles most things
1259             ((looking-at "^\\(\\(<!\\|[ \t]*\\)?\\(--\\)?[ \t]*\\([^ \t\n>]+\\([- \t]+[^- \t\n>]+\\)*\\)?\\)[ \t]*\\(\\(--\\)?>?\\)?$")
1260              (progn
1261                ;;(message ":%s:%s:%s:%s:" mdc mdc-column (match-string 4) (match-string 1))
1262                (goto-char (match-end 1))
1263                (delete-region (point) (save-excursion
1264                                         (end-of-line)
1265                                         (point)))
1266                (dtd-indent-or-newline-to mdc-column)
1267                (insert mdc)))
1268             (t
1269              ;;(insert "@")
1270              (message "Could not insert mdc")))))))
1271  (if font-lock-mode
1272      (save-excursion
1273        (font-lock-fontify-region
1274         (dtd-font-lock-region-point-min)
1275         (dtd-font-lock-region-point-max)))))
1276
1277;; A work in progress
1278(defun dtd-fill-paragraph (&optional justify)
1279  "DTD fill paragraph function.  A work in progress."
1280  (interactive "P")
1281  ;;(insert "DTD")
1282  (save-excursion
1283    (if dtd-autodetect-type
1284        (dtd-autodetect-type))
1285    (let ((current-point (point))
1286          (fill-column dtd-mdc-indent-column)
1287          (fill-prefix (make-string
1288                        (if dtd-xml-flag
1289                            (1-
1290                             dtd-xml-element-content-spec-continuation-column)
1291                          (1- dtd-element-content-spec-continuation-column))
1292                        ?\ ))
1293          (saved-auto-fill-function auto-fill-function))
1294      (if (not (looking-at dtd-sgml-mdo))
1295          (re-search-backward (concat "^" dtd-sgml-mdo) nil t))
1296      (if (looking-at "\\(<!ELEMENT\\)\\(\\s-+\\)")
1297          (progn
1298            (goto-char (match-end 1))
1299            (delete-region (match-beginning 2) (match-end 2))
1300            (dtd-indent-or-newline-to (1- dtd-element-name-column))
1301            (if (looking-at "\\(\\sw\\|\\s_\\)+\\(\\s-+\\)")
1302                (progn
1303                  (message "It's an element")
1304                  (delete-region (match-beginning 2) (match-end 2))
1305                  (goto-char (match-end 1))
1306                  (if (looking-at "\\([-o]\\)\\s-+\\([-o]\\)\\s-+")
1307                      (let ((omit-start (match-string 1))
1308                            (omit-end (match-string 2)))
1309                        (message "It has ommissibility indicators")
1310                        (message "%s %s" omit-start omit-end)
1311                        (delete-region
1312                         (match-beginning 0) (match-end 0))
1313                        (dtd-indent-or-newline-to
1314                         (1- dtd-element-tag-omission-column))
1315                        (insert omit-start)
1316                        (insert " ")
1317                        (insert omit-end)))
1318                  (dtd-indent-or-newline-to
1319                   (1-
1320                    (if dtd-xml-flag
1321                        dtd-xml-element-content-spec-start-column
1322                      dtd-element-content-spec-start-column)))
1323                  (auto-fill-mode 1)
1324;;                (let ((content-model-start (point))
1325;;                      (content-model-end (save-excursion
1326;;                                           (forward-sexp)
1327;;                                           (point))))
1328                  (save-excursion
1329                    (let ((content-model-end (save-excursion
1330                                               (forward-sexp)
1331                                               (point))))
1332                    (while (re-search-forward
1333                            "\n+"
1334                            content-model-end t)
1335                      (replace-match " " nil nil))))
1336                  (fill-region (point) (save-excursion
1337                                         (forward-sexp)
1338                                         (point))
1339                               nil
1340                               1
1341                               1)
1342                  ;; The fill does the right thing, but it always ends with
1343                  ;; an extra newline, so we delete the newline.
1344                  (backward-char 1)
1345                  ;;    (setq content-spec-end (point))
1346                  (if (not saved-auto-fill-function)
1347                      (auto-fill-mode 0))))
1348            (dtd-insert-mdc)
1349;;                (insert "$")
1350;;          (if (looking-at "\\(\\s-*\\)>")
1351;;              (progn
1352;;                (delete-region (match-beginning 1) (match-end 1))
1353;;                (dtd-indent-or-newline-to dtd-mdc-indent-column)
1354;;                (insert "$")
1355))
1356;;                (dtd-insert-mdc)
1357;;                (insert "\n")
1358;;                (auto-fill-mode nil)
1359                  (if font-lock-mode
1360        (save-excursion
1361          (font-lock-fontify-region
1362           (dtd-font-lock-region-point-min)
1363           (dtd-font-lock-region-point-max)))))))
1364;;          (if (looking-at "ANY\\|CDATA\\|EMPTY\\|RCDATA")
1365;;              (progn
1366;;                (dtd-indent-or-newline-to
1367;;                 (1- dtd-element-content-spec-start-column)))
1368;;            (message "It's something else"))))))
1369        ;; If we're not doing anything, return nil so the built-in function
1370        ;; will run
1371;;  (font-lock-fontify-block))
1372
1373
1374;; Functions that set fill-prefix, etc. for various regions in a DTD
1375(defun dtd-comment-setup ()
1376  "Setup the auto-fill variables for comments in the body of the DTD"
1377  (interactive)
1378  (setq fill-prefix (make-string (1- dtd-comment-start-column) ?\ ))
1379  (auto-fill-mode 'true)
1380  (setq fill-column dtd-comment-max-column))
1381
1382(defun dtd-design-comment-setup ()
1383  "Setup the auto-fill variables for \"Design Considerations\" comments"
1384  (interactive)
1385  (setq fill-prefix (make-string
1386                     (1- dtd-design-comment-start-column)
1387                     ?\ ))
1388  (auto-fill-mode 'true)
1389  (setq fill-column dtd-comment-max-column))
1390
1391(defun dtd-declaration-setup ()
1392  "Setup the auto-fill variables for declarations"
1393  (interactive)
1394  (setq fill-prefix (make-string
1395                     (1- dtd-element-content-spec-continuation-column)
1396                     ?\ ))
1397  (auto-fill-mode 'true)
1398  (setq fill-column dtd-mdc-indent-column))
1399
1400(defun dtd-init-comment-setup ()
1401  "Setup the auto-fill variables for initial comments"
1402  (interactive)
1403  (setq fill-prefix dtd-init-comment-fill-prefix)
1404  (auto-fill-mode 'true)
1405  (setq fill-column (- dtd-dtd-max-column (length dtd-comment-end))))
1406
1407;;(setq fill-paragraph-function 'dtd-fill-paragraph)
1408
1409;; Not quite sure what this gets me, but these variables seem to apply
1410;; in auto-fill mode
1411(make-local-variable 'comment-start)
1412(setq comment-start "<!-- ")
1413(make-local-variable 'comment-end)
1414(setq comment-end " -->")
1415
1416
1417;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1418;; imenu stuff
1419
1420(defun dtd-sort-alist (alist)
1421  "Sort an alist"
1422  (sort
1423   alist
1424   (lambda (a b) (string< (car a) (car b)))))
1425
1426(defun dtd-imenu-create-index-function ()
1427  "Create an alist of elements, etc. suitable for use with imenu."
1428  (let ((element-alist '())
1429        (notation-alist '())
1430        (general-entity-alist '())
1431        (parameter-entity-alist '()))
1432    (goto-char (point-min))
1433    (while
1434        (re-search-forward
1435         "^<!ELEMENT[ \t]+\\([^ \t\n]+\\)+[ \t\n]+\\([-o][ \t]+[-o][ \t]+\\)?(*" nil t)
1436      (setq element-alist
1437            (cons (cons (buffer-substring-no-properties
1438                         (match-beginning 1)
1439                         (match-end 1))
1440                        (match-end 0))
1441                  element-alist)))
1442    (goto-char (point-min))
1443    (while
1444        (re-search-forward
1445         "^<!NOTATION[ \t]+\\([^ \t\n]+\\)" nil t)
1446      (setq notation-alist
1447            (cons (cons (buffer-substring-no-properties
1448                         (match-beginning 1)
1449                         (match-end 1))
1450                        (match-beginning 1))
1451                  notation-alist)))
1452    (goto-char (point-min))
1453    (while
1454        (re-search-forward
1455         "^<!ENTITY[ \t]+\\([^% \t\n]+\\)" nil t)
1456      (setq general-entity-alist
1457            (cons (cons (buffer-substring-no-properties
1458                         (match-beginning 1)
1459                         (match-end 1))
1460                        (match-beginning 1))
1461                  general-entity-alist)))
1462    (goto-char (point-min))
1463    (while
1464        (re-search-forward
1465         "^<!ENTITY[ \t]+%[ \t]+\\([^ \t\n]+\\)[ \t\n]+[\"']?" nil t)
1466      (setq parameter-entity-alist
1467            (cons (cons (buffer-substring-no-properties
1468                         (match-beginning 1)
1469                         (match-end 1))
1470                        (match-end 0))
1471                  parameter-entity-alist)))
1472    (append
1473     (if notation-alist
1474         (list (cons "<!NOTATION" (dtd-sort-alist notation-alist))))
1475     (if general-entity-alist
1476         (list (cons "<!ENTITY" (dtd-sort-alist general-entity-alist))))
1477     (if parameter-entity-alist
1478         (list (cons "<!ENTITY %" (dtd-sort-alist parameter-entity-alist))))
1479     (dtd-sort-alist element-alist))))
1480
1481;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1482;; grep stuff
1483
1484;;;###autoload
1485(defun dtd-grep (pattern filespec)
1486  "Grep for PATTERN in files matching FILESPEC.
1487
1488Runs `grep' with PATTERN and FILESPEC as arguments.
1489
1490PATTERN is the pattern on which `grep' is to match.  PATTERN is quoted
1491with single quotes in the `grep' command arguments to avoid
1492interpretation of characters in PATTERN.  `dtd-grep' maintains a
1493history of PATTERNs so you can easily re-use a previous value.
1494
1495FILESPEC is the names or regular expression for the files to be
1496scanned by grep.  Since `dtd-grep' uses `grep', regular expressions
1497and multiple filenames are supported, and \"*.dtd\" and \"*.dtd
1498*.ent\" are both valid FILESPEC values.
1499
1500When called interactively, the initial FILESPEC is taken from
1501dtd-default-filespec, but `dtd-grep' also maintains a history of
1502FILESPEC arguments so you can easily re-use a previous value.  The
1503history is shared with `dtd-etags' so you can re-use the same FILESPEC
1504with both functions.
1505"
1506  (interactive
1507   (list
1508    (dtd-read-from-minibuffer "Pattern: "
1509                              (find-tag-default)
1510                              'dtd-grep-pattern-history)
1511    (dtd-read-from-minibuffer "Files: "
1512                              (car dtd-filespec-history)
1513                              'dtd-filespec-history)))
1514  ;; We include "--" in the command in case the pattern starts with "-"
1515  (grep (format dtd-grep-command-format
1516                (if (not dtd-grep-case-sensitive-flag)
1517                    "-i")
1518                pattern
1519                filespec)))
1520
1521
1522;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1523;; Tags stuff
1524
1525;;;###autoload
1526(defun dtd-etags (filespec)
1527  "Execute etags on FILESPEC and match on DTD-specific regular expressions.
1528
1529Runs the \"etags\" program on the specified file with a regular
1530expression for finding element, entity, attribute list, and notation
1531declarations.  `dtd-tags' then modifies the output tags file to
1532disambiguate parameter entity names versus other names.
1533
1534FILESPEC is the names or regular expression for the files to be
1535scanned by etags.  `dtd-etags' uses `shell-command' to execute etags
1536specifically so regular expressions and multiple filenames are
1537supported, and \"*.dtd\" and \"*.dtd *.ent\" are both valid FILESPEC
1538values.
1539
1540When called interactively, the initial prompt is taken from
1541dtd-default-filespec, but `dtd-etags' also maintains a history of
1542FILESPEC arguments so you can easily re-use a previous value.  The
1543history is shared with `dtd-grep' so you can re-use the same FILESPEC
1544with both functions.
1545
1546The output tags file is added to tags-table-list using
1547`visit-tags-table', and, depending on the value of tags-add-tables,
1548you may be prompted whether to add the new tags table to the current
1549list or to start a new list.
1550
1551`dtd-etags' also uses the dtd-etags-program, dtd-etags-regex-option,
1552and dtd-etags-output-file variables to construct the command passed to
1553`shell-command'."
1554  (interactive
1555   (list (dtd-read-from-minibuffer "Files: "
1556                                   (car dtd-filespec-history)
1557                                   'dtd-filespec-history)))
1558  (shell-command
1559   (format "%s %s --output=%s %s"
1560           dtd-etags-program
1561           dtd-etags-regex-option
1562           dtd-etags-output-file
1563           filespec))
1564  (save-excursion
1565    (save-window-excursion
1566      ;; We could be visiting the TAGS file, which causes an
1567      ;; unecessary complication when Emacs prompts to see if
1568      ;; we want to reload the buffer, so we just kill it out
1569      ;; of hand.  Since more than one buffer can be visiting it,
1570      ;; we make sure we catch them all.
1571      (while (get-file-buffer dtd-etags-output-file)
1572        (kill-buffer (get-file-buffer dtd-etags-output-file)))
1573      (find-file dtd-etags-output-file)
1574      (while (re-search-forward
1575              "^\\(<!ENTITY[ \t]+%[ \t]+[^\177]+\177\\)\\([^%\1]+\\)"
1576              nil t)
1577        (replace-match "\\1%\\2;" nil nil))
1578      ;; Go again to find element names to add to
1579      ;; dtd-declared-element-type-names
1580      (goto-char (point-min))
1581      ;; Remove whatever we had as dtd-declared-element-type-names
1582      (setq dtd-declared-element-type-names nil)
1583      ;; Add the element names as found by the etags program
1584      (while (re-search-forward
1585              "^<!ELEMENT[ \t]+\\([^\177]+\\)"
1586              nil t)
1587        (add-to-list 'dtd-declared-element-type-names (match-string 1)))
1588      ;; Don't bother saving a backup
1589      (setq backup-inhibited t)
1590      (save-buffer)
1591      (kill-buffer (current-buffer))
1592      (visit-tags-table
1593       (expand-file-name dtd-etags-output-file)))))
1594
1595(defun dtd-find-tag-hook ()
1596  "Slight customization of find-tags."
1597  ;; If we're looking at an element declaration, move point to the first
1598  ;; syntactic literal, element type name, or parameter entity reference
1599  ;; in the content model.
1600  (cond
1601   ((looking-at
1602     "<!ELEMENT[ \t]+[^ \t]+[ \t\n]+\\([-o][ \t]+[-o][ \t]+\\)?(*")
1603    (goto-char (match-end 0)))
1604   ((looking-at
1605     "<!ATTLIST[ \t]+[^ \t]+[ \t\n]+")
1606    (goto-char (match-end 0)))
1607   ((looking-at
1608     "<!ENTITY[ \t]+%[ \t]+[^ \t]+[ \t\n]+\"?")
1609      (goto-char (match-end 0)))))
1610
1611
1612(defvar dtd-mode-abbrev-table nil
1613  "Abbrev table used while in DTD mode.")
1614
1615;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1616;; Mode map stuff
1617
1618(defvar dtd-mode-map nil
1619  "Keymap for DTD mode.")
1620
1621(if dtd-mode-map
1622    ()
1623  (setq dtd-mode-map (make-sparse-keymap))
1624  (define-key dtd-mode-map ">"                         'dtd-electric-mdc)
1625  (define-key dtd-mode-map '[(control c) (>)]          'dtd-insert-mdc)
1626  (define-key dtd-mode-map '[(control c) (control a)]  'dtd-declare-attribute)
1627  (define-key dtd-mode-map '[(control c) (control n)]  'dtd-declare-notation)
1628  (define-key dtd-mode-map '[(control c) (meta control %)]
1629    'dtd-declare-external-entity)
1630  (define-key dtd-mode-map '[(control c) (control %)]
1631    'dtd-declare-parameter-entity)
1632  (define-key dtd-mode-map '[(control c) (control e)]  'dtd-declare-element)
1633  (define-key dtd-mode-map '[(control c) (control c)]  'dtd-comment)
1634  (define-key dtd-mode-map '[(control c) (control v)]  'sgml-validate)
1635  ;;(define-key dtd-mode-map '[(control c) (b)]     'dtd-big-comment)
1636  (define-key dtd-mode-map '[(meta g) (meta f)]    'font-lock-fontify-buffer)
1637  (define-key dtd-mode-map '[(meta g) (meta control g)]
1638    'font-lock-fontify-buffer)
1639  (define-key dtd-mode-map '[(meta control g)]     'font-lock-fontify-buffer)
1640  (define-key dtd-mode-map [(control m)]          'dtd-electric-return)
1641  ;; This overrides the sgml-mode mapping and puts it back to its default
1642  (define-key dtd-mode-map '[(meta tab)]           'complete-tag))
1643
1644(defun dtd-electric-mdc ()
1645  (interactive)
1646  (insert ">")
1647  (if font-lock-mode
1648      (save-excursion
1649        (font-lock-fontify-region
1650         (dtd-font-lock-region-point-min)
1651         (dtd-font-lock-region-point-max)))))
1652
1653(defun dtd-electric-return ()
1654  "What do do for a newline in dtd mode"
1655  (interactive)
1656  (message "%s" (current-column))
1657  (if (= (char-before) ?>)
1658      (progn
1659        (message "mdc")
1660        (insert "\n"))
1661    (progn
1662      ;;  (message "%s" (current-column))
1663      (insert "\n")
1664      (delete-horizontal-space)
1665      (indent-to
1666       (save-excursion
1667         (backward-char )
1668         (delete-horizontal-space)
1669         (let*
1670             ((declaration-type
1671               (save-excursion
1672                 (re-search-backward "^<!")
1673                 (cond
1674                  ((looking-at "<!ELEMENT") 'element)
1675                  ((looking-at "<!ATTLIST") 'attlist)
1676                  ((looking-at "<!--") 'comment)
1677                  ((looking-at "<!ENTITY\\s-+%") 'parameter-entity)
1678                  ((looking-at "<!ENTITY") 'entity)
1679                  ((looking-at "<!DOCTYPE") 'doctype)
1680                  ((looking-at "<!\\[\\s-+CDATA") 'cdata-section)
1681                  (t
1682                   'unknown))))
1683              (indent
1684               (progn
1685                 (message "%s" declaration-type)
1686                 (beginning-of-line)
1687;;               (insert "@")
1688                 (case declaration-type
1689                   ('element
1690                    (cond
1691                     ((looking-at "<!ELEMENT\\s-*$")
1692                      (message "ELEMENT")
1693                      (1- dtd-element-name-column))
1694                     ((looking-at "<!ELEMENT\\s-+\\sw+\\s-*$")
1695                      (message "ELEMENT name")
1696                      (1- (if dtd-xml-flag
1697                              dtd-xml-element-content-spec-start-column
1698                            dtd-element-tag-omission-column)))
1699                     ((looking-at
1700                       "<!ELEMENT\\s-+\\sw+\\s-+[-oO]\\s-+[-oO]\\s-*$")
1701                      (1- dtd-element-content-spec-start-column))
1702                     ((looking-at
1703                       "<!ELEMENT\\s-+\\sw+\\s-+[-oO]\\s-+[-oO]\\s-+\\S+")
1704                      (message "ELEMENT name")
1705                      dtd-element-content-spec-start-column)
1706                     ((looking-at "<!ELEMENT\\s-+\\sw+\\s-+[^-oO]")
1707                      (message "ELEMENT name xml")
1708                      (1-
1709                       dtd-xml-element-content-spec-continuation-column))
1710                     ((looking-at "\\s-+[-oO]\\s-+[-oO]\\s-+(")
1711                      (message "SGML element continuation")
1712                      (1- dtd-element-content-spec-continuation-column))
1713                     ((looking-at "\\(\\s-+\\)\\S-+")
1714                      (goto-char (match-end 1))
1715                      (current-column))
1716                     (t
1717                      (message "other element")
1718                      0)))
1719                    ('attlist
1720                     (cond
1721                      ((looking-at "<!ATTLIST$")
1722                       (message "attlist")
1723                       (1- dtd-element-name-column))
1724                      ((looking-at "<!ATTLIST\\s-+\\sw+")
1725                       (message "attlist element name")
1726                       (1- dtd-attribute-name-column))
1727                      ((looking-at
1728                        "\\(\\s-+\\(\\sw+\\s-+\\)?NOTATION\\s-+(\\)")
1729                       (if (save-excursion
1730                             (end-of-line)
1731                             (= (char-before) ?\)))
1732                           (progn
1733                             (message "Finished NOTATION list")
1734                             (1- dtd-attribute-default-column))
1735                         (progn
1736                           (message "Unfinished NOTATION list")
1737                           (- (match-end 1) (match-beginning 1)))))
1738                      ((looking-at "[^(]*([^)]*)\\s-+$")
1739                       (message "Finished group")
1740                       (1- dtd-attribute-default-column))
1741                      ((looking-at "\\(\\s-+\\)\\sw+\\s-*")
1742                       (message "attribute list, single token")
1743                       (end-of-line)
1744                       (delete-horizontal-space)
1745                       (cond
1746                        ((or
1747                          (= (char-before) ?\))
1748                          (= (char-before) ?\"))
1749                         (message "attribute list, after ) or \"")
1750                         (1- dtd-attribute-default-column))
1751                        ((<= (abs (- (- (match-end 1)
1752                                        (match-beginning 1))
1753                                     dtd-attribute-name-column))
1754                             2)
1755                         (message "After attribute name")
1756                         (1- dtd-comment-start-column))
1757                        (t
1758                         (goto-char (match-end 1))
1759                         (current-column))))
1760                      ((looking-at "\\(\\s-+\\)(")
1761                       (message "Start of group")
1762                       (goto-char (match-end 1))
1763                       (1+ (current-column)))
1764                      ((looking-at "\\(\\s-+\\)\\sw")
1765                       (if (<= (abs (- (length (- (match-end 1)
1766                                                  (match-beginning 1)))
1767                                       dtd-attribute-name-column))
1768                               2)
1769                           (1- dtd-comment-start-column)
1770                         (progn
1771                           (message "??")
1772                           (goto-char (match-end 1))
1773                           (current-column))))
1774                      ((looking-at "\\(\\s-+\\)\\sw+\\s-+\\sw")
1775                       (goto-char (match-end 1))
1776                       (current-column))
1777                      (t
1778                       (message "other attlist")
1779                       0)))
1780                    ('parameter-entity
1781                     (cond
1782                      ((looking-at
1783                        "<!ENTITY\\s-+%\\s-+\\sw+\\s-+\\(PUBLIC\\|SYSTEM\\)$")
1784                       (message "PUBLIC or SYSTEM")
1785                       0)
1786                      ((looking-at "<!ENTITY\\s-+%\\s-+\\sw+\\s-+\"")
1787                       (message "Internal PE continuation")
1788                       dtd-entity-entity-value-continuation-column)
1789                      ((looking-at "<!ENTITY\\s-+%\\s-+\\sw")
1790                       (message "Internal PE")
1791                       (1- dtd-entity-entity-value-start-column))
1792                     ((looking-at "\\(\\s-+\\)\\(\\sw\\|(\\)+")
1793                       dtd-entity-entity-value-continuation-column)
1794                     (t
1795                      (message "other parameter entity")
1796                      0)))
1797                    ('comment
1798                     (cond
1799                      ((looking-at "<!--")
1800                       (message "comment")
1801                       (re-search-forward
1802                        "<!--\\s-+")
1803                       (current-column))
1804                     ((looking-at "\\(\\s-+\\)\\(\\sw\\|(\\)+")
1805                      (goto-char (match-end 1))
1806                      (current-column))
1807                     (t
1808                      (message "other comment")
1809                      0)))
1810                    ('unknown
1811                     (message "unknown")
1812                     0)))))
1813           indent))))))
1814
1815;;         (cond
1816;;          ((looking-at "\\(\\s-+\\)\\(\\sw\\|(\\)+")
1817;;           (message "whitespace+")
1818;;           (if (progn
1819;;                 (re-search-backward "<")
1820;;                 (looking-at "<!ATTLIST"))
1821;;               (progn
1822;;                 (message "inside ATTLIST")
1823;;                 (end-of-line)
1824;;                 (backward-char)
1825;;                 (if (looking-at ")")
1826;;                     (1- dtd-attribute-default-column)))
1827;;             (progn
1828;;               (message "also inside ATTLIST")
1829;;               ;;            (end-of-line)
1830;;               (if (looking-at "\\(\\s-+\\)\\(\\sw\\|(\\)+")
1831;;                   (goto-char (match-end 1)))
1832;;               (current-column))))
1833;;          ((looking-at "\\s-+")
1834;;           (message "whitespace")
1835;;           0)
1836;;          ((looking-at "$")
1837;;           (message "end of line")
1838;;           0)
1839;;          ((looking-at "<!\\[\W*CDATA")
1840;;           (message "CDATA marked section")
1841;;           0)
1842;;          (t
1843;;           (message "unknown")
1844;;           0))))
1845;;      (message "%s" (current-column)))
1846
1847
1848;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1849;; Syntax table stuff
1850
1851(defvar dtd-mode-syntax-table nil
1852  "Syntax table used while in DTD mode.")
1853
1854(if dtd-mode-syntax-table
1855    ()
1856  (setq dtd-mode-syntax-table (make-syntax-table))
1857  ;; set the non-alphanumeric characters in XML names to
1858  ;; 'symbol constituent' class
1859  (modify-syntax-entry ?: "_" dtd-mode-syntax-table)
1860  (modify-syntax-entry ?_ "_" dtd-mode-syntax-table)
1861  (modify-syntax-entry ?- "_ 1234" dtd-mode-syntax-table)
1862  (modify-syntax-entry ?. "_" dtd-mode-syntax-table)
1863  ;; "-" is a special case because it is the first and second characters
1864  ;; of the start- and end-comment sequences.
1865  (modify-syntax-entry ?- "_ 1234" dtd-mode-syntax-table)
1866  ;; "%" does double duty in parameter entity declarations and references.
1867  ;; Not necessary to make "%" and ";" act like parentheses since the
1868  ;; font lock highlighting tells you when you've put the ";" on the
1869  ;; end of a parameter entity reference.
1870  (modify-syntax-entry ?% "_" dtd-mode-syntax-table)
1871  (modify-syntax-entry ?\; "_" dtd-mode-syntax-table)
1872  ;; "/" is just punctuation in DTDs, and really only has a role in
1873  ;; Formal Public Identifiers
1874  (modify-syntax-entry ?/ "." dtd-mode-syntax-table)
1875  ;; Sometimes a string is more than just a string, Dr Freud.
1876  ;; Unfortunately, the syntax stuff isn't fussy about matching
1877  ;; on paired delimeters, and will happily match a single quote
1878  ;; with a double quote, and vice versa.  At least the font
1879  ;; lock stuff is more fussy and won't change colour if the
1880  ;; delimiters aren't paired.
1881  (modify-syntax-entry ?\" "$" dtd-mode-syntax-table)
1882  (modify-syntax-entry ?\' "$" dtd-mode-syntax-table)
1883  ;; The occurrence indicators and connectors are punctuation to us.
1884  (modify-syntax-entry ?| "." dtd-mode-syntax-table)
1885  (modify-syntax-entry ?, "." dtd-mode-syntax-table)
1886  (modify-syntax-entry ?& "." dtd-mode-syntax-table)
1887  (modify-syntax-entry ?? "." dtd-mode-syntax-table)
1888  (modify-syntax-entry ?+ "." dtd-mode-syntax-table)
1889  (modify-syntax-entry ?* "." dtd-mode-syntax-table)
1890  ;; `<' and `>' are also punctuation
1891  (modify-syntax-entry ?< "." dtd-mode-syntax-table)
1892  (modify-syntax-entry ?> "." dtd-mode-syntax-table)
1893  ;; "#" is syntax too
1894  (modify-syntax-entry ?# "_" dtd-mode-syntax-table))
1895
1896;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1897;; Font-lock utility functions
1898
1899(defun dtd-font-lock-mark-block-function ()
1900  "Function to mark the area of text we want to fontify.
1901
1902Used with font-lock-fontify-block.  Set font-lock-mark-block-function
1903to this function for this function to take effect.
1904
1905This function marks the area beginning five \"<!\" before point and five
1906\">\" at ends of lines after point.  The default without a function like
1907this is to fontify 16 lines before and after point, but then the region
1908often starts or ends partway through a comment or declaration, turning
1909that half white because the keywords didn't match, and it just looks so
1910ugly."
1911  (let ((current-point (point)))
1912    (re-search-forward ">$" (point-max) 'limit 5)
1913    (set-mark (point))
1914    (goto-char current-point)
1915    (re-search-backward "^<!" (point-min) 'limit 5)))
1916
1917(defun dtd-font-lock-region-point-min ()
1918  "Return the start point of the region we want to fontify"
1919  (save-excursion
1920    (re-search-backward "^<!" (point-min) 'limit 5)
1921;;    (insert "@")
1922    (point)))
1923
1924(defun dtd-font-lock-region-point-max ()
1925  "Return the start point of the region we want to fontify"
1926  (save-excursion
1927    (re-search-forward ">$" (point-max) 'limit 5)
1928;;    (insert "!")
1929    (point)))
1930
1931
1932;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1933;; After change functions
1934
1935;;(defun dtd-recalc-mdc-after-change (begin end length)
1936;;  "Recalculate the position of the mdc after a change"
1937;;  (if dtd-use-after-change-functions-flag
1938;;      (save-match-data
1939;;      (save-excursion
1940;;        (goto-char end)
1941;;        (end-of-line)
1942;;        (if (and (= (char-before) ?>)
1943;;                 (or (= (char-before (- (point) 2)) ?-)
1944;;                     (= (char-before (- (point) 2)) ?\ )))
1945;;            (dtd-insert-mdc))))))
1946
1947(defun dtd-recalc-mdc-after-change (begin end length)
1948  "Recalculate the position of the mdc after a change"
1949  (if dtd-use-after-change-functions-flag
1950      (let ((end-column (save-excursion
1951                              (goto-char end)
1952                              (current-column))))
1953      (save-match-data
1954        (save-excursion
1955          (goto-char end)
1956          (end-of-line)
1957          (if (and (= (char-before) ?>)
1958                   (or (= (char-before (- (point) 2)) ?-)
1959                       (= (char-before (- (point) 2)) ?\ )))
1960              (dtd-insert-mdc))))
1961      (if (= (char-before end) ?\ )
1962          (move-to-column end-column)))))
1963
1964
1965;;;###autoload
1966(defun dtd-mode ()
1967  "Major mode for SGML and XML DTDs.
1968
1969dtd-mode features include:
1970
1971 - dtd-etags function for creating Emacs TAGS files for easy lookup of
1972   any element, parameter entity, or notation's definition using
1973   Emacs's built-in tag-lookup functions;
1974
1975 - Font lock highlighting of declarations so that the important
1976   information stands out;
1977
1978 - XML-specific behaviour that, at user option, is triggered by
1979   automatic detection of the XML Declaration; and
1980
1981 - Functions for writing and editing declarations and comments to ease
1982   creating and keeping a consistent style.
1983
1984dtd-mode builds on sgml-mode, and the full sgml-mode functions are
1985still available.  Use with Lennart Staflin's psgml package is
1986recommended.
1987
1988dtd-mode uses many user-definable variables to control the formatting
1989of declarations, some of which are shown in the following examples:
1990
1991                        dtd-comment-start-column    dtd-dtd-max-column
1992                        |                    dtd-comment-max-column  |
1993                        |                                         |  |
1994<!--                    This is a comment                          -->
1995
1996           dtd-element-name-column
1997           |            dtd-element-tag-ommission-column
1998           |            |   dtd-element-content-spec-start-column
1999           |            |   |dtd-element-content-spec-continuation-column
2000           |            |   ||                      dtd-dtd-max-column
2001           |            |   ||                                       |
2002<!ELEMENT  element-tag  - - (insert, your, content, specification,
2003                             here)                                   >
2004
2005\\{dtd-mode-map}"
2006  (interactive)
2007  (kill-all-local-variables)
2008  (use-local-map dtd-mode-map)
2009  (setq mode-name "DTD")
2010  (setq major-mode 'dtd-mode)
2011  (setq local-abbrev-table dtd-mode-abbrev-table)
2012  ;; XEmacs users don't all have imenu
2013  (if (featurep 'imenu)
2014      (progn
2015        ;; If you don't have imenu, you'll get a "free variable"
2016        ;; warning for imenu-create-index-function when you
2017        ;; byte-compile, but not having imenu won't cause problems
2018        ;; when you use tdtd
2019        (setq imenu-create-index-function 'dtd-imenu-create-index-function)
2020        (setq imenu-extract-index-name-function 'dtd-imenu-create-index-function)
2021        (imenu-add-to-menubar "TAGS")))
2022  (set-syntax-table dtd-mode-syntax-table)
2023  ;; XML specific behaviour can be specific to a buffer
2024  (make-local-variable 'dtd-xml-flag)
2025  ;; Maybe select XML-specific behaviour if we have an XML declaration
2026  (if dtd-autodetect-type
2027      (dtd-autodetect-type))
2028  ;; dtd font-lock highlighting setup
2029  (make-local-variable 'font-lock-defaults)
2030  (make-local-variable 'font-lock-mark-block-function)
2031  (cond
2032   (dtd-xml-flag
2033    (setq font-lock-defaults '(dtd-xml-font-lock-keywords t)))
2034   (dtd-decl-flag
2035    (setq font-lock-defaults '(dtd-decl-font-lock-keywords t)))
2036   (dtd-sys-decl-flag
2037    (setq font-lock-defaults '(dtd-sys-decl-font-lock-keywords t)))
2038   (t
2039    (setq font-lock-defaults '(dtd-sgml-font-lock-keywords t))))
2040  (setq font-lock-mark-block-function 'dtd-font-lock-mark-block-function)
2041  ;; Enable mode-specific behaviour on finding tags (in the Emacs sense)
2042  (make-local-variable 'find-tag-hook)
2043  (add-hook 'find-tag-hook
2044            'dtd-find-tag-hook)
2045  (make-local-variable 'after-change-functions)
2046  (setq after-change-functions '(dtd-recalc-mdc-after-change))
2047  ;; Maybe insert space characters when user hits "Tab" key
2048  (setq indent-tabs-mode dtd-indent-tabs-mode)
2049  (run-hooks 'dtd-mode-hooks))
2050
2051
2052;;;; Bug reporting
2053
2054(defun dtd-submit-bug-report ()
2055  "Submit via mail a bug report on TDTD."
2056  (interactive)
2057  (require 'reporter)
2058  (require 'sendmail)
2059  (and (y-or-n-p "Do you really want to submit a report on DTD mode? ")
2060       (reporter-submit-bug-report
2061        tdtd-maintainer-address
2062        (concat "tdtd.el " tdtd-version)
2063        (list
2064         'dtd-attribute-default-column
2065         'dtd-attribute-default-history
2066         'dtd-attribute-name-column
2067         'dtd-attribute-tag-history
2068         'dtd-attribute-type-history
2069         'dtd-autodetect-type
2070         'dtd-comment-end
2071         'dtd-comment-max-column
2072         'dtd-comment-start
2073         'dtd-comment-start-column
2074         'dtd-declared-element-type-names
2075         'dtd-declared-parameter-entity-names
2076         'dtd-default-element-type-name
2077         'dtd-default-filespec
2078         'dtd-design-comment-start-column
2079         'dtd-dtd-max-column
2080         'dtd-element-comment-history
2081         'dtd-element-content-spec-continuation-column
2082         'dtd-element-content-spec-history
2083         'dtd-element-content-spec-start-column
2084         'dtd-element-name-column
2085         'dtd-element-tag-omission-column
2086         'dtd-element-type-name-history
2087         'dtd-empty-literal
2088         'dtd-entity-entity-value-start-column
2089         'dtd-etags-output-file
2090         'dtd-etags-program
2091         'dtd-etags-regex-option
2092         'dtd-filespec-history
2093         'dtd-grep-case-sensitive-flag
2094         'dtd-grep-pattern-history
2095         'dtd-indent-tabs-mode
2096         'dtd-init-comment-column
2097         'dtd-init-comment-fill-prefix
2098         'dtd-line-comment
2099         'dtd-mdc-indent-column
2100         'dtd-outdent-attribute-pe
2101         'dtd-parameter-entity-value-history
2102         'dtd-referenced-element-type-names
2103         'dtd-referenced-parameter-entity-names
2104         'dtd-sgml-mdc
2105         'dtd-sgml-mdo
2106         'dtd-upcase-name-comment-flag
2107         'dtd-xml-element-content-spec-continuation-column
2108         'dtd-xml-element-content-spec-start-column
2109         'dtd-xml-flag
2110         )
2111        nil
2112        nil
2113     "Please change the Subject header to a concise bug description.\nRemember to cover the basics, that is, what you expected to\nhappen and what in fact did happen.  Please remove these\ninstructions from your message.")
2114    (save-excursion
2115      (goto-char (point-min))
2116      (mail-position-on-field "Subject")
2117      (beginning-of-line)
2118      (delete-region (point) (progn (forward-line) (point)))
2119      (insert
2120       "Subject: tdtd version " tdtd-version " is wonderful but...\n"))))
2121
2122
2123;;;; Last provisions
2124;;;(provide 'tdtd)
2125
2126;;; tdtd.el ends here
Note: See TracBrowser for help on using the browser.