blob: 98d452d5282c19b978b7fee4c4f027201577e0b3 [file] [log] [blame]
Fred Drakebfc18bd2002-05-03 04:50:51 +00001;;; py2texi.el -- Conversion of Python LaTeX documentation to Texinfo
2
3;; Copyright (C) 1998, 1999, 2001, 2002 Milan Zamazal
4
5;; Author: Milan Zamazal <pdm@zamazal.org>
6;; Version: $Id$
7;; Keywords: python
8
9;; COPYRIGHT NOTICE
10;;
11;; This program is free software; you can redistribute it and/or modify it
12;; under the terms of the GNU General Public License as published by the Free
13;; Software Foundation; either version 2, or (at your option) any later
14;; version.
15;;
16;; This program is distributed in the hope that it will be useful, but
17;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19;; for more details.
20;;
21;; You can find the GNU General Public License at
22;; http://www.gnu.org/copyleft/gpl.html
23;; or you can write to the Free Software Foundation, Inc., 59 Temple Place,
24;; Suite 330, Boston, MA 02111-1307, USA.
25
26;;; Commentary:
27
28;; This is a Q&D hack for conversion of Python manuals to on-line help format.
29;; I desperately needed usable online documenta for Python, so I wrote this.
30;; The result code is ugly and need not contain complete information from
31;; Python manuals. I apologize for my ignorance, especially ignorance to
32;; python.sty. Improvements of this convertor are welcomed.
33
34;; How to use it:
35;; Load this file and apply `M-x py2texi'. You will be asked for name of a
36;; file to be converted.
37
38;; Where to find it:
39;; New versions of this code might be found at
40;; http://www.zamazal.org/software/python/py2texi/ .
41
42;;; Code:
43
44
45(require 'texinfo)
46(eval-when-compile
47 (require 'cl))
48
49
50(defvar py2texi-python-version "2.2"
51 "What to substitute for the \\version macro.")
52
53(defvar py2texi-python-short-version
54 (progn
55 (string-match "[0-9]+\\.[0-9]+" py2texi-python-version)
56 (match-string 0 py2texi-python-version))
57 "Short version number, usually set by the LaTeX commands.")
58
59(defvar py2texi-stop-on-problems nil
60 "*If non-nil, stop when you encouter soft problem.")
61
62(defconst py2texi-environments
63 '(("abstract" 0 "@quotation" "@end quotation\n")
64 ("center" 0 "" "")
65 ("cfuncdesc" 3
66 (progn (setq findex t)
67 "\n@table @code\n@item \\1 \\2(\\3)\n@findex \\2\n")
68 "@end table")
69 ("classdesc" 2
70 (progn (setq obindex t)
71 "\n@table @code\n@item \\1(\\2)\n@obindex \\1\n")
72 "@end table")
73 ("classdesc*" 1
74 (progn (setq obindex t)
75 "\n@table @code\n@item \\1\n@obindex \\1\n")
76 "@end table")
77 ("csimplemacrodesc" 1
78 (progn (setq cindex t)
79 "\n@table @code\n@item \\1\n@cindex \\1\n")
80 "@end table")
81 ("ctypedesc" 1
82 (progn (setq cindex t)
83 "\n@table @code\n@item \\1\n@cindex \\1\n")
84 "@end table")
85 ("cvardesc" 2
86 (progn (setq findex t)
87 "\n@table @code\n@item \\1 \\2\n@findex \\2\n")
88 "@end table")
89 ("datadesc" 1
90 (progn (setq findex t)
91 "\n@table @code\n@item \\1\n@findex \\1\n")
92 "@end table")
93 ("datadescni" 1 "\n@table @code\n@item \\1\n" "@end table")
94 ("definitions" 0 "@table @dfn" "@end table\n")
95 ("description" 0 "@table @samp" "@end table\n")
96 ("displaymath" 0 "" "")
97 ("document" 0
98 (concat "@defcodeindex mo\n"
99 "@defcodeindex ob\n"
100 "@titlepage\n"
101 (format "@title " title "\n")
102 (format "@author " author "\n")
103 "@page\n"
104 author-address
105 "@end titlepage\n"
106 "@node Top, , , (dir)\n")
107 (concat "@indices\n"
108 "@contents\n"
109 "@bye\n"))
110 ("enumerate" 0 "@enumerate" "@end enumerate")
111 ("excdesc" 1
112 (progn (setq obindex t)
113 "\n@table @code\n@item \\1\n@obindex \\1\n")
114 "@end table")
115 ("excclassdesc" 2
116 (progn (setq obindex t)
117 "\n@table @code\n@item \\1(\\2)\n@obindex \\1\n")
118 "@end table")
119 ("flushleft" 0 "" "")
120 ("fulllineitems" 0 "\n@table @code\n" "@end table")
121 ("funcdesc" 2
122 (progn (setq findex t)
123 "\n@table @code\n@item \\1(\\2)\n@findex \\1\n")
124 "@end table")
125 ("funcdescni" 2 "\n@table @code\n@item \\1(\\2)\n" "@end table")
126 ("itemize" 0 "@itemize @bullet" "@end itemize\n")
127 ("list" 2 "\n@table @code\n" "@end table")
128 ("longtableii" 4 (concat "@multitable @columnfractions .5 .5\n"
129 "@item \\3 @tab \\4\n"
130 "@item ------- @tab ------ \n")
131 "@end multitable\n")
132 ("longtableiii" 5 (concat "@multitable @columnfractions .33 .33 .33\n"
133 "@item \\3 @tab \\4 @tab \\5\n"
134 "@item ------- @tab ------ @tab ------\n")
135 "@end multitable\n")
136 ("memberdesc" 1
137 (progn (setq findex t)
138 "\n@table @code\n@item \\1\n@findex \\1\n")
139 "@end table")
140 ("memberdescni" 1 "\n@table @code\n@item \\1\n" "@end table")
141 ("methoddesc" 2
142 (progn (setq findex t)
143 "\n@table @code\n@item \\1(\\2)\n@findex \\1\n")
144 "@end table")
145 ("methoddescni" 2 "\n@table @code\n@item \\1(\\2)\n" "@end table")
146 ("notice" 0 "@emph{Notice:} " "")
147 ("opcodedesc" 2
148 (progn (setq findex t)
149 "\n@table @code\n@item \\1 \\2\n@findex \\1\n")
150 "@end table")
151 ("productionlist" 0 "\n@table @code\n" "@end table")
152 ("quotation" 0 "@quotation" "@end quotation")
153 ("seealso" 0 "See also:\n@table @emph\n" "@end table")
154 ("seealso*" 0 "@table @emph\n" "@end table")
155 ("sloppypar" 0 "" "")
156 ("small" 0 "" "")
157 ("tableii" 4 (concat "@multitable @columnfractions .5 .5\n"
158 "@item \\3 @tab \\4\n"
159 "@item ------- @tab ------ \n")
160 "@end multitable\n")
161 ("tableiii" 5 (concat "@multitable @columnfractions .33 .33 .33\n"
162 "@item \\3 @tab \\4 @tab \\5\n"
163 "@item ------- @tab ------ @tab ------\n")
164 "@end multitable\n")
165 ("tableiv" 6 (concat
166 "@multitable @columnfractions .25 .25 .25 .25\n"
167 "@item \\3 @tab \\4 @tab \\5 @tab \\6\n"
168 "@item ------- @tab ------- @tab ------- @tab -------\n")
169 "@end multitable\n")
170 ("tablev" 7 (concat
171 "@multitable @columnfractions .20 .20 .20 .20 .20\n"
172 "@item \\3 @tab \\4 @tab \\5 @tab \\6 @tab \\7\n"
173 "@item ------- @tab ------- @tab ------- @tab ------- @tab -------\n")
174 "@end multitable\n"))
175 "Associative list defining substitutions for environments.
176Each list item is of the form (ENVIRONMENT ARGNUM BEGIN END) where:
177- ENVIRONMENT is LaTeX environment name
178- ARGNUM is number of (required) macro arguments
179- BEGIN is substitution for \begin{ENVIRONMENT}
180- END is substitution for \end{ENVIRONMENT}
181Both BEGIN and END are evaled. Moreover, you can reference arguments through
182\N regular expression notation in strings of BEGIN.")
183
184(defconst py2texi-commands
185 '(("ABC" 0 "ABC")
186 ("appendix" 0 (progn (setq appendix t) ""))
187 ("ASCII" 0 "ASCII")
188 ("author" 1 (progn (setq author (match-string 1 string)) ""))
189 ("authoraddress" 1
190 (progn (setq author-address (match-string 1 string)) ""))
191 ("b" 1 "@w{\\1}")
192 ("bf" 0 "@destroy")
193 ("bifuncindex" 1 (progn (setq findex t) "@findex{\\1}"))
194 ("C" 0 "C")
195 ("c" 0 "@,")
196 ("catcode" 0 "")
197 ("cdata" 1 "@code{\\1}")
198 ("centerline" 1 "@center \\1")
199 ("cfunction" 1 "@code{\\1}")
200 ("chapter" 1 (format "@node \\1\n@%s \\1\n"
201 (if appendix "appendix" "chapter")))
202 ("chapter*" 1 "@node \\1\n@unnumbered \\1\n")
203 ("character" 1 "@samp{\\1}")
204 ("citetitle" 1 "@ref{Top,,,\\1}")
205 ("class" 1 "@code{\\1}")
206 ("code" 1 "@code{\\1}")
207 ("command" 1 "@command{\\1}")
208 ("constant" 1 "@code{\\1}")
209 ("copyright" 1 "@copyright{}")
210 ("Cpp" 0 "C++")
211 ("ctype" 1 "@code{\\1}")
212 ("dataline" 1 (progn (setq findex t) "@item \\1\n@findex \\1\n"))
213 ("date" 1 "\\1")
214 ("declaremodule" 2 (progn (setq cindex t) "@label{\\2}@cindex{\\2}"))
215 ("deprecated" 2 "@emph{This is deprecated in Python \\1. \\2}")
216 ("dfn" 1 "@dfn{\\1}")
217 ("documentclass" 1 py2texi-magic)
218 ("e" 0 "@backslash{}")
219 ("else" 0 (concat "@end ifinfo\n@" (setq last-if "iftex")))
220 ("EOF" 0 "@code{EOF}")
221 ("email" 1 "@email{\\1}")
222 ("emph" 1 "@emph{\\1}")
223 ("envvar" 1 "@samp{\\1}")
224 ("exception" 1 "@code{\\1}")
225 ("exindex" 1 (progn (setq obindex t) "@obindex{\\1}"))
226 ("fi" 0 (concat "@end " last-if))
227 ("file" 1 "@file{\\1}")
228 ("filevar" 1 "@file{@var{\\1}}")
229 ("footnote" 1 "@footnote{\\1}")
230 ("frac" 0 "")
231 ("funcline" 2 (progn (setq findex t) "@item \\1 \\2\n@findex \\1"))
232 ("funclineni" 2 "@item \\1 \\2")
233 ("function" 1 "@code{\\1}")
234 ("grammartoken" 1 "@code{\\1}")
235 ("hline" 0 "")
236 ("ifhtml" 0 (concat "@" (setq last-if "ifinfo")))
237 ("iftexi" 0 (concat "@" (setq last-if "ifinfo")))
238 ("index" 1 (progn (setq cindex t) "@cindex{\\1}"))
239 ("indexii" 2 (progn (setq cindex t) "@cindex{\\1 \\2}"))
240 ("indexiii" 3 (progn (setq cindex t) "@cindex{\\1 \\2 \\3}"))
241 ("indexiv" 3 (progn (setq cindex t) "@cindex{\\1 \\2 \\3 \\4}"))
242 ("infinity" 0 "@emph{infinity}")
243 ("it" 0 "@destroy")
244 ("kbd" 1 "@key{\\1}")
245 ("keyword" 1 "@code{\\1}")
246 ("kwindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
247 ("label" 1 "@label{\\1}")
248 ("Large" 0 "")
249 ("LaTeX" 0 "La@TeX{}")
250 ("large" 0 "")
251 ("ldots" 0 "@dots{}")
252 ("leftline" 1 "\\1")
253 ("lineii" 2 "@item \\1 @tab \\2")
254 ("lineiii" 3 "@item \\1 @tab \\2 @tab \\3")
255 ("lineiv" 4 "@item \\1 @tab \\2 @tab \\3 @tab \\4")
256 ("linev" 5 "@item \\1 @tab \\2 @tab \\3 @tab \\4 @tab \\5")
257 ("localmoduletable" 0 "")
258 ("longprogramopt" 1 "@option{--\\1}")
259 ("mailheader" 1 "@code{\\1}")
260 ("makeindex" 0 "")
261 ("makemodindex" 0 "")
262 ("maketitle" 0 (concat "@top " title "\n"))
263 ("makevar" 1 "@code{\\1}")
264 ("manpage" 2 "@samp{\\1(\\2)}")
265 ("mbox" 1 "@w{\\1}")
266 ("member" 1 "@code{\\1}")
267 ("memberline" 1 "@item \\1\n@findex \\1\n")
268 ("menuselection" 1 "@samp{\\1}")
269 ("method" 1 "@code{\\1}")
270 ("methodline" 2 (progn (setq moindex t) "@item \\1(\\2)\n@moindex \\1\n"))
271 ("methodlineni" 2 "@item \\1(\\2)\n")
272 ("mimetype" 1 "@samp{\\1}")
273 ("module" 1 "@samp{\\1}")
274 ("moduleauthor" 2 "This module was written by \\1 @email{\\2}.@*")
275 ("modulesynopsis" 1 "\\1")
276 ("moreargs" 0 "@dots{}")
277 ("n" 0 "@backslash{}n")
278 ("newcommand" 2 "")
279 ("newsgroup" 1 "@samp{\\1}")
280 ("nodename" 1
281 (save-excursion
282 (save-match-data
283 (re-search-backward "^@node "))
284 (delete-region (point) (save-excursion (end-of-line) (point)))
285 (insert "@node " (match-string 1 string))
286 ""))
287 ("noindent" 0 "@noindent ")
288 ("note" 1 "@emph{Note:} \\1")
289 ("NULL" 0 "@code{NULL}")
290 ("obindex" 1 (progn (setq obindex t) "@obindex{\\1}"))
291 ("opindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
292 ("option" 1 "@option{\\1}")
293 ("optional" 1 "[\\1]")
294 ("pep" 1 (progn (setq cindex t) "PEP@ \\1@cindex PEP \\1\n"))
295 ("pi" 0 "pi")
296 ("platform" 1 "")
297 ("plusminus" 0 "+-")
298 ("POSIX" 0 "POSIX")
299 ("production" 2 "@item \\1 \\2")
300 ("program" 1 "@command{\\1}")
301 ("programopt" 1 "@option{\\1}")
302 ("protect" 0 "")
303 ("pytype" 1 "@code{\\1}")
304 ("ref" 1 "@ref{\\1}")
305 ("refbimodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
306 ("refmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
307 ("refmodule" 1 "@samp{\\1}")
308 ("refstmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
309 ("regexp" 1 "\"\\1\"")
310 ("release" 1
311 (progn (setq py2texi-python-version (match-string 1 string)) ""))
312 ("renewcommand" 2 "")
313 ("rfc" 1 (progn (setq cindex t) "RFC@ \\1@cindex RFC \\1\n"))
314 ("rm" 0 "@destroy")
315 ("samp" 1 "@samp{\\1}")
316 ("section" 1 (let ((str (match-string 1 string)))
317 (save-match-data
318 (if (string-match "\\(.*\\)[ \t\n]*---[ \t\n]*\\(.*\\)"
319 str)
320 (format
321 "@node %s\n@section %s\n"
322 (py2texi-backslash-quote (match-string 1 str))
323 (py2texi-backslash-quote (match-string 2 str)))
324 "@node \\1\n@section \\1\n"))))
325 ("sectionauthor" 2
326 "\nThis manual section was written by \\1 @email{\\2}.@*")
327 ("seemodule" 2 "@ref{\\1} \\2")
328 ("seepep" 3 "\n@table @strong\n@item PEP\\1 \\2\n\\3\n@end table\n")
329 ("seerfc" 3 "\n@table @strong\n@item RFC\\1 \\2\n\\3\n@end table\n")
330 ("seetext" 1 "\\1")
331 ("seetitle" 1 "@cite{\\1}")
332 ("seeurl" 2 "\n@table @url\n@item \\1\n\\2\n@end table\n")
333 ("setindexsubitem" 1 (progn (setq cindex t) "@cindex \\1"))
334 ("setreleaseinfo" 1 (progn (setq py2texi-releaseinfo "")))
335 ("setshortversion" 1
336 (progn (setq py2texi-python-short-version (match-string 1 string)) ""))
337 ("shortversion" 0 py2texi-python-short-version)
338 ("sqrt" 0 "")
339 ("stindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
340 ("stmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
341 ("strong" 1 "@strong{\\1}")
342 ("sub" 0 "/")
343 ("subsection" 1 "@node \\1\n@subsection \\1\n")
344 ("subsubsection" 1 "@node \\1\n@subsubsection \\1\n")
345 ("sum" 0 "")
346 ("tableofcontents" 0 "")
347 ("term" 1 "@item \\1")
348 ("textasciitilde" 0 "~")
349 ("textasciicircum" 0 "^")
350 ("textbackslash" 0 "@backslash{}")
351 ("textrm" 1 "\\1")
352 ("texttt" 1 "@code{\\1}")
353 ("textunderscore" 0 "_")
354 ("title" 1 (progn (setq title (match-string 1 string)) "@settitle \\1"))
355 ("today" 0 "@today{}")
356 ("token" 1 "@code{\\1}")
357 ("tt" 0 "@destroy")
358 ("ttindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
359 ("u" 0 "@backslash{}u")
360 ("ulink" 2 "\\1")
361 ("UNIX" 0 "UNIX")
362 ("unspecified" 0 "@dots{}")
363 ("url" 1 "@url{\\1}")
364 ("usepackage" 1 "")
365 ("var" 1 "@var{\\1}")
366 ("verbatiminput" 1 "@code{\\1}")
367 ("version" 0 py2texi-python-version)
368 ("versionadded" 1 "@emph{Added in Python version \\1}")
369 ("versionchanged" 1 "@emph{Changed in Python version \\1}")
370 ("vskip" 1 "")
371 ("vspace" 1 "")
372 ("warning" 1 "@emph{\\1}")
373 ("withsubitem" 2 "\\2")
374 ("XXX" 1 "@strong{\\1}"))
375 "Associative list of command substitutions.
376Each list item is of the form (COMMAND ARGNUM SUBSTITUTION) where:
377- COMMAND is LaTeX command name
378- ARGNUM is number of (required) command arguments
379- SUBSTITUTION substitution for the command. It is evaled and you can
380 reference command arguments through the \\N regexp notation in strings.")
381
382(defvar py2texi-magic "@documentclass\n"
383 "\"Magic\" string for auxiliary insertion at the beginning of document.")
384
385(defvar py2texi-dirs '("./" "../texinputs/")
386 "Where to search LaTeX input files.")
387
388(defvar py2texi-buffer "*py2texi*"
389 "The name of a buffer where Texinfo is generated.")
390
391(defconst py2texi-xemacs (string-match "^XEmacs" (emacs-version))
392 "Running under XEmacs?")
393
394
395(defmacro py2texi-search (regexp &rest body)
396 `(progn
397 (goto-char (point-min))
398 (while (re-search-forward ,regexp nil t)
399 ,@body)))
400
401(defmacro py2texi-search-safe (regexp &rest body)
402 `(py2texi-search ,regexp
403 (unless (py2texi-protected)
404 ,@body)))
405
406
407(defun py2texi-message (message)
408 "Report message and stop if `py2texi-stop-on-problems' is non-nil."
409 (if py2texi-stop-on-problems
410 (error message)
411 (message message)))
412
413
414(defun py2texi-backslash-quote (string)
415 "Double backslahes in STRING."
416 (let ((i 0))
417 (save-match-data
418 (while (setq i (string-match "\\\\" string i))
419 (setq string (replace-match "\\\\\\\\" t nil string))
420 (setq i (+ i 2))))
421 string))
422
423
424(defun py2texi (file)
425 "Convert Python LaTeX documentation FILE to Texinfo."
426 (interactive "fFile to convert: ")
427 (switch-to-buffer (get-buffer-create py2texi-buffer))
428 (erase-buffer)
429 (insert-file file)
430 (let ((case-fold-search nil)
431 (title "")
432 (author "")
433 (author-address "")
434 (appendix nil)
435 (findex nil)
436 (obindex nil)
437 (cindex nil)
438 (moindex nil)
439 last-if)
440 (py2texi-process-verbatims)
441 (py2texi-process-comments)
442 (py2texi-process-includes)
443 (py2texi-process-funnyas)
444 (py2texi-process-environments)
445 (py2texi-process-commands)
446 (py2texi-fix-indentation)
447 (py2texi-fix-nodes)
448 (py2texi-fix-references)
449 (py2texi-fix-indices)
450 (py2texi-process-simple-commands)
451 (py2texi-fix-fonts)
452 (py2texi-fix-braces)
453 (py2texi-fix-backslashes)
454 (py2texi-destroy-empties)
455 (py2texi-fix-newlines)
456 (py2texi-adjust-level))
457 (let* ((filename (concat "./"
458 (file-name-nondirectory file)
459 (if (string-match "\\.tex$" file) "i" ".texi")))
460 (infofilename (py2texi-info-file-name filename)))
461 (goto-char (point-min))
462 (when (looking-at py2texi-magic)
463 (delete-region (point) (progn (beginning-of-line 2) (point)))
464 (insert "\\input texinfo @c -*-texinfo-*-\n")
465 (insert "@setfilename " (file-name-nondirectory infofilename)))
466 (when (re-search-forward "@chapter" nil t)
467 (texinfo-all-menus-update t))
468 (goto-char (point-min))
469 (write-file filename)
470 (message (format "You can apply `makeinfo %s' now." filename))))
471
472
473(defun py2texi-info-file-name (filename)
474 "Generate name of info file from original file name FILENAME."
475 (setq filename (expand-file-name filename))
476 (let ((directory (file-name-directory filename))
477 (basename (file-name-nondirectory filename)))
478 (concat directory "python-"
479 (substring basename 0 (- (length basename) 4)) "info")))
480
481
482(defun py2texi-process-verbatims ()
483 "Process and protect verbatim environments."
484 (let (delimiter
485 beg
486 end)
487 (py2texi-search-safe "\\\\begin{\\(verbatim\\|displaymath\\)}"
488 (replace-match "@example")
489 (setq beg (copy-marker (point) nil))
490 (re-search-forward "\\\\end{\\(verbatim\\|displaymath\\)}")
491 (setq end (copy-marker (match-beginning 0) nil))
492 (replace-match "@end example")
493 (py2texi-texinfo-escape beg end)
494 (put-text-property (- beg (length "@example"))
495 (+ end (length "@end example"))
496 'py2texi-protected t))
497 (py2texi-search-safe "\\\\verb\\([^a-z]\\)"
498 (setq delimiter (match-string 1))
499 (replace-match "@code{")
500 (setq beg (copy-marker (point) nil))
501 (re-search-forward (regexp-quote delimiter))
502 (setq end (copy-marker (match-beginning 0) nil))
503 (replace-match "}")
504 (put-text-property (- beg (length "@code{")) (+ end (length "}"))
505 'py2texi-protected t)
506 (py2texi-texinfo-escape beg end))))
507
508
509(defun py2texi-process-comments ()
510 "Remove comments."
511 (let (point)
512 (py2texi-search-safe "%"
513 (setq point (point))
514 (when (save-excursion
515 (re-search-backward "\\(^\\|[^\\]\\(\\\\\\\\\\)*\\)%\\=" nil t))
516 (delete-region (1- point)
517 (save-excursion (beginning-of-line 2) (point)))))))
518
519
520(defun py2texi-process-includes ()
521 "Include LaTeX input files.
522Do not include .ind files."
523 (let ((path (file-name-directory file))
524 filename
525 dirs
526 includefile)
527 (py2texi-search-safe "\\\\input{\\([^}]+\\)}"
528 (setq filename (match-string 1))
529 (unless (save-match-data (string-match "\\.tex$" filename))
530 (setq filename (concat filename ".tex")))
531 (setq includefile (save-match-data
532 (string-match "\\.ind\\.tex$" filename)))
533 (setq dirs py2texi-dirs)
534 (while (and (not includefile) dirs)
535 (setq includefile (concat path (car dirs) filename))
536 (unless (file-exists-p includefile)
537 (setq includefile nil)
538 (setq dirs (cdr dirs))))
539 (if includefile
540 (save-restriction
541 (narrow-to-region (match-beginning 0) (match-end 0))
542 (delete-region (point-min) (point-max))
543 (when (stringp includefile)
544 (insert-file-contents includefile)
545 (goto-char (point-min))
546 (insert "\n")
547 (py2texi-process-verbatims)
548 (py2texi-process-comments)
549 (py2texi-process-includes)))
550 (replace-match (format "\\\\emph{Included file %s}" filename))
551 (py2texi-message (format "Input file %s not found" filename))))))
552
553
554(defun py2texi-process-funnyas ()
555 "Convert @s."
556 (py2texi-search-safe "@"
557 (replace-match "@@")))
558
559
560(defun py2texi-process-environments ()
561 "Process LaTeX environments."
562 (let ((stack ())
563 kind
564 environment
565 parameter
566 arguments
567 n
568 string
569 description)
570 (py2texi-search-safe (concat "\\\\\\(begin\\|end\\|item\\)"
571 "\\({\\([^}]*\\)}\\|[[]\\([^]]*\\)[]]\\|\\)")
572 (setq kind (match-string 1)
573 environment (match-string 3)
574 parameter (match-string 4))
575 (replace-match "")
576 (cond
577 ((string= kind "begin")
578 (setq description (assoc environment py2texi-environments))
579 (if description
580 (progn
581 (setq n (cadr description))
582 (setq description (cddr description))
583 (setq string (py2texi-tex-arguments n))
584 (string-match (py2texi-regexp n) string)
585 ; incorrect but sufficient
586 (insert (replace-match (eval (car description))
587 t nil string))
588 (setq stack (cons (cadr description) stack)))
589 (py2texi-message (format "Unknown environment: %s" environment))
590 (setq stack (cons "" stack))))
591 ((string= kind "end")
592 (insert (eval (car stack)))
593 (setq stack (cdr stack)))
594 ((string= kind "item")
595 (insert "\n@item " (or parameter "") "\n"))))
596 (when stack
597 (py2texi-message (format "Unclosed environment: %s" (car stack))))))
598
599
600(defun py2texi-process-commands ()
601 "Process LaTeX commands."
602 (let (done
603 command
604 command-info
605 string
606 n)
607 (while (not done)
608 (setq done t)
609 (py2texi-search-safe "\\\\\\([a-zA-Z*]+\\)\\(\\[[^]]*\\]\\)?"
610 (setq command (match-string 1))
611 (setq command-info (assoc command py2texi-commands))
612 (if command-info
613 (progn
614 (setq done nil)
615 (replace-match "")
616 (setq command-info (cdr command-info))
617 (setq n (car command-info))
618 (setq string (py2texi-tex-arguments n))
619 (string-match (py2texi-regexp n) string)
620 ; incorrect but sufficient
621 (insert (replace-match (eval (cadr command-info))
622 t nil string)))
623 (py2texi-message (format "Unknown command: %s (not processed)"
624 command)))))))
625
626
627(defun py2texi-argument-pattern (count)
628 (let ((filler "\\(?:[^{}]\\|\\\\{\\|\\\\}\\)*"))
629 (if (<= count 0)
630 filler
631 (concat filler "\\(?:{"
632 (py2texi-argument-pattern (1- count))
633 "}" filler "\\)*" filler))))
634(defconst py2texi-tex-argument
635 (concat
636 "{\\("
637 (py2texi-argument-pattern 10) ;really at least 10!
638 "\\)}[ \t%@c\n]*")
639 "Regexp describing LaTeX command argument including argument separators.")
640
641
642(defun py2texi-regexp (n)
643 "Make regexp matching N LaTeX command arguments."
644 (if (= n 0)
645 ""
646 (let ((regexp "^[^{]*"))
647 (while (> n 0)
648 (setq regexp (concat regexp py2texi-tex-argument))
649 (setq n (1- n)))
650 regexp)))
651
652
653(defun py2texi-tex-arguments (n)
654 "Remove N LaTeX command arguments and return them as a string."
655 (let ((point (point))
656 (i 0)
657 result
658 match)
659 (if (= n 0)
660 (progn
661 (when (re-search-forward "\\=\\({}\\| *\\)" nil t)
662 (replace-match ""))
663 "")
664 (while (> n 0)
665 (unless (re-search-forward
666 "\\(\\=\\|[^\\\\]\\)\\(\\\\\\\\\\)*\\([{}]\\)" nil t)
667 (debug))
668 (if (string= (match-string 3) "{")
669 (setq i (1+ i))
670 (setq i (1- i))
671 (when (<= i 0)
672 (setq n (1- n)))))
673 (setq result (buffer-substring-no-properties point (point)))
674 (while (string-match "\n[ \t]*" result)
675 (setq result (replace-match " " t nil result)))
676 (delete-region point (point))
677 result)))
678
679
680(defun py2texi-process-simple-commands ()
681 "Replace single character LaTeX commands."
682 (let (char)
683 (py2texi-search-safe "\\\\\\([^a-z]\\)"
684 (setq char (match-string 1))
685 (replace-match (format "%s%s"
686 (if (or (string= char "{")
687 (string= char "}")
688 (string= char " "))
689 "@"
690 "")
691 (if (string= char "\\")
692 "\\\\"
693 char))))))
694
695
696(defun py2texi-fix-indentation ()
697 "Remove white space at the beginning of lines."
698 (py2texi-search-safe "^[ \t]+"
699 (replace-match "")))
700
701
702(defun py2texi-fix-nodes ()
703 "Remove unwanted characters from nodes and make nodes unique."
704 (let ((nodes (make-hash-table :test 'equal))
705 id
706 counter
707 string
708 label)
709 (py2texi-search "^@node +\\(.*\\)$"
710 (setq string (match-string 1))
711 (if py2texi-xemacs
712 (replace-match "@node " t)
713 (replace-match "" t nil nil 1))
714 (when (string-match "@label{[^}]*}" string)
715 (setq label (match-string 0 string))
716 (setq string (replace-match "" t nil string)))
717 (while (string-match "@[a-zA-Z]+\\|[{}():]\\|``\\|''" string)
718 (setq string (replace-match "" t nil string)))
719 (while (string-match " -- " string)
720 (setq string (replace-match " - " t nil string)))
721 (when (string-match " +$" string)
722 (setq string (replace-match "" t nil string)))
723 (when (string-match "^\\(Built-in\\|Standard\\) Module \\|The " string)
724 (setq string (replace-match "" t nil string)))
725 (string-match "^[^,]+" string)
726 (setq id (match-string 0 string))
727 (setq counter (gethash id nodes))
728 (if counter
729 (progn
730 (setq counter (1+ counter))
731 (setq string (replace-match (format "\\& %d" counter)
732 t nil string)))
733 (setq counter 1))
734 (setf (gethash id nodes) counter)
735 (insert string)
736 (when label
737 (beginning-of-line 3)
738 (insert label "\n")))))
739
740
741(defun py2texi-fix-references ()
742 "Process labels and make references to point to appropriate nodes."
743 (let ((labels ())
744 node)
745 (py2texi-search-safe "@label{\\([^}]*\\)}"
746 (setq node (save-excursion
747 (save-match-data
748 (and (re-search-backward "@node +\\([^,\n]+\\)" nil t)
749 (match-string 1)))))
750 (when node
751 (setq labels (cons (cons (match-string 1) node) labels)))
752 (replace-match ""))
753 (py2texi-search-safe "@ref{\\([^}]*\\)}"
754 (setq node (assoc (match-string 1) labels))
755 (replace-match "")
756 (when node
757 (insert (format "@ref{%s}" (cdr node)))))))
758
759
760(defun py2texi-fix-indices ()
761 "Remove unwanted characters from @*index commands and create final indices."
762 (py2texi-search-safe "@..?index\\>[^\n]*\\(\\)\n"
763 (replace-match "" t nil nil 1))
764 (py2texi-search-safe "@..?index\\>[^\n]*\\(\\)"
765 (replace-match "\n" t nil nil 1))
766 (py2texi-search-safe "@..?index\\({\\)\\([^}]+\\)\\(}+\\)"
767 (replace-match " " t nil nil 1)
768 (replace-match "" t nil nil 3)
769 (let ((string (match-string 2)))
770 (save-match-data
771 (while (string-match "@[a-z]+{" string)
772 (setq string (replace-match "" nil nil string)))
773 (while (string-match "{" string)
774 (setq string (replace-match "" nil nil string))))
775 (replace-match string t t nil 2)))
776 (py2texi-search-safe "@..?index\\>.*\\([{}]\\|@[a-z]*\\)"
777 (replace-match "" t nil nil 1)
778 (goto-char (match-beginning 0)))
779 (py2texi-search-safe "[^\n]\\(\\)@..?index\\>"
780 (replace-match "\n" t nil nil 1))
781 (goto-char (point-max))
782 (re-search-backward "@indices")
783 (replace-match "")
784 (insert (if moindex
785 (concat "@node Module Index\n"
786 "@unnumbered Module Index\n"
787 "@printindex mo\n")
788 "")
789 (if obindex
790 (concat "@node Class-Exception-Object Index\n"
791 "@unnumbered Class, Exception, and Object Index\n"
792 "@printindex ob\n")
793 "")
794 (if findex
795 (concat "@node Function-Method-Variable Index\n"
796 "@unnumbered Function, Method, and Variable Index\n"
797 "@printindex fn\n")
798 "")
799 (if cindex
800 (concat "@node Miscellaneous Index\n"
801 "@unnumbered Miscellaneous Index\n"
802 "@printindex cp\n")
803 "")))
804
805
806(defun py2texi-fix-backslashes ()
807 "Make backslashes from auxiliary commands."
808 (py2texi-search-safe "@backslash{}"
809 (replace-match "\\\\")))
810
811
812(defun py2texi-fix-fonts ()
813 "Remove garbage after unstructured font commands."
814 (let (string)
815 (py2texi-search-safe "@destroy"
816 (replace-match "")
817 (when (eq (preceding-char) ?{)
818 (forward-char -1)
819 (setq string (py2texi-tex-arguments 1))
820 (insert (substring string 1 (1- (length string))))))))
821
822
823(defun py2texi-fix-braces ()
824 "Escape braces for Texinfo."
825 (let (string)
826 (py2texi-search "{"
827 (unless (or (py2texi-protected)
828 (save-excursion
829 (re-search-backward
830 "@\\([a-zA-Z]*\\|multitable.*\\){\\=" nil t)))
831 (forward-char -1)
832 (setq string (py2texi-tex-arguments 1))
833 (insert "@" (substring string 0 (1- (length string))) "@}")))))
834
835
836(defun py2texi-fix-newlines ()
837 "Remove extra newlines."
838 (py2texi-search "\n\n\n+"
839 (replace-match "\n\n"))
840 (py2texi-search-safe "@item.*\n\n"
841 (delete-backward-char 1))
842 (py2texi-search "@end example"
843 (unless (looking-at "\n\n")
844 (insert "\n"))))
845
846
847(defun py2texi-destroy-empties ()
848 "Remove all comments.
849This avoids some makeinfo errors."
850 (py2texi-search "@c\\>"
851 (unless (eq (py2texi-protected) t)
852 (delete-region (- (point) 2) (save-excursion (end-of-line) (point)))
853 (cond
854 ((looking-at "\n\n")
855 (delete-char 1))
856 ((save-excursion (re-search-backward "^[ \t]*\\=" nil t))
857 (delete-region (save-excursion (beginning-of-line) (point))
858 (1+ (point))))))))
859
860
861(defun py2texi-adjust-level ()
862 "Increase heading level to @chapter, if needed.
863This is only needed for distutils, so it has a very simple form only."
864 (goto-char (point-min))
865 (unless (re-search-forward "@chapter\\>" nil t)
866 (py2texi-search-safe "@section\\>"
867 (replace-match "@chapter" t))
868 (py2texi-search-safe "@\\(sub\\)\\(sub\\)?section\\>"
869 (replace-match "" nil nil nil 1))))
870
871
872(defun py2texi-texinfo-escape (beg end)
873 "Escape Texinfo special characters in region."
874 (save-excursion
875 (goto-char beg)
876 (while (re-search-forward "[@{}]" end t)
877 (replace-match "@\\&"))))
878
879
880(defun py2texi-protected ()
881 "Return protection status of the point before current point."
882 (get-text-property (1- (point)) 'py2texi-protected))
883
884
885;;; Announce
886
887(provide 'py2texi)
888
889
890;;; py2texi.el ends here