initial commit
[emacs-init.git] / nxhtml / nxhtml / tidy-xhtml.el
1 ;;; tidy-xhtml.el --- Interface to the HTML Tidy program
2
3 ;; Copyright (C) 2001, 2002, 2003, 2006, 2007 by Free Software
4 ;; Foundation, Inc.
5
6 ;; Emacs Lisp Archive Entry
7 ;; Ancestors filename: tidy.el
8 ;; Author: Kahlil (Kal) HODGSON <dorge@tpg.com.au>
9 ;; Author: Lennart Borgman (lennart O borgman A gmail O com)
10 ;; Original X-URL: http://www.emacswiki.org/elisp/tidy.el
11 ;; Last-Updated: 2008-03-09T13:10:06+0100 Sun
12 (defconst tidy-xhtml:version "2.25")
13 ;; Keywords: languages
14
15 ;; This file is NOT part of GNU Emacs.
16
17 ;; This file is free software; you can redistribute it and/or modify
18 ;; it under the terms of the GNU General Public License as published by
19 ;; the Free Software Foundation; either version 2, or (at your option)
20 ;; any later version.
21
22 ;; This file is distributed in the hope that it will be useful,
23 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
24 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 ;; GNU General Public License for more details.
26
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with GNU Emacs; see the file COPYING.  If not, write to
29 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
30 ;; Boston, MA 02111-1307, USA.
31
32 ;;;; Commentary:
33
34 ;; Provides a simple interface to the HTML Tidy program -- a free
35 ;; utility that can fix common errors in your mark-up and clean up
36 ;; sloppy editing automatically. See
37 ;;
38 ;;       <http://tidy.sourceforge.net/>
39 ;;
40 ;; for more details.  This package provides the following functions:
41 ;;
42 ;;       `tidy-buffer',
43 ;;       `tidy-region',
44 ;;       `tidy-tree',
45 ;;       `tidy-html-site',
46 ;;       `tidy-parse-config-file',
47 ;;       `tidy-save-settings',
48 ;;       `tidy-describe-options',
49 ;;       `tidy-show-xhtml-options',
50 ;;       `tidy-set-xhtml-options',
51 ;;
52 ;; These can be invoked interactively (using M-x) or via the menu-bar.
53 ;; The function `tidy-buffer' sends the current buffer to HTML Tidy,
54 ;; replacing the existing contents with a "tidied" version.  If
55 ;; `tidy-buffer' is given a prefix argument, tidy operates on the
56 ;; current region, ignoring mark-up outside <BODY>...</BODY> tags
57 ;; (useful for writhing cgi scripts in Pearl).  Warnings and errors
58 ;; are presented in a compilation buffer to facilitate tracking down
59 ;; necessary changes (e.g. C-x ` is bound to `next-error').
60 ;;
61 ;; This package also provides menu-bar support for setting Tidy's many
62 ;; options, and includes support for Tidy configuration files.  The
63 ;; function `tidy-parse-config-file' will synchronise options
64 ;; displayed in the menu-bar with the settings in `tidy-config-file'.
65 ;; This is normally called by the load-hook for your HTML editing mode
66 ;; (see installation instructions below).  The function
67 ;; `tidy-save-settings' will save the current option settings to your
68 ;; `tidy-config-file'.  Finally `tidy-describe-options' allows you to
69 ;; browse the documentation strings associated with each option.
70
71 ;;;
72
73 ;;;; Installation:
74
75 ;; This package assumes you have and up-to-date HTML Tidy program
76 ;; installed on your system.  See the URL above for instructions on
77 ;; how to do this.  To set up this support package, first place the
78 ;; "tidy.el" file somewhere in your `load-path' and open it in Emacs.
79 ;; Byte-compile and load this package using the command
80 ;;
81 ;; M-x emacs-lisp-byte-compile-and-load <RET>
82 ;;
83 ;; Next customise the variables `tidy-config-file', `tidy-temp-dir'
84 ;; `tidy-shell-program', `tidy-menu-lock' and `tidy-menu-x-position'
85 ;;
86 ;; M-x customize-group <RET> tidy <RET>
87 ;;
88 ;; Now add the following autoloads to your ".emacs.el" file:
89 ;;
90 ;; (autoload 'tidy-buffer "tidy" "Run Tidy HTML parser on current buffer" t)
91 ;; (autoload 'tidy-parse-config-file "tidy" "Parse the `tidy-config-file'" t)
92 ;; (autoload 'tidy-save-settings "tidy" "Save settings to `tidy-config-file'" t)
93 ;; (autoload 'tidy-build-menu  "tidy" "Install an options menu for HTML Tidy." t)
94 ;;
95 ;; If you use html-mode to edit HTML files then add something like
96 ;; this as well
97
98 ;; (defun my-html-mode-hook () "Customize my html-mode."
99 ;;   (tidy-build-menu html-mode-map)
100 ;;   (local-set-key [(control c) (control c)] 'tidy-buffer)
101 ;;   (setq sgml-validate-command "tidy"))
102 ;;
103 ;; (add-hook 'html-mode-hook 'my-html-mode-hook)
104
105 ;; This will set up a "tidy" menu in the menu bar and bind the key
106 ;; sequence "C-c C-c" to `tidy-buffer' in html-mode (normally bound to
107 ;; `validate-buffer').
108 ;;
109 ;; For other modes (like html-helper-mode) simple change the variables
110 ;; `html-mode-hook' and `html-mode-map' to whatever is appropriate e.g.
111
112 ;; (defun my-html-mode-hook () "Customize my html-helper-mode."
113 ;;   (tidy-build-menu html-helper-mode-map)
114 ;;   (local-set-key [(control c) (control c)] 'tidy-buffer)
115 ;;   (setq sgml-validate-command "tidy"))
116 ;;
117 ;; (add-hook 'html-helper-mode-hook 'my-html-mode-hook)
118
119 ;; Finally, restart Emacs and open an HTML file to test-drive the tidy
120 ;; package. For people new to HTML tidy check that the option "markup"
121 ;; under the "Input/Output" sub menu is set. You can read the
122 ;; documentation on this option via the menu item "Describe Options".
123 ;;
124 ;; Enjoy!
125
126 ;;;; New Features:
127 ;;
128 ;; 0. Now compatible with CVS version of Tidy as at 22 May 2003
129 ;; 1. Improved menu support to facillitate incorporting new options
130 ;; 2. Menu lock option makes menu stick when toggling options.
131 ;; 3. Now runs on XEmacs!!
132 ;; 4. Uses error file rather than std-error to retrieve errors (this
133 ;;    fixes some odd pop up behaviour)
134 ;; 5. minor bug fix (empty config files)
135 ;; 6. handle buffer modified query in error buffer better
136 ;; 7. make it impossible to mark the error buffer as modified
137 ;; 8. Added the variable `tidy-temp-directory'.
138 ;; 9. Bugfix in tidy-buffer: call find-file-noselect with NOWARN
139 ;; 10. Removes ^M on w32.
140 ;; 11. Changed defcustom types to 'file and 'directory.
141 ;; 12. Added `tidy-set-xhtml-options'.
142 ;; 13. Tried to handle encodings.
143 ;; 14. Added the function `tidy-region'.
144 ;; 15. Added ediff support.
145 ;; 16. Added `tidy-tree'.
146 ;; 17. Added `tidy-html-site'.
147
148 ;;;; ToDo:
149 ;;
150 ;; 1. Automatically set "char-encoding" according to the buffer encoding
151 ;; 2. Should check value of HTML_TIDY environment variable.
152
153
154 ;;;; Bugs:
155
156 ;; Requires a version of HTML Tidy that understands the "-f"
157 ;; "-config" "--show-body-only" command line options e.g. source-forge
158 ;; pre-release.
159 ;;
160 ;; There may be a bug with setting doctypes.  I don't use this feature
161 ;; yet and, well, don't really know how its supposed to work:-)
162 ;;
163 ;; Care with character encodings!!
164
165
166 ;;; History:
167
168 ;; 2006-05-09: New features 10-17 above.
169 ;;             - Lennart Borgman
170 ;; 2006-05-24: Fixed some errors spotted by Andreas Roethler.
171
172
173 ;;;; Credits
174
175 ;; This code was inspired by an Emacs "tip" suggested by Pete Gelbman.
176 ;;
177 ;; Thanks to Hans-Michael Stahl for comments regarding XEmacs
178 ;; compatibility.
179 ;;
180 ;; Thanks to Thomas Baumann for bugfix's in `tidy-parse-config-file'
181 ;; and `tidy-buffer'.
182 ;;
183 ;; Thanks to Chris Lott for comments regarding installation and menu
184 ;; display
185 ;;
186 ;; Thanks to Jeroen Baekelandt for noting a problem with ange-ftp and
187 ;; inspiring `tidy-temp-directory'.
188
189 ;;;; Code:
190
191 ;;;;; Forward references (stuff which must come first)
192
193 (eval-when-compile (require 'cl))
194 (eval-when-compile (require 'ediff))
195 (eval-when-compile (require 'mumamo nil t))
196 (eval-when-compile (require 'ourcomments-util nil t))
197 (eval-when-compile
198   (add-to-list 'load-path default-directory))
199 (eval-when-compile (require 'html-site nil t))
200 (require 'easymenu) ;; This makes menus so much easier!
201 (require 'compile)  ;; To make the error buffer more sexy
202 (require 'cus-edit) ;; Just for face custom-button
203 (require 'help-mode)
204
205 ;; The following two are functions so that the same compiled code will
206 ;; work in both situations (time cost is negligible)
207
208 (defsubst tidy-xemacs-p ()
209   "Return t iff we are running XEmacs this session."
210   (not (null (string-match "^XEmacs.*" (emacs-version)))))
211
212 (defsubst tidy-windows-p ()
213   "Return t iff we are running on a Windows system."
214   (memq system-type '(emx win32 w32 mswindows ms-dos windows-nt)))
215
216 ;; function definitions
217
218 ;; XEmacs
219 (defalias 'tidy-x-event-function          'event-function)
220 (defalias 'tidy-x-event-object            'event-object)
221 (defalias 'tidy-x-find-menu-item          'find-menu-item)
222 (defalias 'tidy-x-get-popup-menu-response 'get-popup-menu-response)
223 (defalias 'tidy-x-make-event              'make-event)
224 (defalias 'tidy-x-misc-user-event-p       'misc-user-event-p)
225
226 ;;;;; User Variables
227
228 ;;;###autoload
229 (defgroup tidy nil
230   "Provides a simple interface to the HTML Tidy program -- a free
231 utility that can fix common errors in your mark-up and clean up
232 sloppy editing automatically. See
233
234       <http://tidy.sourceforge.net/>
235
236 for more details.  This package provides the following functions:
237
238       `tidy-buffer',
239       `tidy-parse-config-file',
240       `tidy-save-settings', and
241       `tidy-describe-options',
242
243 These can be invoked interactively (using M-x) or via the menu-bar.
244 The function `tidy-buffer' sends the current buffer to HTML Tidy,
245 replacing the existing contents with a \"tidied\" version.  If
246 `tidy-buffer' is given a prefix argument, tidy operates on the
247 current region, ignoring mark-up outside <BODY>...</BODY> tags
248 \(useful for writhing cgi scripts in Pearl).  Warnings and errors
249 are presented in a compilation buffer to facilitate tracking down
250 necessary changes (e.g. C-x ` is bound to `next-error').
251
252 This package also provides menu-bar support for setting Tidy's many
253 options, and includes support for Tidy configuration files.  The
254 function `tidy-parse-config-file' will synchronise options
255 displayed in the menu-bar with the settings in `tidy-config-file'.
256 This is normally called by the load-hook for your HTML editing mode
257 \(see installation instructions below).  The function
258 `tidy-save-settings' will save the current option settings to your
259 `tidy-config-file'.  Finally `tidy-describe-options' allows you to
260 browse the documentation strings associated with each option.
261 "
262   :group 'nxhtml
263   :group 'hypermedia)
264
265 ;; (defcustom tidy-use-ediff nil
266 ;;   "If non-nil call ediff in `tidy-buffer' instead of replacing."
267 ;;   :group 'tidy
268 ;;   :type 'boolean)
269
270 (defvar tidy-warnings 0)
271 (defvar tidy-errors 0)
272 (defvar tidy-message nil)
273
274 ;;(defvar tidy-batch-buffer nil)
275 (defvar tidy-batch-last-file nil)
276
277 (defvar tidy-default-config-file "~/.tidyrc")
278
279 (defvar tidy-config-file-parsed nil)
280
281 (defcustom tidy-config-file tidy-default-config-file
282   "Path to your default tidy configuration file.
283
284 This is used by `tidy-parse-config-file' to synchronise Tidy's behaviour
285 inside Emacs with its behaviour outside, and by `tidy-save-settings' to
286 set your configuration file from within Emacs.  If you never want this to
287 happen, set `tidy-config-file' to \"\"."
288   :group 'tidy
289   :type 'file
290   :set (lambda (symbol value)
291          (set-default symbol value)
292          (if (file-readable-p value)
293              ;; Just set the default values here:
294              ;;(tidy-parse-config-file)
295              ;; Just tell we need to parse:
296              (setq tidy-config-file-parsed nil)
297            (if (file-exists-p value)
298                (lwarn '(tidy-config-file)
299                       :warning "Tidy config file not readable: %s" value)
300              (unless (string= value tidy-default-config-file)
301                (lwarn '(tidy-config-file)
302                       :warning "Tidy config file not found: %s" value))))))
303
304
305 (defcustom tidy-shell-program "tidy"
306   "The HTML program."
307   :group 'tidy
308   :type '(choice (file :must-match t)
309                  (string :tag "File name (searched for in path): "))
310   :set (lambda (symbol value)
311          (set-default symbol value)
312          (unless (string= value "tidy")
313            (or (file-executable-p value)
314                (executable-find value)
315                (lwarn '(tidy-shell-program)
316                       :error "Tidy program not found: %s" value)))))
317
318 (defcustom tidy-temp-directory temporary-file-directory
319   "Directory where tidy places its temp files.  The default is the
320 current directory which works fine unless you are operating on remote
321 files via `ange-ftp' and its ilk, in which case it will try to place
322 the temp files on the remote server (and will probably fail).  If this
323 is the case try setting this variable to something like \"/tmp/\" or
324 \"/var/tmp/\"."
325   :group 'tidy
326   :type 'directory
327   :set-after '(temporary-file-directory))
328
329 (defcustom tidy-menu-lock t
330   " *Non-nil means menu is locked (i.e. doesn't pop down) when
331 selecting toggle and radio options.
332
333 See also `tidy-menu-x-position'."
334   :type 'boolean
335   :group 'tidy)
336
337 (defcustom tidy-menu-x-position 211
338   "Specify menu position height in pixels.
339
340 This variable is used to set the horizontal position of the locked
341 menu, so don't forget to adjust it if menu position is not ok.
342
343 See also `tidy-menu-lock'."
344   :type 'integer
345   :group 'tidy)
346
347 ;;;;; Local Variables
348
349 (defvar tidy-debug  nil
350   "If t then we rebuild everything on reload. Useful for debugging.")
351
352 ;;(eval-when-compile (setq tidy-debug t))
353
354 (defun tidy-toggle-debug ()
355   "Toggle value of tidy-debug."
356   (interactive)
357   (message "tidy-debug is %s" (setq tidy-debug (not tidy-debug))))
358
359 ;; (defun tidy-boolean-option-value (symbol)
360 ;;   "Return t when the symbol's value is \"yes\"."
361 ;;   (let ((name (symbol-name symbol)))
362 ;;     (assert (string= "tidy-" (substring name 0 5)))
363 ;;     (setq name (substring name 5))
364 ;;     (let ((entry (assoc name tidy-options-alist)))
365 ;;       (assert (string= "Boolean" (nth 2 entry)))))
366 ;;   (when (symbol-value symbol)
367 ;;     (string= (symbol-value symbol))))
368
369 (defvar tidy-options-alist nil
370   "An alist containing all valid tidy options.
371 Each element is a list of the form
372     (NAME, SUB-MENU, VALUE-TYPE, DEFAULT-VALUE, DOC-STRING).
373 This is used to automatically construct variables and a menu bar.
374 To add new or modify exiting options simply modify this list.")
375
376 ;; Fix-me: built the options list dynamically, point to
377 ;; http://tidy.sourceforge.net/docs/quickref.html for help
378 (defun tidy-build-options-alist ()
379   (when (and tidy-shell-program
380              (executable-find tidy-shell-program))
381     (let ((outbuf (get-buffer-create "* Tidy options *")))
382       (call-process tidy-shell-program
383                     nil     ;; No input
384                     outbuf  ;; Output here
385                     nil     ;; Do not display
386                     "-help-config")
387       (switch-to-buffer outbuf))))
388
389 (when (or (null tidy-options-alist) tidy-debug)
390   (setq tidy-options-alist
391         '(
392           ("add-xml-decl" "Fix Markup" "Boolean" "no"
393            "
394 Type: Boolean
395 Default: no
396 Example: y/n, yes/no, t/f, true/false, 1/0
397
398 This option specifies if Tidy should add the XML declaration when
399 outputting XML or XHTML. Note that if the input already includes an <?xml
400 ... ?> declaration then this option will be ignored.")
401
402 ;;        ("add-xml-pi" "Fix Markup" "Boolean" "no"
403 ;;         "
404 ;; Type: Boolean
405 ;; Default: no
406 ;; Example: y/n, yes/no, t/f, true/false, 1/0
407
408 ;; This option is the same as the add-xml-decl option.")
409
410           ("add-xml-space" "Fix Markup" "Boolean" "no"
411            "
412 Type: Boolean
413 Default: no
414 Example: y/n, yes/no, t/f, true/false, 1/0
415
416 This option specifies if Tidy should add xml:space=\"preserve\" to elements
417 such as <PRE>, <STYLE> and <SCRIPT> when generating XML. This is needed if
418 the whitespace in such elements is to be parsed appropriately without
419 having access to the DTD.")
420
421           ("alt-text" "Fix Markup" "String" ""
422            "
423 Type: String
424 Default: -none-
425
426 This option specifies the default \"alt=\" text Tidy uses for <IMG>
427 attributes. This feature is dangerous as it suppresses further
428 accessibility warnings. You are responsible for making your documents
429 accessible to people who can not see the images!")
430
431           ("ascii-chars" "Fix Markup" "Boolean" "yes"
432            "
433 Type: Boolean
434 Default: yes
435 Example: y/n, yes/no, t/f, true/false, 1/0
436
437 Can be used to modify behavior of -c (--clean yes) option.
438 Defaults to \"yes\" when using -c. Set to \"no\" to prevent
439 converting &emdash;, &rdquo;, and other named character entities
440 to their ascii equivalents.")
441
442           ("assume-xml-procins" "Fix Markup" "Boolean" "no"
443            "
444 Type: Boolean
445 Default: no
446 Example: y/n, yes/no, t/f, true/false, 1/0
447
448 This option specifies if Tidy should change the parsing of processing
449 instructions to require ?> as the terminator rather than >. This option is
450 automatically set if the input is in XML.")
451
452           ("bare" "Fix Markup" "Boolean" "no"
453            "
454 Type: Boolean
455 Default: no
456 Example: y/n, yes/no, t/f, true/false, 1/0
457
458 This option specifies if Tidy should strip Microsoft specific HTML from
459 Word 2000 documents, and output spaces rather than non-breaking spaces
460 where they exist in the input.")
461
462           ("break-before-br" "Fix Markup" "Boolean" "no"
463            "
464 Type: Boolean
465 Default: no
466 Example: y/n, yes/no, t/f, true/false, 1/0
467
468 This option specifies if Tidy should output a line break before each <BR>
469 element.")
470
471           ("char-encoding" "Encoding" "Encoding" "ascii"
472            "
473 Type: Encoding
474 Default: ascii
475 Example: ascii, latin1, raw, utf8, iso2022, mac, win1252
476
477 This option specifies the character encoding Tidy uses for both the input
478 and output. Possible values are: ascii, latin1, raw, utf8, iso2022, mac,
479 win1252. For ascii, Tidy will accept Latin-1 (ISO-8859-1) character
480 values, but will use entities for all characters whose value > 127. For
481 raw, Tidy will output values above 127 without translating them into
482 entities. For latin1, characters above 255 will be written as
483 entities. For utf8, Tidy assumes that both input and output is encoded as
484 UTF-8. You can use iso2022 for files encoded using the ISO-2022 family of
485 encodings e.g. ISO-2022-JP. For mac and win1252, Tidy will accept vendor
486 specific character values, but will use entities for all characters whose
487 value > 127.")
488
489           ("clean" "Fix Markup" "Boolean" "no"
490            "
491 Type: Boolean
492 Default: no
493 Example: y/n, yes/no, t/f, true/false, 1/0
494
495 This option specifies if Tidy should strip out surplus presentational tags
496 and attributes replacing them by style rules and structural markup as
497 appropriate. It works well on the HTML saved by Microsoft Office products.")
498
499           ("doctype" "Fix Markup" "DocType" "auto"
500            "
501 Type: DocType
502 Default: auto
503 Example: auto, omit, strict, loose, transitional, user specified fpi \(string\)
504
505 This option specifies the DOCTYPE declaration generated by Tidy. If set to
506 \"omit\" the output won't contain a DOCTYPE declaration. If set to \"auto\"
507 \(the default\) Tidy will use an educated guess based upon the contents of
508 the document. If set to \"strict\", Tidy will set the DOCTYPE to the strict
509 DTD. If set to \"loose\", the DOCTYPE is set to the loose \(transitional\)
510 DTD. Alternatively, you can supply a string for the formal public
511 identifier \(FPI\). For example:
512
513       doctype: \"-//ACME//DTD HTML 3.14159//EN\"
514
515 If you specify the FPI for an XHTML document, Tidy will set
516 the system identifier to the empty string. Tidy leaves the DOCTYPE for
517 generic XML documents unchanged.")
518
519           ("drop-empty-paras" "Fix Markup" "Boolean" "yes"
520            "
521 Type: Boolean
522 Default: yes
523 Example: y/n, yes/no, t/f, true/false, 1/0
524
525 This option specifies if Tidy should discard empty paragraphs. If set to
526 no, empty paragraphs are replaced by a pair of <BR> elements as HTML4
527 precludes empty paragraphs.")
528
529           ("drop-font-tags" "Fix Markup" "Boolean" "no"
530            "
531 Type: Boolean
532 Default: no
533 Example: y/n, yes/no, t/f, true/false, 1/0
534
535 This option specifies if Tidy should discard <FONT> and <CENTER> tags
536 rather than creating the corresponding style rules, but only if the clean
537 option is also set to yes.")
538
539           ("drop-proprietary-attributes" "Fix Markup" "Boolean" "no"
540            "
541 Type: Boolean
542 Default: no
543 Example: y/n, yes/no, t/f, true/false, 1/0
544
545 This option specifies if Tidy should strip out proprietary attributes,
546 such as MS data binding attributes.")
547
548           ("enclose-block-text" "Fix Markup" "Boolean" "no"
549            "
550 Type: Boolean
551 Default: no
552 Example: y/n, yes/no, t/f, true/false, 1/0
553
554 This option specifies if Tidy should insert a <P> element to enclose any
555 text it finds in any element that allows mixed content for HTML
556 transitional but not HTML strict.")
557
558           ("enclose-text" "Fix Markup" "Boolean" "no"
559            "
560 Type: Boolean
561 Default: no
562 Example: y/n, yes/no, t/f, true/false, 1/0
563
564 This option specifies if Tidy should enclose any text it finds in the body
565 element within a <P> element. This is useful when you want to take
566 existing HTML and use it with a style sheet.")
567
568 ;;        ("error-file" "Omit" "String" "-none-"
569 ;;         "
570 ;; Type: String
571 ;; Default: -none-
572
573 ;; This option specifies the error file Tidy uses for errors and
574 ;; warnings. Normally errors and warnings are output to \"stderr\".
575
576 ;; This is option is ignored in Emacs.")
577
578           ("escape-cdata" "Fix Markup" "Boolean" "no"
579            "
580 Type: Boolean
581 Default: no
582 Example: y/n, yes/no, t/f, true/false, 1/0
583
584 This option specifies if Tidy should convert <![CDATA[]]> sections to
585 normal text.")
586
587           ("fix-backslash" "Fix Markup" "Boolean" "yes"
588            "
589 Type: Boolean
590 Default: yes
591 Example: y/n, yes/no, t/f, true/false, 1/0
592
593 This option specifies if Tidy should replace backslash characters \"\\\" in
594 URLs by forward slashes \"/\".")
595
596           ("fix-bad-comments" "Fix Markup" "Boolean" "yes"
597            "
598 Type: Boolean
599 Default: yes
600 Example: y/n, yes/no, t/f, true/false, 1/0
601
602 This option specifies if Tidy should replace unexpected hyphens with \"=\"
603 characters when it comes across adjacent hyphens. The default is yes. This
604 option is provided for users of Cold Fusion which uses the comment syntax:
605 <!--- --->")
606
607           ("fix-uri" "Fix Markup" "Boolean" "yes"
608            "
609 Type: Boolean
610 Default: yes
611 Example: y/n, yes/no, t/f, true/false, 1/0
612
613 This option specifies if Tidy should check attribute values that carry
614 URIsfor illegal characters and if such are found, escape them as HTML 4
615 recommends.")
616
617           ("force-output" "Input/Output" "Boolean" "no"
618            "
619 Type: Boolean
620 Default: no
621 Example: y/n, yes/no, t/f, true/false, 1/0
622
623 This option specifies if Tidy should produce output even if errors are
624 encountered. Use this option with care - if Tidy reports an error,
625 this means Tidy was not able to, or is not sure how to, fix the error,
626 so the resulting output may not reflect your intention.")
627
628 ;;        ("gnu-emacs" "Omit" "Boolean" "no"
629 ;;         "
630 ;; Type: Boolean
631 ;; Default: no
632 ;; Example: y/n, yes/no, t/f, true/false, 1/0
633
634 ;; This option specifies if Tidy should change the format for reporting
635 ;; errors and warnings to a format that is more easily parsed by GNU
636 ;; Emacs.
637
638 ;; This option is automatically set in Emacs."  )
639
640           ("hide-comments" "Fix Markup" "Boolean" "no"
641            "
642 Type: Boolean
643 Default: no
644 Example: y/n, yes/no, t/f, true/false, 1/0
645
646 This option specifies if Tidy should print out comments.")
647
648           ("hide-endtags" "Fix Markup" "Boolean" "no"
649            "
650 Type: Boolean
651 Default: no
652 Example: y/n, yes/no, t/f, true/false, 1/0
653
654 This option specifies if Tidy should omit optional end-tags when
655 generating the pretty printed markup. This option is ignored if you are
656 outputting to XML.")
657
658           ("indent" "Indentation" "AutoBool" "no"
659            "
660 Type: AutoBool
661 Default: no
662 Example: auto, y/n, yes/no, t/f, true/false, 1/0
663
664 This option specifies if Tidy should indent block-level tags.  If set to
665 \"auto\", this option causes Tidy to decide whether or not to indent the
666 content of tags such as TITLE, H1-H6, LI, TD, TD, or P depending on whether
667 or not the content includes a block-level element. You are advised to avoid
668 setting indent to yes as this can expose layout bugs in some browsers.")
669
670           ("indent-attributes" "Indentation" "Boolean" "no"
671            "
672 Type: Boolean
673 Default: no
674 Example: y/n, yes/no, t/f, true/false, 1/0
675
676 This option specifies if Tidy should begin each attribute on a new line.")
677
678           ("indent-cdata" "Indent" "Boolean" "no"
679            "
680 Type: Boolean
681 Default: no
682 Example: y/n, yes/no, t/f, true/false, 1/0
683
684 This option specifies if Tidy should indent <![CDATA[]]> sections.")
685
686           ("indent-spaces" "Indentation" "Integer" "2"
687            "
688 Type: Integer
689 Default: 2
690 Example: 0, 1, 2, ...
691
692 This option specifies the number of spaces Tidy uses to indent content,
693 when indentation is enabled.")
694
695           ("input-encoding" "Encoding" "Encoding" "latin1"
696            "
697 Type: Encoding
698 Default: ascii
699 Example: ascii, latin1, raw, utf8, iso2022, mac, win1252
700
701 This option specifies the character encoding Tidy uses for the input. See
702 char-encoding for more info.")
703
704           ("input-xml" "Input/Output" "Boolean" "no"
705            "
706 Type: Boolean
707 Default: no
708 Example: y/n, yes/no, t/f, true/false, 1/0
709
710 This option specifies if Tidy should use the XML parser rather than the
711 error correcting HTML parser.")
712
713           ("join-classes" "Fix Markup" "Boolean" "no"
714            "
715 Type: Boolean
716 Default: no
717 Example: y/n, yes/no, t/f, true/false, 1/0
718
719 This option specifies if Tidy should combine class names to generate a
720 single new class name, if multiple class assignments are detected on an
721 element.")
722
723           ("join-styles" "Fix Markup" "Boolean" "yes"
724            "
725 Type: Boolean
726 Default: yes
727 Example: y/n, yes/no, t/f, true/false, 1/0
728
729 This option specifies if Tidy should combine styles to generate a single
730 new style, if multiple style values are detected on an element.")
731
732           ("keep-time" "Preference" "Boolean" "no"
733            "
734 Type: Boolean
735 Default: no
736 Example: y/n, yes/no, t/f, true/false, 1/0
737
738 This option specifies if Tidy should alter the last modified time for
739 files it writes back to. The default is no, which allows you to tidy files
740 without affecting which ones will be uploaded to a Web server when using a
741 tool such as 'SiteCopy'. Note that this feature may not work on some
742 platforms.")
743
744           ("literal-attributes" "Preference" "Boolean" "no"
745            "
746 Type: Boolean
747 Default: no
748 Example: y/n, yes/no, t/f, true/false, 1/0
749
750 This option specifies if Tidy should ensure that whitespace characters
751 within attribute values are passed through unchanged.")
752
753           ("logical-emphasis" "Fix Markup" "Boolean" "no"
754            "
755 Type: Boolean
756 Default: no
757 Example: y/n, yes/no, t/f, true/false, 1/0
758
759 This option specifies if Tidy should replace any occurrence of <I> by <EM>
760 and any occurrence of <B> by <STRONG>. In both cases, the attributes are
761 preserved unchanged. This option can be set independently of the clean and
762 drop-font-tags options.")
763
764           ("lower-literals" "Preference" "Boolean" "yes"
765            "
766 Type: Boolean
767 Default: yes
768 Example: y/n, yes/no, t/f, true/false, 1/0
769
770 This option specifies if Tidy should convert the value of an attribute
771 that takes a list of predefined values to lower case. This is required for
772 XHTML documents.")
773
774           ("markup" "Input/Output" "Boolean" "yes"
775            "
776 Type: Boolean
777 Default: yes
778 Example: y/n, yes/no, t/f, true/false, 1/0
779
780 This option specifies if Tidy should generate a pretty printed version of
781 the markup. Note that Tidy won't generate a pretty printed version if it
782 finds significant errors (see force-output).")
783
784           ("ncr" "Preference" "Boolean" "yes"
785            "
786 Type: Boolean
787 Default: yes
788 Example: y/n, yes/no, t/f, true/false, 1/0
789
790 This option specifies if Tidy should allow numeric character references.")
791
792           ("newline" "Encoding" "Encoding" "LF"
793            "
794 Type: Encoding
795 Default: LF
796 Example: LF, CRLF, CR
797
798 Line ending style. \(Only used in batch operations here.\)")
799
800           ("new-blocklevel-tags" "Tags" "Tag names" ""
801            "
802 Type: Tag names
803 Default: -none-
804 Example: tagX, tagY, ...
805
806 This option specifies new block-level tags. This option takes a space or
807 comma separated list of tag names. Unless you declare new tags, Tidy will
808 refuse to generate a tidied file if the input includes previously unknown
809 tags. Note you can't change the content model for elements such as
810 <TABLE>, <UL>, <OL> and <DL>.")
811
812           ("new-empty-tags" "Tags" "Tag names" ""
813            "
814 Type: Tag names
815 Default: -none-
816 Example: tagX, tagY, ...
817
818 This option specifies new empty inline tags. This option takes a space or
819 comma separated list of tag names. Unless you declare new tags, Tidy will
820 refuse to generate a tidied file if the input includes previously unknown
821 tags. Remember to also declare empty tags as either inline or blocklevel.")
822
823           ("new-inline-tags" "Tags" "Tag names" ""
824            "
825 Type: Tag names
826 Default: -none-
827 Example: tagX, tagY, ...
828
829 This option specifies new non-empty inline tags. This option takes a space
830 or comma separated list of tag names. Unless you declare new tags, Tidy
831 will refuse to generate a tidied file if the input includes previously
832 unknown tags.")
833
834           ("new-pre-tags" "Tags" "Tag names" ""
835            "
836 Type: Tag names
837 Default: -none-
838 Example: tagX, tagY, ...
839
840 This option specifies new tags that are to be processed in exactly the
841 same way as HTML's <PRE> element. This option takes a space or comma
842 separated list of tag names. Unless you declare new tags, Tidy will refuse
843 to generate a tidied file if the input includes previously unknown
844 tags. Note you can not as yet add new CDATA elements (similar to
845 <SCRIPT>).")
846
847           ("numeric-entities" "Preference" "Boolean" "no"
848            "
849 Type: Boolean
850 Default: no
851 Example: y/n, yes/no, t/f, true/false, 1/0
852
853 This option specifies if Tidy should output entities other than the
854 built-in HTML entities \(&amp;, &lt;, &gt; and &quot;\) in the numeric
855 rather than the named entity form.")
856
857           ("output-bom" "Encoding" "AutoBool" "auto"
858            "
859 Type: AutoBool
860 Default: auto
861 Example: auto, y/n, yes/no, t/f, true/false, 1/0
862
863 This option specifies if Tidy should write a Unicode Byte Order Mark
864 character (BOM; also known as Zero Width No-Break Space; has value of
865 U+FEFF) to the beginning of the output; only for UTF-8 and UTF-16 output
866 encodings. If set to \"auto\", this option causes Tidy to write a BOM to the
867 output only if a BOM was present at the beginning of the input. A BOM is
868 always written for XML/XHTML output using UTF-16 output encodings.")
869
870           ("output-encoding" "Encoding" "Encoding" "ascii"
871            "
872 Type: Encoding
873 Default: ascii
874 Example: ascii, latin1, raw, utf8, iso2022, mac, win1252
875
876 This option specifies the character encoding Tidy uses for the output. See
877 char-encoding for more info. May only be different from input-encoding for
878 Latin encodings (ascii, latin1, mac, win1252).")
879
880           ("output-xhtml" "Input/Output" "Boolean" "no"
881            "
882 Type: Boolean
883 Default: no
884 Example: y/n, yes/no, t/f, true/false, 1/0
885
886 This option specifies if Tidy should generate pretty printed output,
887 writing it as extensible HTML. This option causes Tidy to set the DOCTYPE
888 and default namespace as appropriate to XHTML. If a DOCTYPE or namespace
889 is given they will checked for consistency with the content of the
890 document. In the case of an inconsistency, the corrected values will
891 appear in the output. For XHTML, entities can be written as named or
892 numeric entities according to the setting of the \"numeric-entities\"
893 option.  The original case of tags and attributes will be preserved,
894 regardless of other options.")
895
896           ("output-xml" "Input/Output" "Boolean" "no"
897            "
898 Type: Boolean
899 Default: no
900 Example: y/n, yes/no, t/f, true/false, 1/0
901
902 This option specifies if Tidy should pretty print output, writing it as
903 well-formed XML. Any entities not defined in XML 1.0 will be written as
904 numeric entities to allow them to be parsed by a XML parser. The original
905 case of tags and attributes will be preserved, regardless of other
906 options.")
907
908           ("quiet" "Input/Output" "Boolean" "no"
909            "
910 Type: Boolean
911 Default: no
912 Example: y/n, yes/no, t/f, true/false, 1/0
913
914 This option specifies if Tidy should output the summary of the numbers of
915 errors and warnings, or the welcome or informational messages.")
916
917           ("quote-ampersand" "Preference" "Boolean" "yes"
918            "
919 Type: Boolean
920 Default: yes
921 Example: y/n, yes/no, t/f, true/false, 1/0
922
923 This option specifies if Tidy should output unadorned & characters as
924 &amp;.")
925
926           ("quote-marks" "Preference"  "Boolean" "no"
927            "
928 Type: Boolean
929 Default: no
930 Example: y/n, yes/no, t/f, true/false, 1/0
931
932 This option specifies if Tidy should output \" characters as &quot; as is
933 preferred by some editing environments. The apostrophe character \' is
934 written out as &#39; since many web browsers don't yet support &apos;.")
935
936           ("quote-nbsp" "Preference" "Boolean" "yes"
937            "
938 Type: Boolean
939 Default: yes
940 Example: y/n, yes/no, t/f, true/false, 1/0
941
942 This option specifies if Tidy should output non-breaking space characters
943 as entities, rather than as the Unicode character value 160 (decimal).")
944
945           ("raw" "Omit" "Boolean" "no"
946            "
947 Type: Boolean
948 Default: no
949 Example: y/n, yes/no, t/f, true/false, 1/0 char-encoding
950
951 Currently not used, but this option would be the same as the
952 char-encoding: raw option.")
953
954           ("repeated-attributes" "Fix Markup" ("keep-first" "keep-last") "keep-last"
955            "
956 Type: -
957 Default: keep-last
958 Example: keep-first, keep-last
959
960 This option specifies if Tidy should keep the first or last attribute, if
961 an attribute is repeated, e.g. has two align attributes.")
962
963
964           ("replace-color" "Fix Markup" "Boolean" "no"
965            "
966 Type: Boolean
967 Default: no
968 Example: y/n, yes/no, t/f, true/false, 1/0
969
970 This option specifies if Tidy should replace numeric values in color
971 attributes by HTML/XHTML color names where defined, e.g. replace
972 \"#ffffff\" with \"white\"."  )
973
974           ("slide-style" "Omit" "String"
975            "
976 Type: Name
977 Default: -none-
978 split Currently not used.")
979
980 ;;        ("show-body-only" "Omit" "Boolean" "no"
981 ;;         "
982 ;; Type: Boolean
983 ;; Default: no
984 ;; Example: y/n, yes/no, t/f, true/false, 1/0
985
986 ;; This option specifies if Tidy should print only the contents of the body
987 ;; tag as an HTML fragment. Useful for incorporating existing whole pages as
988 ;; a portion of another page.
989
990 ;; Emacs overrides this option.")
991
992           ("show-errors" "Input/Output" "Integer" "6"
993            "
994 Type: Integer
995 Default: 6
996 Example: 0, 1, 2, ...
997
998 This option specifies the number Tidy uses to determine if further errors
999 should be shown. If set to 0, then no errors are shown.")
1000
1001           ("show-warnings" "Input/Output" "Boolean" "yes"
1002            "
1003 Type: Boolean
1004 Default: yes
1005 Example: y/n, yes/no, t/f, true/false, 1/0
1006
1007 This option specifies if Tidy should suppress warnings. This can be useful
1008 when a few errors are hidden in a flurry of warnings.")
1009
1010           ("slide-style" "Omit" "String" ""
1011            "
1012 Type: Name
1013 Default: -none-
1014
1015 Currently not used.")
1016
1017           ("split" "Omit" "Boolean" "no"
1018             "
1019 Type: Boolean
1020 Default: no
1021 Example: y/n, yes/no, t/f, true/false, 1/0
1022
1023 This option specifies if Tidy should create a sequence of slides from
1024 the input, splitting the markup prior to each successive <H2>. The
1025 slides are written to \"slide001.html\", \"slide002.html\" etc.
1026
1027 There is currently no Emacs support for this option.")
1028
1029           ("tab-size" "Indentation" "Integer" "4"
1030            "
1031 Type: Integer
1032 Default: 4
1033 Example: 0, 1, 2, ...
1034
1035 This option specifies the number of columns that Tidy uses between
1036 successive tab stops. It is used to map tabs to spaces when reading the
1037 input. Tidy never outputs tabs.")
1038
1039           ("tidy-mark" "Preference" "Boolean" "yes"
1040            "
1041 Type: Boolean
1042 Default: yes
1043 Example: y/n, yes/no, t/f, true/false, 1/0
1044
1045 This option specifies if Tidy should add a meta element to the document
1046 head to indicate that the document has been tidied. Tidy won't add a meta
1047 element if one is already present.")
1048
1049           ("uppercase-attributes" "Preference" "Boolean" "no"
1050            "
1051 Type: Boolean
1052 Default: no
1053 Example: y/n, yes/no, t/f, true/false, 1/0
1054
1055 This option specifies if Tidy should output attribute names in upper
1056 case. The default is no, which results in lower case attribute names,
1057 except for XML input, where the original case is preserved.")
1058
1059           ("uppercase-tags" "Preference" "Boolean" "no"
1060            "
1061 Type: Boolean
1062 Default: no
1063 Example: y/n, yes/no, t/f, true/false, 1/0
1064
1065 This option specifies if Tidy should output tag names in upper case. The
1066 default is no, which results in lower case tag names, except for XML
1067 input, where the original case is preserved.")
1068
1069           ("word-2000" "Fix Markup" "Boolean" "no"
1070            "
1071 Type: Boolean
1072 Default: no
1073 Example: y/n, yes/no, t/f, true/false, 1/0
1074
1075 This option specifies if Tidy should go to great pains to strip out all
1076 the surplus stuff Microsoft Word 2000 inserts when you save Word documents
1077 as \"Web pages\".  Doesn't handle embedded images or VML.")
1078
1079           ("wrap" "Line Wrapping" "Integer" "68"
1080            "
1081 Type: Integer
1082 Default: 68
1083 Example: 0, 1, 2, ...
1084
1085 This option specifies the right margin Tidy uses for line wrapping. Tidy
1086 tries to wrap lines so that they do not exceed this length. Set wrap to
1087 zero if you want to disable line wrapping.")
1088
1089           ("wrap-asp" "Line Wrapping" "Boolean" "yes"
1090            "
1091 Type: Boolean
1092 Default: yes
1093 Example: y/n, yes/no, t/f, true/false, 1/0
1094
1095 This option specifies if Tidy should line wrap text contained within ASP
1096 pseudo elements, which look like: <% ... %>.")
1097
1098           ("wrap-attributes" "Line Wrapping" "Boolean" "no"
1099            "
1100 Type: Boolean
1101 Default: no
1102 Example: y/n, yes/no, t/f, true/false, 1/0
1103
1104 This option specifies if Tidy should line wrap attribute values, for
1105 easier editing. This option can be set independently of
1106 wrap-script-literals.")
1107
1108           ("wrap-jste" "Line Wrapping" "Boolean" "yes"
1109            "
1110 Type: Boolean
1111 Default: yes
1112 Example: y/n, yes/no, t/f, true/false, 1/0
1113
1114 This option specifies if Tidy should line wrap text contained within JSTE
1115 pseudo elements, which look like: <# ... #>.")
1116
1117           ("wrap-php" "Line Wrapping" "Boolean" "yes"
1118            "
1119 Type: Boolean
1120 Default: yes
1121 Example: y/n, yes/no, t/f, true/false, 1/0
1122
1123 This option specifies if Tidy should line wrap text contained within PHP
1124 pseudo elements, which look like: <?php ... ?>.")
1125
1126           ("wrap-script-literals" "Line Wrapping" "Boolean" "no"
1127            "
1128 Type: Boolean
1129 Default: no
1130 Example: y/n, yes/no, t/f, true/false, 1/0
1131
1132 This option specifies if Tidy should line wrap string literals that appear
1133 in script attributes. Tidy wraps long script string literals by inserting
1134 a backslash character before the line break.")
1135
1136           ("wrap-sections" "Line Wrapping" "Boolean" "yes"
1137            "
1138 Type: Boolean
1139 Default: yes
1140 Example: y/n, yes/no, t/f, true/false, 1/0
1141
1142 This option specifies if Tidy should line wrap text contained within
1143 <![... ]> section tags.")
1144
1145 ;;        ("write-back" "Omit" "Boolean" "no"
1146 ;;         "
1147 ;; Type: Boolean
1148 ;; Default: no
1149 ;; Example: y/n, yes/no, t/f, true/false, 1/0
1150
1151 ;; This option specifies if Tidy should write back the tidied markup to
1152 ;; the same file it read from. You are advised to keep copies of
1153 ;; important files before tidying them, as on rare occasions the result
1154 ;; may not be what you expect.
1155
1156 ;; This option is ignored by Emacs.")
1157           ))
1158   )
1159
1160 ;;;;; Create a variable for each option in `tidy-options-alist'"
1161
1162 ;; these variables are made buffer local so that different buffers can
1163 ;; use a different set of options
1164
1165 (let ((options-alist tidy-options-alist)
1166       option name symbol docstring)
1167
1168   (while (setq option (car options-alist))
1169     (setq name      (nth 0 option)
1170           docstring (nth 4 option)
1171           symbol    (intern (concat "tidy-" name)))
1172     ;; create the variable on the fly
1173     (put symbol 'variable-documentation docstring)
1174     (make-variable-buffer-local symbol)
1175     (set symbol nil) ;;default)
1176     (setq options-alist (cdr options-alist))
1177     )
1178   )
1179
1180
1181 ;;;;; Quick options setting
1182
1183 (defvar tidy-xhtml-values
1184   '(
1185     (add-xml-decl "yes")
1186     (add-xml-space "no")
1187     (doctype "auto")
1188     (escape-cdata "no")
1189     (fix-backslash "yes")
1190     (fix-bad-comments "yes")
1191     (fix-uri "yes")
1192     (indent "yes")
1193     (indent-cdata "yes")
1194     (indent-spaces "2")
1195     (join-classes "yes")
1196     (join-styles "yes")
1197     (lower-literals "yes")
1198     (newline "LF")
1199     (output-xhtml "yes")
1200     (output-xml "no")
1201     (quote-ampersand "yes")
1202     (quote-nbsp "yes")
1203     (tidy-mark "no")
1204     (uppercase-attributes "no")
1205     (uppercase-tags "no")))
1206
1207 (defun tidy-xhtml-options-ok ()
1208   (let ((ok t))
1209     (dolist (optval tidy-xhtml-values)
1210       (let* ((opt (car optval))
1211              (val (cadr optval))
1212              (nam (symbol-name opt))
1213              (ent (assoc nam tidy-options-alist))
1214              (def (nth 3 ent))
1215              (sym (intern (concat "tidy-" nam))))
1216         (when (equal val def)
1217           (setq val nil))
1218         (unless (equal val (symbol-value sym))
1219           (setq ok nil))))
1220     ok))
1221
1222 (defun tidy-show-xhtml-options ()
1223   "List the settings set by `tidy-set-xhtml-options'."
1224   (interactive)
1225   (with-output-to-temp-buffer (help-buffer)
1226     (with-current-buffer (help-buffer)
1227       (help-setup-xref (list #'tidy-show-xhtml-settings) (interactive-p))
1228       (let ((inhibit-read-only t)
1229             (point (point))
1230             s)
1231         (insert "Values that will be set by `tidy-set-xhtml-options'.  ")
1232         (setq s "Green")
1233         (put-text-property 0 (length s)
1234                            'face '(:foreground "green")
1235                            s)
1236         (insert s " indicates that the local value in the current buffer")
1237         (insert " is the value that would be set, ")
1238         (setq s "red")
1239         (put-text-property 0 (length s)
1240                            'face '(:foreground "red")
1241                            s)
1242         (insert s " indicates it is not.\n\n")
1243         (fill-region point (point))
1244         (dolist (optval tidy-xhtml-values)
1245           (let* ((opt (car optval))
1246                  (val (cadr optval))
1247                  (nam (symbol-name opt))
1248                  (ent (assoc nam tidy-options-alist))
1249                  (def (nth 3 ent))
1250                  (cur (symbol-value (intern (concat "tidy-" nam))))
1251                  (show (copy-seq val))
1252                 )
1253             (unless cur (setq cur def))
1254             (put-text-property 0 (length show)
1255                                'face
1256                                (if (equal val cur)
1257                                    '(:foreground "green")
1258                                  'face '(:foreground "red"))
1259                                show)
1260             (insert (format "%25s => %s\n" opt show))))))
1261     (with-no-warnings (print-help-return-message))))
1262
1263 (defun tidy-set-xhtml-options (&optional only-current-buffer)
1264   "Set option necessary to convert to XHTML.
1265 To get a list of this settings use `tidy-show-xhtml-options'.
1266
1267 Note that the option variables are buffer local. The default
1268 variable values are always set. If ONLY-CURRENT-BUFFER is nil set
1269 the buffer local variables in all buffers."
1270   (interactive (list
1271                 (not (y-or-n-p "Set XHTML options in all buffers? "))))
1272   (let ((buffers (if (not only-current-buffer)
1273                      (buffer-list)
1274                    (list (current-buffer)))))
1275     (dolist (buffer buffers)
1276       (when (buffer-live-p buffer)
1277         (with-current-buffer buffer
1278           (dolist (optval tidy-xhtml-values)
1279             (let* ((opt (car optval))
1280                    (val (cadr optval))
1281                    (nam (symbol-name opt))
1282                    (ent (assoc nam tidy-options-alist))
1283                    (def (nth 3 ent))
1284                    (symbol (intern (concat "tidy-" nam))))
1285               (when (equal val def)
1286                 (setq val nil))
1287               (set-default symbol val) ;; The least overhead I think
1288               (unless (equal val (symbol-value symbol))
1289                 (set symbol val)))))))))
1290
1291
1292 ;;;;; Menu Lock (adapted from printing.el)
1293
1294 ;; quite compiler
1295 (eval-when-compile
1296   (progn
1297          (or (boundp 'current-menubar)
1298              (defvar current-menubar nil))
1299          (or (fboundp 'tidy-menu-position)
1300              (defun  tidy-menu-position () ""))
1301          (or (fboundp 'tidy-menu-lock)
1302              (defun tidy-menu-lock (entry state path) ""))
1303          (or (fboundp 'tidy-menu-lookup)
1304              (defun tidy-menu-lookup (path) ""))
1305          ))
1306
1307 ;; always define these
1308 (defvar tidy-menu nil "Menu used by tidy.")
1309 (defvar tidy-menu-position nil)
1310 (defvar tidy-menu-state nil)
1311
1312 (cond
1313  ((tidy-xemacs-p)
1314   ;; XEmacs
1315   (defun tidy-menu-position ()
1316     (tidy-x-make-event
1317      'button-release
1318      (list 'button 1
1319            'x tidy-menu-x-position
1320            'y -5
1321            )))
1322
1323   ;; XEmacs
1324   (defun tidy-menu-lock (entry state path)
1325     (when (and (not (interactive-p)) tidy-menu-lock)
1326       (or (and tidy-menu-position (eq state tidy-menu-state))
1327           (setq tidy-menu-position (tidy-menu-position)
1328                 tidy-menu-state    state))
1329       (let* ((menu   (tidy-menu-lookup path))
1330              (result (tidy-x-get-popup-menu-response menu tidy-menu-position)))
1331         (and (tidy-x-misc-user-event-p result)
1332              (funcall (tidy-x-event-function result)
1333                       (tidy-x-event-object result))))
1334       (setq tidy-menu-position nil)))
1335
1336   ;; XEmacs
1337   (defun tidy-menu-lookup (path)
1338     (car (tidy-x-find-menu-item current-menubar (cons "Tidy" path)))))
1339
1340  (t
1341     ;; GNU Emacs
1342   (defun tidy-menu-position ()
1343     (let ()
1344       (list (list tidy-menu-x-position '-5)
1345             (selected-frame))))         ; frame
1346
1347   ;; GNU Emacs
1348 ;;   (defun tidy-menu-lock-old (entry state path)
1349 ;;     (when (and (not (interactive-p)) tidy-menu-lock)
1350 ;;       (or (and tidy-menu-position (eq state tidy-menu-state))
1351 ;;        (setq tidy-menu-position (tidy-menu-position )
1352 ;;              tidy-menu-state    state))
1353 ;;       (let* ((menu   (tidy-menu-lookup path))
1354 ;;           (result (x-popup-menu tidy-menu-position menu)))
1355 ;;      (and result
1356 ;;           (let ((command (lookup-key menu (vconcat result))))
1357 ;;             (if (fboundp command)
1358 ;;                 (funcall command)
1359 ;;               (eval command)))))
1360 ;;       (setq tidy-menu-position nil)))
1361   (defun tidy-menu-lock (entry state path)
1362     (when (and (not (interactive-p)) tidy-menu-lock)
1363       (or (and tidy-menu-position (eq state tidy-menu-state))
1364           (setq tidy-menu-position (tidy-menu-position )
1365                 tidy-menu-state    state))
1366       ;;(popup-menu tidy-menu tidy-menu-position)))
1367       (run-with-idle-timer 1 nil 'popup-menu tidy-menu tidy-menu-position)))
1368
1369   ;; GNU Emacs
1370   (defun tidy-menu-lookup (dummy)
1371     (lookup-key (current-local-map) [menu-bar Tidy])))
1372  )
1373
1374 ;;;;; Define classes of menu items
1375
1376 (defun tidy-set (var-sym value mess entry state &optional path)
1377   "Set the value of the symbol VAR-SYM to VALUE giving a message
1378 derived from VALUE and MESS.  Pass on menu data to `tidy-menu-lock'."
1379   (set var-sym value)
1380   (message "%s is %s" mess value)
1381   (tidy-menu-lock entry state path))
1382
1383 (defun tidy-boolean-entry (symbol name type default menu)
1384   "Returns a menu entry that allows us to toggle the value of SYMBOL.
1385 SYMBOL refers to the option called NAME which has default value
1386 DEFAULT.  TYPE should always have the value \"Boolean\".  MENU refers
1387 to the sub-menu that this item belongs to and POSITION its position in
1388 that list."
1389   (cond ((equal default "no")
1390          (list (vector name
1391                        (list 'tidy-set (list 'quote symbol)
1392                              (list 'if symbol 'nil "yes")
1393                              name
1394                              (list 'quote menu)
1395                              '(quote toggle)
1396                              )
1397                        :style 'toggle
1398                        :selected (list 'if symbol 't 'nil))))
1399
1400         ((equal default "yes")
1401          (list (vector name (list 'tidy-set (list 'quote symbol)
1402                                   (list 'if symbol 'nil "no")
1403                                   name
1404                                   (list 'quote menu)
1405                                   '(quote toggle)
1406                                   )
1407                        :style 'toggle
1408                        :selected (list 'if symbol 'nil 't))))))
1409
1410 (defun tidy-list-entry (symbol name type default menu)
1411 "Returns a menu entry that allows us to set via a radio button the
1412 value of SYMBOL.  SYMBOL refers to the option called NAME which has
1413 default value DEFAULT.  TYPE should be a list of the possible
1414 values. MENU refers to the sub-menu that this item belongs to and
1415 POSITION its position in that list."
1416   (let (value element)
1417       (while (setq value (car type))
1418         (if (equal value default)
1419             (setq element
1420                   (append element
1421                           (list
1422                            (vector
1423                             (concat name " is \"" value "\"")
1424                             (list 'tidy-set (list 'quote symbol)
1425                                   (list 'if symbol 'nil value)
1426                                   name
1427                                   (list 'quote menu)
1428                                   '(quote toggle)
1429                                   )
1430                             :style 'radio
1431                             :selected (list 'if symbol 'nil 't)
1432                             ))))
1433           (setq element
1434                 (append element
1435                         (list
1436                          (vector
1437                           (concat name " is \"" value "\"")
1438
1439                           (list 'tidy-set (list 'quote symbol)
1440                                 (list 'if symbol 'nil value)
1441                                 name
1442                                 (list 'quote menu)
1443                                 '(quote toggle)
1444                                 )
1445
1446                           :style 'radio
1447                           :selected (list
1448                                      'if (list 'string-equal symbol value)
1449                                      't 'nil)
1450                          )))))
1451         (setq type (cdr type)))
1452         element))
1453
1454 (defun tidy-set-string (symbol name default)
1455   "Set the value of SYMBOL identified by name to VALUE,
1456 unless VALUE equals DEFAULT, in which case we set it to nil."
1457   (interactive)
1458   (let* ((last-value (symbol-value symbol))
1459          (new-value
1460           (if (tidy-xemacs-p)
1461               (read-string (format "Set %s to: " name)
1462                            (or last-value default) nil) ;; no default arg
1463             (read-string (format "Set %s to: " name)
1464                          (or last-value default) nil default))))
1465     (set symbol (if (equal new-value default) nil new-value))))
1466
1467 (defun tidy-set-integer (symbol name default)
1468   "Set the value of SYMBOL identified by name to VALUE,
1469 unless VALUE = DEFAULT, in which case we set it to nil."
1470   (interactive)
1471   (let* ((last-value (symbol-value symbol))
1472          ;; careful to interpret the string as a number
1473          (new-value
1474           (string-to-number
1475            (if (tidy-xemacs-p)
1476                (read-string (format "Set %s to: " name)
1477                 (or last-value default) nil)
1478              (read-string (format "Set %s to: " name)
1479                 (or last-value default) nil default)
1480              ))))
1481          (set symbol (if (= new-value (string-to-number default)) nil
1482                        (number-to-string new-value)))))
1483
1484
1485 (defun tidy-string-entry (symbol name type default menu)
1486   "Returns a menu entry that allows us to set the value of SYMBOL.
1487 SYMBOL refers to the option called NAME which has default value
1488 DEFAULT.  TYPE should always be one of \"String\" \"Tags\", or
1489 \"DocType\".  MENU and POSITION are not used in this case."
1490
1491   (list (vector (concat "set " name)
1492                 (list 'tidy-set-string
1493                       (list 'quote symbol)
1494                       name default))))
1495
1496 (defun tidy-integer-entry (symbol name type default menu)
1497 "Returns a menu entry that allows us to set the value of SYMBOL.
1498 SYMBOL refers to the option called NAME which has default value
1499 DEFAULT.  TYPE should always have the value \"Integer\". MENU and
1500 POSITION are not used in this case. "
1501   (list (vector (concat "set " name)
1502                 (list 'tidy-set-integer
1503                       (list 'quote symbol)
1504                       name default))))
1505
1506 (defun tidy-exe-found ()
1507   (when tidy-shell-program
1508     ;;(file-executable-p (car (split-string tidy-shell-program)))))
1509     (or (file-executable-p tidy-shell-program)
1510         (executable-find tidy-shell-program))))
1511
1512
1513 (defvar tidy-top-menu nil
1514   "The first part of the menu.")
1515 (when (or (null tidy-top-menu) tidy-debug)
1516   (setq tidy-top-menu
1517         '("Tidy"
1518           ["Tidy Buffer" tidy-buffer
1519            :active (tidy-exe-found)]
1520
1521 ;;        ["Tidy Region" tidy-region
1522 ;;         :active (and (mark)
1523 ;;                         (tidy-exe-found))
1524 ;;         :keys "C-u \\[tidy-buffer]"]
1525
1526           ["Tidy Directory Tree" tidy-tree
1527            :active (tidy-exe-found)]
1528
1529           ["Tidy Site" tidy-tree
1530            :active (and (featurep 'html-site)
1531                         (tidy-exe-found))]
1532
1533           "----------------------------------------------"
1534
1535           ["Customize Tidy" (customize-group-other-window 'tidy)]
1536
1537 ;;           ["Use Ediff" (lambda () (interactive)
1538 ;;                          (setq tidy-use-ediff (not tidy-use-ediff)))
1539 ;;            :style toggle
1540 ;;            :selected tidy-use-ediff
1541 ;;            ]
1542
1543 ;;        ["Load Settings" tidy-parse-config-file
1544 ;;         :active (and tidy-config-file (file-exists-p tidy-config-file))]
1545
1546 ;;        ["Load Settings All Buffers" (lambda () (interactive)
1547 ;;                                          (tidy-parse-config-file t))
1548 ;;         :active (and tidy-config-file (file-readable-p tidy-config-file))]
1549
1550           ["Save Settings" tidy-save-settings
1551            :active (and tidy-config-file (file-readable-p tidy-config-file))]
1552
1553           "----------------------------------------------"
1554
1555           )))
1556
1557 (defvar tidy-newline-menu
1558   '("Set newline"
1559     ["LF"     (tidy-set 'tidy-newline
1560                         nil
1561                         "newline"
1562                         'newline
1563                         'toggle)
1564      :style radio
1565      :selected (if (null tidy-newline) t nil)]
1566
1567     ["CRLF"   (tidy-set 'tidy-newline
1568                         "CRLF"
1569                         "newline"
1570                         'newline
1571                         'toggle)
1572      :style radio
1573      :selected (if (equal tidy-newline "CRLF") t nil)]
1574
1575     ["CR"     (tidy-set 'tidy-newline
1576                         "CR"
1577                         "newline"
1578                         'newline
1579                         'toggle)
1580      :style radio
1581      :selected (if (equal tidy-newline "CRLF") t nil)]
1582     ))
1583
1584 (defvar tidy-doctype-menu nil "The second to last sub-menu.")
1585 (when (or (null tidy-doctype-menu) tidy-debug)
1586   (setq  tidy-doctype-menu
1587          '("Set doctype" ;; ==>
1588
1589            ["auto"   (tidy-set 'tidy-doctype
1590                                nil
1591                                "doctype"
1592                                'doctype
1593                                'toggle)
1594             :style radio
1595             :selected (if (null tidy-doctype)  t nil)]
1596
1597            ["omit"   (tidy-set 'tidy-doctype
1598                                "omit"
1599                                "doctype"
1600                                'doctype
1601                                'toggle)
1602             :style radio
1603             :selected (if (equal tidy-doctype "omit") t nil)]
1604
1605            ["strict" (tidy-set 'tidy-doctype
1606                                "strict"
1607                                "doctype"
1608                                'doctype
1609                                'toggle)
1610             :style radio
1611             :selected (if (equal tidy-doctype "strict") t nil)]
1612
1613            ["loose"  (tidy-set 'tidy-doctype
1614                                "loose"
1615                                "doctype"
1616                                'doctype
1617                                'toggle)
1618             :style radio
1619             :selected (if (equal tidy-doctype "loose") t nil)]
1620
1621            ["transitional"  (tidy-set 'tidy-doctype
1622                                       "transitional"
1623                                       "doctype"
1624                                       'doctype
1625                                       'toggle)
1626             :style radio
1627             :selected (if (equal tidy-doctype "transitional") t nil)]
1628
1629            ["fpi" (null nil) ;; stub function
1630             :style radio
1631             :selected (if (or (null tidy-doctype)
1632                               (equal tidy-doctype "omit")
1633                               (equal tidy-doctype "strict")
1634                               (equal tidy-doctype "loose"))
1635                           nil t) ]
1636
1637            ["reset fpi" (tidy-set-string 'tidy-doctype "doctype" "" "")]
1638            )))
1639
1640 (defconst tidy-emacs-encoding-lbl "Use Emacs' encoding")
1641
1642 (defun tidy-create-encoding-menu (label encoding-what msg-what)
1643   (list label ;; ==>
1644         (vector tidy-emacs-encoding-lbl   (list 'tidy-set (list 'quote encoding-what)
1645                                 tidy-emacs-encoding-lbl ;(list 'tidy-get-buffer-encoding)
1646                                 msg-what
1647                                 ''encoding
1648                                 ''toggle)
1649                 :style 'radio
1650                 :selected (list 'if (list 'equal encoding-what tidy-emacs-encoding-lbl) t nil)
1651                 )
1652
1653         "----------------------------------------------"
1654
1655         (vector "ascii"   (list 'tidy-set (list 'quote encoding-what)
1656                                 nil
1657                                 msg-what
1658                                 ''encoding
1659                                 ''toggle)
1660                 :style 'radio
1661                 :selected (list 'if (list 'null encoding-what) t nil) ;; default
1662                 )
1663
1664         (vector "raw"     (list 'tidy-set (list 'quote encoding-what)
1665                                 "raw"
1666                                 msg-what
1667                                 ''encoding
1668                                 ''toggle)
1669                 :style 'radio
1670                 :selected (list 'if (list 'equal encoding-what "raw") t nil))
1671
1672         (vector "latin1"  (list 'tidy-set (list 'quote encoding-what)
1673                                 "latin1"
1674                                 msg-what
1675                                 ''encoding
1676                                 ''toggle)
1677                 :style 'radio
1678                 :selected (list 'if (list 'equal encoding-what "latin1") t nil))
1679
1680         (vector "utf8"    (list 'tidy-set (list 'quote encoding-what)
1681                                 "utf8"
1682                                 msg-what
1683                                 ''encoding
1684                                 ''toggle)
1685                 :style 'radio
1686                 :selected (list 'if (list 'equal encoding-what "utf8") t nil))
1687
1688         (vector "iso2022" (list 'tidy-set (list 'quote encoding-what)
1689                                 "iso2022"
1690                                 msg-what
1691                                 ''encoding
1692                                 ''toggle)
1693                 :style 'radio
1694                 :selected (list 'if (list 'equal encoding-what "iso2022") t nil))
1695
1696         (vector "mac" (list 'tidy-set (list 'quote encoding-what)
1697                             "mac"
1698                             msg-what
1699                             ''encoding
1700                             ''toggle)
1701                 :style 'radio
1702                 :selected (list 'if (list 'equal encoding-what "mac") t nil))
1703
1704         (vector "win1252" (list 'tidy-set (list 'quote encoding-what)
1705                                 "win1252"
1706                                 msg-what
1707                                 ''encoding
1708                                 ''toggle)
1709                 :style 'radio
1710                 :selected (list 'if (list 'equal encoding-what "win1252") t nil))
1711
1712         ))
1713 ;; (defvar tidy-char-encoding-menu nil "The last sub-menu.")
1714 ;; (when (or (null tidy-char-encoding-menu) tidy-debug)
1715 ;;   (setq tidy-char-encoding-menu
1716 ;;         (tidy-create-encoding-menu
1717 ;;          "Set char-encoding" 'tidy-char-encoding "char-encoding")))
1718 ;; (defvar tidy-input-encoding-menu nil "The last sub-menu.")
1719 ;; (when (or (null tidy-input-encoding-menu) tidy-debug)
1720 ;;   (setq tidy-input-encoding-menu
1721 ;;         (tidy-create-encoding-menu
1722 ;;          "Set input-encoding" 'tidy-input-encoding "input-encoding")))
1723 (defvar tidy-output-encoding-menu nil "The last sub-menu.")
1724 (when (or (null tidy-output-encoding-menu) tidy-debug)
1725   (setq tidy-output-encoding-menu
1726         (tidy-create-encoding-menu
1727          "Set output-encoding" 'tidy-output-encoding "output-encoding")))
1728
1729 ;;;;; Create a menu item for each option that has a valid sub-menu
1730 ;; field
1731
1732 (when (or (null tidy-menu) tidy-debug)
1733   (let ((options-alist       tidy-options-alist)
1734
1735         ;; sub menus are divided into two parts with list type options
1736         ;; coming first, followed by the rest
1737
1738         markup-menu-bool     markup-menu-set
1739         line-wrap-menu-bool  line-wrap-menu-set
1740         preference-menu-bool preference-menu-set
1741         indent-menu-bool     indent-menu-set
1742         io-menu-bool         io-menu-set
1743         tags-menu-bool       tags-menu-set
1744
1745         name sub-menu type default symbol entry entry-function option)
1746
1747     (while (setq option (car options-alist))
1748       (setq name      (nth 0 option)
1749             sub-menu  (nth 1 option)
1750             type      (nth 2 option)
1751             default   (nth 3 option)
1752             symbol    (intern (concat "tidy-" name))
1753             entry     nil)
1754
1755       (cond ((equal type "Boolean")
1756              (setq entry-function 'tidy-boolean-entry))
1757
1758             ((equal type "AutoBool")
1759              (setq entry-function 'tidy-list-entry)
1760              (setq type '("auto" "yes" "no")))
1761
1762             ((equal type "DocType")
1763              (setq entry '())) ;; handled below
1764
1765             ((equal type "Tag names")
1766              (setq entry-function 'tidy-string-entry))
1767
1768             ((equal type "String")
1769              (setq entry-function 'tidy-string-entry))
1770
1771             ((equal type "Integer")
1772              (setq entry-function 'tidy-integer-entry))
1773
1774             ((equal type "Encoding")
1775              (setq entry '()));; handled below
1776
1777             ((listp type)
1778              (setq entry-function 'tidy-list-entry))
1779             (t
1780              (error (concat "Tidy: unhandled value type " type " for " name))))
1781
1782       (cond ((equal sub-menu "Fix Markup")
1783              (setq entry (funcall
1784                           entry-function
1785                           symbol
1786                           name
1787                           type
1788                           default
1789                           'markup))
1790
1791              (if (or (equal type "Boolean") (equal type "AutoBool") (listp type))
1792                  (setq markup-menu-bool (append markup-menu-bool entry))
1793                (setq markup-menu-set (append markup-menu-set entry))))
1794
1795             ((equal sub-menu "Indentation")
1796              (setq entry (funcall
1797                           entry-function
1798                           symbol
1799                           name
1800                           type
1801                           default
1802                           'indent))
1803
1804              (if (or (equal type "Boolean") (equal type "AutoBool") (listp type))
1805                  (setq indent-menu-bool (append indent-menu-bool entry))
1806                (setq indent-menu-set (append indent-menu-set entry))))
1807
1808             ((equal sub-menu "Line Wrapping")
1809              (setq entry (funcall
1810                           entry-function
1811                           symbol
1812                           name
1813                           type
1814                           default
1815                           'line-wrap))
1816
1817              (if (or (equal type "Boolean") (equal type "AutoBool") (listp type))
1818                  (setq line-wrap-menu-bool (append line-wrap-menu-bool entry))
1819                (setq line-wrap-menu-set (append line-wrap-menu-set entry))))
1820
1821             ((equal sub-menu "Input/Output")
1822              (setq entry (funcall
1823                           entry-function
1824                           symbol
1825                           name
1826                           type
1827                           default
1828                           'io))
1829
1830              (if (or (equal type "Boolean") (equal type "AutoBool") (listp type))
1831                  (setq io-menu-bool (append io-menu-bool entry))
1832                (setq io-menu-set (append io-menu-set entry))))
1833
1834             ((equal sub-menu "Preference")
1835              (setq entry (funcall
1836                           entry-function
1837                           symbol
1838                           name
1839                           type
1840                           default
1841                           'preference))
1842
1843              (if (or (equal type "Boolean") (equal type "AutoBool") (listp type))
1844                  (setq preference-menu-bool (append preference-menu-bool entry))
1845                (setq preference-menu-set (append preference-menu-set entry))))
1846
1847             ((equal sub-menu "Tags")
1848              (setq entry (funcall
1849                           entry-function
1850                           symbol
1851                           name
1852                           type
1853                           default
1854                           'tags))
1855
1856              (if (or (equal type "Boolean") (equal type "AutoBool"))
1857              (setq tags-menu-bool (append tags-menu-bool entry))
1858              (setq tags-menu-set (append tags-menu-set entry))))
1859             (t)) ;; we simple omit all other menus
1860
1861       (setq options-alist (cdr options-alist)))
1862
1863   (setq tidy-menu (append
1864                    tidy-top-menu
1865                    (list
1866                     (list "Quick Options Settings"
1867                           (vector "Set Options for XHTML"
1868                                   'tidy-set-xhtml-options
1869                                   :style 'toggle
1870                                   :selected '(tidy-xhtml-options-ok)
1871                                   )
1872                           (vector "Show Options for XHTML"
1873                                   'tidy-show-xhtml-options
1874                                   )
1875                           ))
1876                    (list (append (list "Advanced")
1877
1878
1879 ;;        "----------------------------------------------"
1880
1881 ;;        ["Menu Lock" (tidy-set 'tidy-menu-lock
1882 ;;                               (if tidy-menu-lock nil t)
1883 ;;                               "Menu Lock"
1884 ;;                               'top
1885 ;;                               'toggle)
1886 ;;         :style toggle
1887 ;;         :selected (if tidy-menu-lock t nil)
1888 ;;         ]
1889 ;;                                  (vector "Menu Lock"
1890 ;;                                          'tidy-menu-lock
1891 ;;                                          :style 'toggle
1892 ;;                                          :selected '(if tidy-menu-lock t nil)
1893 ;;                                          )
1894                                  (list (vector "Menu Lock"
1895                                                '(tidy-set 'tidy-menu-lock
1896                                                           (if tidy-menu-lock nil t)
1897                                                           "Menu Lock"
1898                                                           'top
1899                                                           'toggle
1900                                                           )
1901                                                :style 'toggle
1902                                                :selected '(if tidy-menu-lock t nil)
1903                                                ))
1904                                  (list (list "-------"))
1905
1906                                  (list (append (list "Fix Markup")
1907                                                markup-menu-bool
1908                                                markup-menu-set))
1909                                  (list (append  (list "Line Wrapping")
1910                                                 line-wrap-menu-bool
1911                                                 line-wrap-menu-set))
1912                                  (list (append (list "Preference")
1913                                                preference-menu-bool
1914                                                preference-menu-set))
1915                                  (list (append (list "Indentation")
1916                                                indent-menu-bool
1917                                                indent-menu-set))
1918                                  (list (append (list "Input/Output")
1919                                               io-menu-bool
1920                                               io-menu-set))
1921                                  (list (append (list "Tags")
1922                                                tags-menu-bool
1923                                                tags-menu-set))
1924                                  (list tidy-doctype-menu)
1925                                  (list tidy-output-encoding-menu)
1926                                  (list tidy-newline-menu)
1927                                  ))
1928                    '(["Describe Options" tidy-describe-options t])
1929                    (list (list "-------"))
1930                    '(["Tidy Home Page"
1931                       (lambda ()
1932                         "Open Tidy home page in your web browser."
1933                         (interactive)
1934                         (browse-url "http://tidy.sourceforge.net/"))
1935                       t])
1936                    ))
1937   )
1938 )
1939
1940 (defvar tidy-menu-symbol nil)
1941 ;;(tidy-build-menu (&optional map)
1942 ;;;###autoload
1943 (defun tidy-build-menu (&optional map)
1944   "Set up the tidy menu in MAP.
1945 Used to set up a Tidy menu in your favourite mode."
1946   (interactive) ;; for debugging
1947   (unless tidy-menu-symbol
1948     (unless tidy-config-file-parsed
1949       (tidy-parse-config-file)
1950       (setq tidy-config-file-parsed t))
1951     ;;(or map (setq map (current-local-map)))
1952     (easy-menu-remove tidy-menu)
1953     (easy-menu-define tidy-menu-symbol map "Menu for Tidy" tidy-menu)
1954     (setq tidy-menu-symbol (delete "Tidy" tidy-menu-symbol))
1955     (easy-menu-add tidy-menu map))
1956   t)
1957
1958 ;;;;; Option description support
1959
1960 ;; quiet FSF Emacs and XEmacs compilers
1961 (eval-when-compile
1962   (progn (or (fboundp 'event-point) (defun event-point (dummy) ""))
1963          (or (fboundp 'posn-point)  (defun posn-point  (dummy) ""))
1964          (or (fboundp 'event-start) (defun event-start (dummy) ""))))
1965
1966 (defun tidy-describe-this-option-mouse (click)
1967   (interactive "e")
1968   (let ((p (if (tidy-xemacs-p)
1969                (event-point click)
1970              (posn-point (event-start click)))))
1971     (tidy-describe-this-option p)))
1972
1973 (defun tidy-describe-this-option (&optional point)
1974   "Describe variable associated with the text at point."
1975   (interactive (list (point)))
1976
1977   (let* ((variable (get-text-property
1978                     point
1979                     'tidy-variable))
1980          value
1981          buffer) ;; reuse the help buffer
1982     (when variable
1983       (with-output-to-temp-buffer (help-buffer)
1984         (help-setup-xref (list #'tidy-describe-this-option point) (interactive-p))
1985         (with-current-buffer (help-buffer)
1986           (setq value (symbol-value variable))
1987           (insert (substring (symbol-name variable) 5) ;; clip the `tidy-' prefix
1988                   " is set to ")
1989           (if value (insert value) (insert "set to the default value"))
1990           (insert "\n\n" (documentation-property variable 'variable-documentation))
1991           (local-set-key [(q)] 'tidy-quit-describe-options)
1992           (with-no-warnings (print-help-return-message)))))))
1993
1994 (defun tidy-quit-describe-options ()
1995   "Rid thyself of any display associated with Tidy's options."
1996   (interactive)
1997   (bury-buffer (get-buffer "*tidy-options*"))
1998   (delete-windows-on (get-buffer "*tidy-options*"))
1999   (bury-buffer (get-buffer "*Help*"))
2000   (delete-windows-on (get-buffer "*Help*")))
2001
2002 ;; nicked this from cal-desk-calendar.el:-)
2003 (defun tidy-current-line ()
2004   "Get the current line number (in the buffer) of point."
2005   ;;(interactive)
2006   (save-restriction
2007     (widen)
2008     (save-excursion
2009       (beginning-of-line)
2010       (1+ (count-lines 1 (point))))))
2011
2012 (defun tidy-goto-line (line)
2013   (save-restriction
2014     (widen)
2015     (goto-char (point-min))
2016     (forward-line (1- line))))
2017
2018 (defun tidy-describe-options ()
2019   "Interactively access documentation strings for `tidy-' variables."
2020   (interactive)
2021   (let ((buffer (get-buffer "*tidy-options*")))
2022     (if buffer (pop-to-buffer buffer)
2023       ;; else build it from scratch
2024       (setq buffer (get-buffer-create "*tidy-options*"))
2025       (let* ((start 0)
2026             (end 0)
2027             name
2028             (count 0)
2029             (option-alist tidy-options-alist)
2030             (column2a (+ (length "drop-proprietary-attributes") 3))
2031             (column2b (/ (window-width) 3))
2032             (column2 (if (> column2a column2b) column2a column2b))
2033             (column3 (* 2 column2))
2034             (start-line 0)
2035             (third-length 0)
2036             (two-third-length 0))
2037
2038         (set-buffer buffer)
2039
2040         (setq buffer-read-only nil)
2041         (delete-region (point-min) (point-max)) ;; empty the buffer
2042
2043         ;; set up local bindings
2044         (if (tidy-xemacs-p)
2045             (local-set-key [(button2)] 'tidy-describe-this-option-mouse)
2046           (local-set-key [(mouse-2)] 'tidy-describe-this-option-mouse))
2047
2048         (local-set-key "\r" 'tidy-describe-this-option)
2049         (local-set-key [(q)] 'tidy-quit-describe-options)
2050
2051         (insert "Press RET over option to see its description.  "
2052                 "Type \"q\" to quit." "\n\n")
2053
2054         (setq start-line (tidy-current-line))
2055         (setq third-length (1+ (/ (length option-alist) 3) ))
2056         (setq two-third-length (1- (* 2 third-length)))
2057
2058         (while (setq name (car (car-safe option-alist)))
2059           (setq option-alist (cdr option-alist))
2060           (setq count (+ count 1))
2061
2062           (cond
2063            ((< count third-length)     ;; 0 <= count < third-length
2064             (setq start (point))
2065                 (insert name)
2066                 (setq end (point))
2067                 (insert "\n"))
2068            ((< count two-third-length) ;; third-length <= count < two-third-length
2069             (if (= count third-length)
2070                 (tidy-goto-line start-line)
2071               (forward-line 1))
2072             (end-of-line)
2073             (setq start (point))
2074             (indent-to-column column2)
2075             (setq end (point))
2076             (put-text-property start end 'mouse-face 'default)
2077             (setq start (point))
2078             (insert name)
2079             (setq end (point)))
2080            (t                          ;; two-third-length <= count < length
2081             (if (= count two-third-length)
2082                 (tidy-goto-line start-line)
2083               (forward-line 1))
2084             (end-of-line)
2085             (setq start (point))
2086             (indent-to-column column3)
2087             (setq end (point))
2088             (put-text-property start end 'mouse-face 'default)
2089             (setq start (point))
2090             (insert name)
2091             (setq end (point))))
2092
2093           ;; make the strings funky
2094           (put-text-property start end 'mouse-face 'highlight)
2095           (put-text-property start end 'tidy-variable (intern (concat "tidy-" name)))
2096           )
2097         (setq buffer-read-only t)
2098         ;;(beginning-of-buffer)
2099         (goto-char (point-min))
2100         (pop-to-buffer buffer)
2101         ))))
2102
2103 ;;;;; Configuration file support
2104
2105 (defun tidy-parse-config-file (&optional all-buffers)
2106   "Parse `tidy-config-file' and set variables accordingly.
2107 If `tidy-config-file' is nil or \"\" do nothing. If the file does
2108 not exist just give a message.
2109
2110 Note that the option variables are buffer local. The default
2111 variable values are always set. If ALL-BUFFERS is non-nil set the
2112 buffer local variables in all buffers."
2113   (interactive (list
2114                 (y-or-n-p "Set Tidy config file values in all buffers? ")))
2115   (tidy-set-xhtml-options all-buffers)
2116   (when (and tidy-config-file
2117              (not (string= "" tidy-config-file)))
2118     (if (not (file-exists-p tidy-config-file))
2119         (unless (string= tidy-config-file tidy-default-config-file)
2120           (message "Could not find Tidy config file \"%s\"." tidy-config-file))
2121       (message "Parsing config file...")
2122       (let ((html-buffer (current-buffer))
2123             (config-buffer (find-file-noselect tidy-config-file t))
2124             config-variables)
2125         (with-current-buffer config-buffer
2126           (goto-char (point-min)) ;; unnecessary but pedantic
2127
2128           ;; delete all comments
2129           (while (re-search-forward "//.*\n" nil t)
2130             (replace-match "" nil nil))
2131
2132           (goto-char (point-min))
2133           (while (re-search-forward "\\([a-z,-]+\\):\\s-*\\(.*\\)\\s-*" nil t)
2134             ;; set the variable
2135             ;; Thanks to Thomas Baumann for this bugfix
2136             (let ((variable (concat "tidy-" (match-string 1)))
2137                   (value (match-string 2)))
2138               ;;(set-default (intern variable value))
2139               (set-default (intern variable) value)
2140               (setq config-variables
2141                     (cons (cons variable value) config-variables))
2142               (with-current-buffer html-buffer
2143                 (set (intern variable) value))
2144               ))
2145
2146           (set-buffer-modified-p nil) ;; don't save changes
2147           (kill-buffer config-buffer))
2148         (when all-buffers
2149           (dolist (buffer (buffer-list))
2150             (when (buffer-live-p buffer)
2151               (with-current-buffer buffer
2152                 (dolist (optval config-variables)
2153                   (let* ((opt (car optval))
2154                          (val (cdr optval))
2155                          (sym (intern opt)))
2156                     (set sym val))))))))
2157       (message "Parsing config file...done")
2158       )))
2159
2160 (defun tidy-save-settings (&optional config-file)
2161   "Query saving the current settings to your `tidy-config-file'.
2162 The local values in the current buffer will be saved."
2163   (interactive)
2164   (or config-file (setq config-file tidy-config-file))
2165   (when config-file
2166
2167       ;; should check for locks!
2168       (if (or (not (interactive-p))
2169               (y-or-n-p "Save settings to your tidy configuration file? "))
2170
2171           (let ((buffer (find-file-noselect config-file t))
2172                 (option-alist tidy-options-alist)
2173                 (outer-buffer (current-buffer))
2174                 option name symbol value)
2175             (with-current-buffer buffer
2176               (delete-region (point-min) (point-max)) ;; clear the buffer
2177
2178               ;; need this line so that config file is always non empty
2179               (insert "// HTML Tidy configuration file \n")
2180               (while (setq option (car option-alist))
2181                 (setq option-alist (cdr option-alist))
2182                 (setq name      (nth 0 option)
2183                       symbol    (intern (concat "tidy-" name)))
2184                 (with-current-buffer outer-buffer
2185                   (setq value (symbol-value symbol)))
2186                 (when (string= value tidy-emacs-encoding-lbl)
2187                   (setq value (tidy-get-buffer-encoding)))
2188                 (when value ;; nil iff default
2189                   (insert (car option) ": " value "\n")))
2190
2191               (save-buffer)
2192               ;;(basic-save-buffer)
2193               (kill-buffer buffer)
2194               )))))
2195
2196
2197 ;;;;; Main user function
2198
2199 (eval-when-compile (defvar tidy-markup nil ""))
2200
2201 (defun tidy-set-buffer-unmodified (dummy1 dummy2 dumm3)
2202   "Used to prevent error buffer form being marked as modified."
2203   (set-buffer-modified-p nil))
2204
2205 ;; See http://www.mhonarc.org/MHonArc/doc/resources/charsetaliases.html
2206 (defconst tidy-encodings-mime-charset-list
2207   '(
2208     ;; ("raw") ;; Same as ascii??
2209     ("ascii"      . "us-ascii")
2210     ("latin0"     . "iso-8859-15")
2211     ("latin1"     . "iso-8859-1")
2212     ("iso2022"    . "iso-2022-jp") ;; Correct? There are several iso-2022-..
2213     ("utf8"       . "utf-8")
2214     ("mac"        . "macintosh")
2215     ("win1252"    . "windows-1252")
2216     ("ibm858"     . "cp850")
2217     ("utf16le"    . "utf-16-le")
2218     ("utf16be"    . "utf-16-be")
2219     ("utf16"      . "utf-16")
2220     ("big5"       . "big5")
2221     ("shiftjis"   . "shift_jis")
2222     )
2223   "Encoding names used by Tidy and Emacs.
2224 First column is Tidy's name, second Emacs' name."
2225   )
2226
2227 (defun tidy-get-buffer-encoding ()
2228   "Get Tidy's name for value of `buffer-file-coding-system'."
2229   (tidy-get-tidy-encoding buffer-file-coding-system))
2230
2231 (defun tidy-get-tidy-encoding (emacs-coding-system)
2232   (let ((encoding (rassoc
2233                    (symbol-name
2234                     (coding-system-get emacs-coding-system 'mime-charset))
2235                    tidy-encodings-mime-charset-list)))
2236     (if encoding
2237         (setq encoding (car encoding))
2238       (setq encoding "raw"))
2239     encoding))
2240
2241 (defun tidy-temp-config-file ()
2242   (expand-file-name "temp-tidy-config"
2243                     tidy-temp-directory))
2244
2245 (defconst tidy-output-buf-name "Tidy (X)HTML Output")
2246
2247 (defvar tidy-tidied-buffer nil)
2248 (make-variable-buffer-local 'tidy-tidied-buffer)
2249 (put 'tidy-tidied-buffer 'permanent-local t)
2250
2251 (defun tidy-check-is-tidied (orig-buf tidy-buf)
2252   (with-current-buffer tidy-buf
2253     (unless tidy-tidied-buffer
2254       (error "%s is not a tidy output buffer" tidy-buf))
2255     (unless (eq orig-buf tidy-tidied-buffer)
2256       (error "Buffer does not contain tidied %s" orig-buf))))
2257
2258 (defconst tidy-control-buffer-name "Tidy Control Buffer")
2259
2260 (defvar tidy-output-encoding) ;; dyn var
2261
2262 (defun tidy-buffer ()
2263   "Run the HTML Tidy program on the current buffer.
2264 Show the errors in a buffer with buttons to:
2265
2266 - show the original buffer
2267 - show the tidied output
2268 - copy tidied to original
2269 - run ediff
2270
2271 This buffer also contains any error and warning messages and they
2272 link to the original source code.
2273
2274 If the buffer to tidy contains a fictive XHTML validation header
2275 \(see `nxhtml-validation-header-mode') then the corresponding
2276 header is added before running tidy. This header is removed again
2277 after tidying, together with additions tidy might have done at
2278 the end of the buffer.
2279
2280 You may tidy part of the buffer, either by narrowing the buffer
2281 or by selecting a region and having it visibly marked (`cua-mode'
2282 etc). A fictive XHTML validation header will apply as
2283 above. However if there is no such header then when you tidy part
2284 of the buffer still a hopefully suitable header is added before
2285 calling tidy."
2286 ;; Fix-me: copy back parts outside visible region
2287   (interactive)
2288   (message "starting tidy-buffer")
2289   (let* ((is-narrowed (buffer-narrowed-p))
2290          (validation-header (when (boundp 'rngalt-validation-header)
2291                               (let ((header (nth 2 rngalt-validation-header)))
2292                                 (when header (concat header "\n")))))
2293          (region-restricted (and mark-active
2294                                  transient-mark-mode
2295                                  (or (< (point-min) (region-beginning))
2296                                      (< (region-end) (point-max)))))
2297          (partial (or validation-header
2298                       is-narrowed
2299                       region-restricted))
2300          (start (if region-restricted (region-beginning) (point-min)))
2301          (end   (if region-restricted (region-end) (point-max)))
2302          (region-header (when region-restricted
2303                           (when (< (point-min) (region-beginning))
2304                             (buffer-substring-no-properties
2305                              (point-min) (region-beginning)))))
2306          (region-footer (when region-restricted
2307                           (when (< (region-end) (point-max))
2308                             (buffer-substring-no-properties
2309                              (region-end) (point-max)))))
2310          (tidy-beg-mark "<!-- TIDY BEGIN MARK 142505535 -->\n")
2311          (tidy-end-mark "<!-- TIDY END MARK 143345187 -->")
2312          ;;(whole-file t) ;(and (= start 1) (= end   (1+ (buffer-size)))))
2313          (filename buffer-file-name)
2314          ;;(orig-dir (file-name-directory filename))
2315          (orig-buffer (current-buffer))
2316          (work-buffer (get-buffer-create "* Tidy Temporary Work Buffer *"))
2317          (line-header-offset nil)
2318
2319          ;; Gasp! We have to use temp files here because the command
2320          ;; line would likely get too long!
2321
2322          (error-buf-name tidy-control-buffer-name)
2323
2324          (error-file (expand-file-name error-buf-name
2325                                        tidy-temp-directory))
2326
2327          (error-buffer (get-buffer-create error-buf-name))
2328
2329          (output-buffer (get-buffer-create tidy-output-buf-name))
2330
2331          (config-file (tidy-temp-config-file))
2332
2333          ;;(eol (coding-system-eol-type buffer-file-coding-system))
2334          ;;(encoding (coding-system-get buffer-file-coding-system 'mime-charset))
2335          ;;(errors 0)
2336          ;;(warnings 0)
2337          (tidy-message "")
2338          (seg-error nil)
2339          ;;(use-ediff tidy-use-ediff)
2340
2341          (want-mumamo nil)
2342          )
2343
2344 ;;     (when (and use-ediff
2345 ;;                (not tidy-show-warnings)) ;; default "yes" hence inverted logic
2346 ;;       (setq use-ediff (y-or-n-p "Warning can not be shown when using ediff. Still use ediff? ")))
2347     (unless buffer-file-name
2348       (error "Can't tidy buffer with no file name"))
2349
2350     (when (buffer-modified-p orig-buffer)
2351       (error "Can't tidy buffer because it is modified"))
2352
2353     (with-current-buffer error-buffer
2354       (setq buffer-read-only nil)
2355       (erase-buffer)
2356       (setq tidy-tidied-buffer orig-buffer))
2357
2358     ;; OK do the tidy
2359 ;;     (message "coding-system: %s, %s, %s"
2360 ;;              (find-operation-coding-system
2361 ;;               'call-process-region start end command)
2362 ;;              coding-system-for-write
2363 ;;              buffer-file-coding-system)
2364     (let* ((coding-system-for-write buffer-file-coding-system)
2365            (tidy-input-encoding (tidy-get-tidy-encoding coding-system-for-write)))
2366
2367       (let ((output-mode (if (not (featurep 'mumamo))
2368                              major-mode
2369                            (if (and (boundp 'mumamo-multi-major-mode)
2370                                     mumamo-multi-major-mode)
2371                                mumamo-multi-major-mode
2372                              major-mode))))
2373         (with-current-buffer output-buffer
2374           (erase-buffer)
2375           ;;(when (and (fboundp 'mumamo-mode) mumamo-mode) (setq want-mumamo t) (mumamo-mode 0))
2376           (funcall output-mode)
2377           (set (make-local-variable 'coding-system-for-read) coding-system-for-write)))
2378
2379       (let ((tidy-output-encoding tidy-output-encoding))
2380         (unless tidy-output-encoding
2381           (setq tidy-output-encoding tidy-input-encoding))
2382         ;;(message "tidy-input-enc=%s, tidy-output-enc=%s" tidy-input-encoding tidy-output-encoding)
2383         (tidy-save-settings config-file)
2384         )
2385
2386       ;; Tidy does not replace the xml declaration so it must be removed:
2387                                         ;(setq tidy-add-xml-decl "no")
2388       (let (;(orig-min (point-min)) (orig-max (point-max))
2389             )
2390 ;;         (when region-restricted
2391 ;;           ;; We have a visible region
2392 ;;           (setq orig-min (region-beginning))
2393 ;;           (setq orig-max (region-end)))
2394         (with-current-buffer work-buffer
2395           (erase-buffer)
2396           (when validation-header
2397             (insert validation-header))
2398           (when partial
2399             ;; Have to insert things to make tidy insert at correctt
2400             ;; position. A bit of guessing here to keep it simple.
2401             (let ((p "\n<p>TIDY</p>\n")
2402                   (b "\n<body>\n")
2403                   (he "\n<head><title></title></head>\n")
2404                   (ht (concat
2405                        "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
2406                        "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
2407                        "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
2408                        "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n")))
2409               (goto-char (point-min))
2410               (if (re-search-forward "<body[^>]*>" nil t)
2411                   (insert p)
2412                 (if (search-forward "</head>" nil t)
2413                     (insert b p)
2414                   (if (re-search-forward "<html[^>]*>" nil t)
2415                       (insert he b p)
2416                     (insert ht he b p))))
2417               (goto-char (point-max))
2418               (insert "\n" tidy-beg-mark)))
2419           (setq line-header-offset (line-number-at-pos))
2420           (insert-buffer-substring orig-buffer start end) ;orig-min orig-max)
2421           (when partial (insert "\n" tidy-end-mark "\n"))
2422           (let ((args
2423                  (list
2424                   ;;start end
2425                   (point-min) (point-max)
2426                   tidy-shell-program
2427                   nil
2428                   output-buffer
2429                   t
2430                   "-config" config-file
2431                   "--error-file" error-file
2432                   "--write-back" "no"
2433                   ;;(if (not whole-file) "--show-body-only" "--show-body-only")
2434                   ;;(if (not whole-file) "yes" "no")
2435                   "--show-body-only" "no"
2436                   "--gnu-emacs" "yes"
2437                   "--gnu-emacs-file" (file-name-nondirectory filename)
2438                   ))
2439                 (default-directory (file-name-directory filename))
2440                 )
2441             (apply 'call-process-region args)))
2442         ))
2443
2444
2445     ;; Since XEmacs can't grab the std error stream we use an error file
2446     ;;(setq error-buffer (find-file-noselect error-file t))
2447     (with-current-buffer error-buffer
2448       (setq tidy-tidied-buffer orig-buffer)
2449       (insert-file-contents error-file)
2450       ;; Change the line numbers if a header was inserted
2451       (when line-header-offset
2452         (goto-char (point-min))
2453         (while (re-search-forward ":\\([0-9]+\\):[0-9]+" nil t)
2454           (let ((line (1+ (- (string-to-number (match-string-no-properties 1))
2455                              line-header-offset))))
2456             (replace-match (number-to-string line) nil nil nil 1))))
2457         )
2458
2459     ;; avoid leaving these guys lying around
2460     (if (file-exists-p error-file)  (delete-file error-file))
2461     ;;(if (file-exists-p config-file) (delete-file config-file))
2462
2463     ;; scan the buffer for error strings
2464     (with-current-buffer error-buffer
2465       ;;(local-set-key [tab] 'tidy-errbuf-forward)
2466       (goto-char (point-min))
2467       (insert "\n")
2468       (make-local-variable 'widget-button-face)
2469       (setq widget-button-face custom-button)
2470       (set (make-local-variable 'widget-push-button-prefix) "")
2471       (set (make-local-variable 'widget-push-button-suffix) "")
2472       (set (make-local-variable 'widget-link-prefix) "")
2473       (set (make-local-variable 'widget-link-suffix) "")
2474       (widget-create 'push-button
2475                      :tag " Show Source "
2476                      :keymap (make-sparse-keymap)
2477                      :arg-orig orig-buffer
2478                      :action (lambda (widget &optional event)
2479                                (let ((orig-buf (widget-get widget :arg-orig))
2480                                      (curr-win (selected-window)))
2481                                  (switch-to-buffer-other-window orig-buf)
2482                                  (select-window curr-win))))
2483       (insert " ")
2484       (widget-create 'push-button
2485                      :tag " Show Tidied "
2486                      :keymap (make-sparse-keymap)
2487                      :arg-tidy output-buffer
2488                      :arg-orig orig-buffer
2489                      :action (lambda (widget &optional event)
2490                                (let ((tidy-buf (widget-get widget :arg-tidy))
2491                                      (orig-buf (widget-get widget :arg-orig))
2492                                      (curr-win (selected-window)))
2493                                  (tidy-check-is-tidied orig-buf tidy-buf)
2494                                  (switch-to-buffer-other-window tidy-buf)
2495                                  (select-window curr-win))))
2496
2497
2498       (insert " ")
2499       (widget-create 'push-button
2500                      :tag " Use Tidied "
2501                      :keymap (make-sparse-keymap)
2502                      :arg-tidy output-buffer
2503                      :arg-orig orig-buffer
2504                      :action (lambda (widget &optional event)
2505                                (message "Copying ...")
2506                                (let* ((orig-buf (widget-get widget :arg-orig))
2507                                       (tidy-buf (widget-get widget :arg-tidy))
2508                                       (orig-buf-str
2509                                        (save-restriction
2510                                          (with-current-buffer orig-buf
2511                                            (widen)
2512                                            (buffer-substring-no-properties (point-min) (point-max)))))
2513                                       (tidy-buf-str
2514                                        (save-restriction
2515                                          (with-current-buffer tidy-buf
2516                                            (widen)
2517                                            (buffer-substring-no-properties (point-min) (point-max)))))
2518                                       )
2519                                  (tidy-check-is-tidied orig-buf tidy-buf)
2520                                  (kill-buffer (current-buffer))
2521                                  (kill-buffer tidy-buf)
2522                                  (if (string= orig-buf-str tidy-buf-str)
2523                                      (message "Original buffer's and tidied buffer's contents are equal")
2524                                    (with-current-buffer orig-buf
2525                                      (erase-buffer)
2526                                      (insert tidy-buf-str)
2527                                      (goto-char (point-min))
2528                                      (delete-window (selected-window))
2529                                      (switch-to-buffer orig-buf)
2530                                      (message "Copied to %s" orig-buf))))))
2531       (insert " ")
2532       (widget-create 'push-button
2533                      :tag " Ediff "
2534                      :keymap (make-sparse-keymap)
2535                      :arg-tidy output-buffer
2536                      :arg-orig orig-buffer
2537                      :action (lambda (widget &optional event)
2538                                (require 'ediff)
2539                                (let ((orig-buf (widget-get widget :arg-orig))
2540                                      (tidy-buf (widget-get widget :arg-tidy))
2541                                      ;; Fix-me: How should ediff-actual-options be set?
2542                                      (old-ediff-actual-diff-options (default-value 'ediff-actual-diff-options))
2543                                      (new-ediff-actual-diff-options " -a -b -w "))
2544                                  (with-current-buffer orig-buf (setq ediff-actual-diff-options " -a -b -w "))
2545                                  (with-current-buffer tidy-buf (setq ediff-actual-diff-options " -a -b -w "))
2546                                  (tidy-check-is-tidied orig-buf tidy-buf)
2547                                  (set-default 'ediff-actual-diff-options new-ediff-actual-diff-options)
2548                                  (tidy-ediff-buffers orig-buf tidy-buf)
2549                                  (set-default 'ediff-actual-diff-options old-ediff-actual-diff-options)
2550                                  )))
2551       ;;(widget-setup)
2552
2553       (insert "\n\n")
2554       (when (re-search-forward (concat
2555                                 "\\([0-9]+\\) warnings?, "
2556                                 "\\([0-9]+\\) errors? were found!")
2557                                nil t)
2558         (setq tidy-warnings (string-to-number (match-string 1)))
2559         (setq tidy-errors (string-to-number (match-string 2)))
2560         (setq tidy-message (match-string 0)))
2561
2562       (goto-char (point-min))
2563       ;;(while (re-search-forward "stdin:" nil t) (replace-match (concat filename ":")))
2564       (wab-compilation-mode)
2565       (set-buffer-modified-p nil)
2566       (goto-char (point-min))
2567       ;; Fix-me: How should this be run? Some hook for compilation I
2568       ;; guess, but what is the name of it?
2569       ;; (wab-forward)
2570       )
2571
2572
2573     (when (buffer-live-p output-buffer)
2574       ;; Catch segmentation violations
2575       ;; Sometimes get this when editing files from Macs
2576       ;; See the function at the bottom of the file
2577       (with-current-buffer output-buffer
2578         (goto-char (point-min))
2579         (let ((case-fold-search t))
2580           (if (looking-at "Segmentation") ;; might work with XEmacs
2581               (setq seg-error t))))
2582       ;; Fix-me: add parts outside region
2583       (when partial
2584         (with-current-buffer output-buffer
2585           (goto-char (point-min))
2586           (when (search-forward tidy-beg-mark nil t)
2587             (delete-region (point-min) (point)))
2588           (when region-header (insert region-header))
2589           (when (search-forward tidy-end-mark nil t)
2590             (backward-char (length tidy-end-mark))
2591             (delete-region (point) (point-max)))
2592           (when region-footer (insert region-footer))))
2593       )
2594
2595     (unless (or (> tidy-errors 0) seg-error)
2596       ;; Do not know if the window stuff is needed?
2597       (let* ((window (get-buffer-window (current-buffer)))
2598              (top (window-start window)))
2599
2600         (unless tidy-markup ;; default is "yes" hence inverted logic
2601           (when (eq system-type 'windows-nt)
2602             (tidy-remove-ctrl-m output-buffer))
2603           (with-current-buffer output-buffer
2604             (setq tidy-tidied-buffer orig-buffer)
2605             (delete-trailing-whitespace)
2606             (indent-region (point-min) (point-max))
2607             (goto-char (point-min))))
2608
2609         ;; Try not to move the window too much when we tidy the whole buffer
2610         (set-window-start window top)))
2611
2612     (switch-to-buffer-other-window error-buffer)
2613
2614     (if seg-error
2615         (message (concat "Tidy: Segmentation violation!!!"
2616                          "  Check your character encoding."))
2617       (message "%s" tidy-message))))
2618
2619 (defun tidy-after-ediff ()
2620   (run-with-idle-timer 0 nil 'remove-hook 'ediff-quit-hook 'tidy-after-ediff)
2621   ;;(lwarn 't :warning "cb=%s, %s, %s" (current-buffer) ediff-buffer-A ediff-buffer-B)
2622   (let ((sw (selected-window))
2623         nw)
2624     (select-window ediff-window-B)
2625     (setq nw (split-window))
2626     (set-window-buffer nw (get-buffer-create tidy-control-buffer-name))
2627     (select-window sw))
2628   nil)
2629
2630 (defun tidy-ediff-buffers (buffer-a buffer-b &optional startup-hooks job-name)
2631   (add-hook 'ediff-quit-hook 'tidy-after-ediff)
2632   (ediff-buffers buffer-a buffer-b startup-hooks job-name))
2633
2634 ;; http://sf.net/tracker/index.php?func=detail&aid=1425219&group_id=27659&atid=390963
2635 (defun tidy-remove-ctrl-m (buffer)
2636   (with-current-buffer buffer
2637     (goto-char (point-min))
2638     (let ((control-m (char-to-string ?\r)))
2639       (while (search-forward control-m nil t)
2640         (replace-match "" nil t)))))
2641
2642 (defvar tidy-html-files-re "\.x?html?$")
2643 (defun tidy-is-html-file (filename)
2644   (string-match tidy-html-files-re filename))
2645
2646 (defun tidy-contains (dir file)
2647   (let ((d (file-name-as-directory dir)))
2648     (when (< (length d) (length file))
2649       (string= d (substring file 0 (length d))))))
2650
2651
2652
2653 (defvar tidy-tree-files nil)
2654 (defun tidy-tree-next ()
2655   (let ((next-file (car tidy-tree-files))
2656         file-buffer
2657         ;;(tidy-use-ediff nil)
2658         )
2659     (if (not next-file)
2660         ;;(setq tidy-batch-buffer nil)
2661         nil
2662       (setq tidy-tree-files (cdr tidy-tree-files))
2663       (if (file-directory-p next-file)
2664           (error "Uh?")
2665         (tidy-batch next-file)))))
2666
2667 (defun tidy-tree (root)
2668   "Run Tidy on all files in the directory ROOT.
2669 The files are first opened in Emacs and then `tidy-buffer' is
2670 called."
2671   (interactive "DDirectory tree: ")
2672   (unless (file-directory-p root)
2673     (error "tidy-tree called with non-directory arg: %s" root))
2674   (setq tidy-tree-files (html-site-get-sub-files root html-site-files-re))
2675   (dolist (f tidy-tree-files)
2676     (let ((b (get-file-buffer f)))
2677       (when (and b
2678                  (buffer-modified-p b))
2679         (unless
2680             (y-or-n-p (format "Modified buffer %s must be saved. Save it and continue? "
2681                               (buffer-name b)))
2682           (error "Modified buffers prevent run with Tidy"))
2683         (with-current-buffer b
2684           (basic-save-buffer)))))
2685   (setq tidy-batch-last-file nil)
2686   (tidy-tree-next))
2687
2688 (defun tidy-html-site ()
2689   "Tidy the whole tree in the current site."
2690   ;; Fix-me: document html-site better.
2691   (interactive)
2692   (unless (featurep 'html-site)
2693     (error "html-site is not loaded"))
2694   (html-site-current-ensure-site-defined)
2695   (tidy-tree (html-site-current-site-dir)))
2696
2697 (defun tidy-batch-sentinel (process event)
2698   (with-current-buffer (process-buffer process)
2699     (let ((inhibit-read-only t))
2700       (insert "PROCESS-EVENT: " event "\n")))
2701   (when (eq (process-status process) 'exit)
2702     (when tidy-batch-last-file
2703       (let ((b (get-file-buffer tidy-batch-last-file)))
2704         (when b
2705           (with-current-buffer b
2706             (save-excursion
2707               (widen)
2708               (let ((old (buffer-substring-no-properties (point-min) (point-max)))
2709                     (new (with-temp-buffer
2710                            ;;(insert-file tidy-batch-last-file)
2711                            (insert-file-contents tidy-batch-last-file)
2712                            (buffer-substring-no-properties (point-min) (point-max))))
2713                     )
2714                 (unless (string= old new)
2715                   (erase-buffer)
2716                   (insert new))))))))
2717     (tidy-tree-next)))
2718
2719 (defun tidy-batch-output-filter (proc string)
2720   (display-buffer (process-buffer proc))
2721   (with-current-buffer (process-buffer proc)
2722     (let ((moving (= (point) (process-mark proc))))
2723       (save-excursion
2724         ;; Insert the text, advancing the process marker.
2725         (goto-char (process-mark proc))
2726         (let ((inhibit-read-only t))
2727           ;; http://sf.net/tracker/index.php?func=detail&aid=1425219&group_id=27659&atid=390963
2728           (setq string (replace-regexp-in-string "\r$" "" string))
2729           (insert string))
2730         (set-marker (process-mark proc) (point)))
2731       (if moving (goto-char (process-mark proc))))))
2732
2733 (defun tidy-batch (filename)
2734   (interactive (list buffer-file-name)) ;; For testing
2735   (setq tidy-batch-last-file filename)
2736   (let* (;;(filename buffer-file-name)
2737          (config-file (tidy-temp-config-file))
2738          (command (list tidy-shell-program
2739                         ;; load configuration file first so that
2740                         ;; options are overridden by command line
2741
2742                         "-config" config-file
2743                         ;;"--error-file" error-file
2744                         "--write-back" "yes"
2745                         ;;"--show-body-only" "no"
2746                         "--gnu-emacs" "yes"
2747                         "--gnu-emacs-file" filename
2748                         filename
2749                         ))
2750          (procbuf (noshell-procbuf-setup "subprocess for Tidy"))
2751          (start (with-current-buffer procbuf (point)))
2752          proc
2753          ;; This does not work at the moment (2006-05-16):
2754          (coding-system-for-read 'undecided-dos)
2755          (coding-system-for-write 'undecided-dos)
2756          )
2757     ;;(setq tidy-batch-buffer procbuf)
2758     (tidy-save-settings config-file)
2759     (unwind-protect
2760           (setq proc (apply 'noshell-procbuf-run procbuf command))
2761       (with-current-buffer procbuf
2762         (set-process-sentinel proc 'tidy-batch-sentinel)
2763         ;;(set-process-coding-system 'dos)
2764         (set-process-filter proc 'tidy-batch-output-filter)
2765         (let ((win (get-buffer-window procbuf)))
2766           (when win
2767             (set-window-point win (point-max))
2768             ))
2769         ))))
2770
2771 ;;;}}} +
2772
2773 ;;;}}}
2774
2775 ;; (with-temp-buffer
2776 ;;   (tidy-parse-config-file))
2777
2778 (defun wab-compilation-button-at (pos)
2779   (let ((old (point))
2780         ret)
2781     (goto-char pos)
2782     (setq ret (eq 'compilation-button-map
2783                   (get-char-property pos 'keymap)))
2784     (goto-char old)
2785     ret))
2786
2787 (defun wab-click (&optional event)
2788   "Do the action that is tighed to the button."
2789   (interactive (list last-input-event))
2790   (if event (posn-set-point (event-end event)))
2791   (let ((button (get-char-property (point) 'button))
2792         done)
2793     (condition-case nil
2794         (progn
2795           (compile-goto-error)
2796           (setq done t))
2797       (error nil))
2798     (unless done
2799       (when button
2800         (if (widget-at)
2801             ;;(widget-apply-action button event)
2802             (widget-apply-action button)
2803           (push-button))))))
2804
2805 (defvar wab-errors-supress
2806   '("No more buttons"
2807     "Moved past last error"
2808     "No buttons or fields found"
2809     "Moved back before first error"
2810     ))
2811
2812 (defun wab-fb-errmsg (err)
2813   (let ((s (error-message-string err)))
2814     (unless (member s wab-errors-supress)
2815       (message "%s" err)
2816       (signal (car err) (cdr err)))))
2817
2818 (defun wab-fb-helper (forward function check-at args)
2819   (let (ret
2820         (len (1+ (- (point-max) (point-min))))
2821         (old (point)))
2822     (condition-case err
2823         (progn
2824           (apply function args)
2825           (when (funcall check-at (point))
2826             (setq ret (point))))
2827       (error (wab-fb-errmsg err)))
2828     (unless ret
2829       (if forward
2830           (goto-char (point-min))
2831         (goto-char (point-max)))
2832       (condition-case err
2833           (progn
2834             (apply function args)
2835             (when (funcall check-at (point))
2836               (setq ret (point))))
2837         (error (message "%s" (error-message-string err)))))
2838     (goto-char old)
2839     (when (and ret (= ret old))
2840       (if forward
2841           (setq ret (+ ret len))
2842         (setq ret (- ret len))))
2843     ;;(message "function=%s, ret=%s" function ret)
2844     ret))
2845 (defvar wab-button-list
2846   '(
2847     (compilation-previous-error (1) compilation-next-error (1) wab-compilation-button-at)
2848     (backward-button (1) forward-button (1) button-at)
2849     (widget-backward (1) widget-forward (1) widget-at)
2850     ))
2851 (defun wab-fb (forward)
2852   ;;(message "==================")
2853   (let* ((pos-list (mapcar (lambda (p)
2854                              (let ((prev-fun (nth 0 p))
2855                                    (prev-arg (nth 1 p))
2856                                    (next-fun (nth 2 p))
2857                                    (next-arg (nth 3 p))
2858                                    (test-fun (nth 4 p)))
2859                                (if forward
2860                                    (wab-fb-helper t next-fun test-fun next-arg)
2861                                  (wab-fb-helper nil prev-fun test-fun prev-arg))))
2862                            wab-button-list))
2863          (len (1+ (- (point-max) (point-min))))
2864          (defpos (if forward (* 2 len) (- len)))
2865          (newpos defpos)
2866          (here (point)))
2867     ;;(message "pos-list=%s" pos-list)
2868     (mapc (lambda (p) (when (and p
2869                                 (if forward
2870                                     (progn
2871                                       (when (< p here)
2872                                         (setq p (+ p len)))
2873                                       (< p newpos))
2874                                   (when (> p here)
2875                                     (setq p (- p len)))
2876                                   (> p newpos)))
2877                        (setq newpos p)))
2878           pos-list)
2879     (if (= newpos defpos)
2880         (setq newpos here)
2881       (setq newpos (mod newpos len)))
2882     (goto-char newpos)))
2883
2884 (defun wab-backward ()
2885   "Go to next button or error link."
2886   (interactive)
2887   (wab-fb nil))
2888
2889 (defun wab-forward ()
2890   "Go to previous button or error link."
2891   (interactive)
2892   (wab-fb t))
2893
2894 (define-compilation-mode wab-compilation-mode "WAB Compilation"
2895   "Mode for tidy control buffer."
2896   )
2897 (define-key wab-compilation-mode-map [tab]         'wab-forward)
2898 (define-key wab-compilation-mode-map [(shift tab)] 'wab-backward)
2899 (define-key wab-compilation-mode-map [backtab]     'wab-backward)
2900 (define-key wab-compilation-mode-map "\r"          'wab-click)
2901 (define-key wab-compilation-mode-map [mouse-1]     'wab-click)
2902 (define-key wab-compilation-mode-map [mouse-2]     'wab-click)
2903
2904 (defvar tidy-menu-mode-map
2905   (let ((map (make-sparse-keymap)))
2906     ;; This did not work:
2907     ;;(define-key map [menu-bar tidy-menu] (list 'menu-item "Tidy" '(lambda () (interactive) tidy-menu-symbol)))
2908     map))
2909
2910 (define-minor-mode tidy-menu-mode
2911   "This mode just adds Tidy to the menu bar."
2912   nil
2913   nil
2914   nil
2915   (when tidy-menu-mode
2916     (define-key tidy-menu-mode-map [menu-bar tidy-menu]
2917       (list 'menu-item "Tidy" tidy-menu-symbol))))
2918
2919 (provide 'tidy-xhtml)
2920
2921 ;;; tidy-xhtml.el ends here