Commit | Line | Data |
---|---|---|
271672fa BG |
1 | ;;; ox-texinfo.el --- Texinfo Back-End for Org Export Engine |
2 | ||
ba318903 | 3 | ;; Copyright (C) 2012-2014 Free Software Foundation, Inc. |
271672fa BG |
4 | ;; Author: Jonathan Leech-Pepin <jonathan.leechpepin at gmail dot com> |
5 | ;; Keywords: outlines, hypermedia, calendar, wp | |
6 | ||
7 | ;; This file is part of GNU Emacs. | |
8 | ||
9 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
10 | ;; it under the terms of the GNU General Public License as published by | |
11 | ;; the Free Software Foundation, either version 3 of the License, or | |
12 | ;; (at your option) any later version. | |
13 | ||
14 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | ;; GNU General Public License for more details. | |
18 | ||
19 | ;; You should have received a copy of the GNU General Public License | |
20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
21 | ||
22 | ;;; Commentary: | |
23 | ;; | |
24 | ;; This library implements a Texinfo back-end for Org generic | |
25 | ;; exporter. | |
26 | ;; | |
27 | ;; To test it, run | |
28 | ;; | |
29 | ;; M-: (org-export-to-buffer 'texinfo "*Test Texinfo*") RET | |
30 | ;; | |
31 | ;; in an Org mode buffer then switch to the buffer to see the Texinfo | |
32 | ;; export. See ox.el for more details on how this exporter works. | |
33 | ;; | |
34 | ||
35 | ;; It introduces nine new buffer keywords: "TEXINFO_CLASS", | |
36 | ;; "TEXINFO_FILENAME", "TEXINFO_HEADER", "TEXINFO_POST_HEADER", | |
37 | ;; "TEXINFO_DIR_CATEGORY", "TEXINFO_DIR_TITLE", "TEXINFO_DIR_DESC" | |
38 | ;; "SUBTITLE" and "SUBAUTHOR". | |
39 | ||
40 | ;; | |
41 | ;; It introduces 1 new headline property keywords: | |
42 | ;; "TEXINFO_MENU_TITLE" for optional menu titles. | |
43 | ;; | |
44 | ;; To include inline code snippets (for example for generating @kbd{} | |
45 | ;; and @key{} commands), the following export-snippet keys are | |
46 | ;; accepted: | |
47 | ;; | |
48 | ;; texinfo | |
49 | ;; info | |
50 | ;; | |
51 | ;; You can add them for export snippets via any of the below: | |
52 | ;; | |
53 | ;; (add-to-list 'org-export-snippet-translation-alist | |
54 | ;; '("info" . "texinfo")) | |
55 | ;; | |
56 | ||
57 | ;;; Code: | |
58 | ||
59 | (eval-when-compile (require 'cl)) | |
60 | (require 'ox) | |
61 | ||
62 | (defvar orgtbl-exp-regexp) | |
63 | ||
64 | ||
65 | \f | |
66 | ;;; Define Back-End | |
67 | ||
68 | (org-export-define-backend 'texinfo | |
69 | '((bold . org-texinfo-bold) | |
70 | (center-block . org-texinfo-center-block) | |
71 | (clock . org-texinfo-clock) | |
72 | (code . org-texinfo-code) | |
73 | (comment . org-texinfo-comment) | |
74 | (comment-block . org-texinfo-comment-block) | |
75 | (drawer . org-texinfo-drawer) | |
76 | (dynamic-block . org-texinfo-dynamic-block) | |
77 | (entity . org-texinfo-entity) | |
78 | (example-block . org-texinfo-example-block) | |
79 | (export-block . org-texinfo-export-block) | |
80 | (export-snippet . org-texinfo-export-snippet) | |
81 | (fixed-width . org-texinfo-fixed-width) | |
82 | (footnote-definition . org-texinfo-footnote-definition) | |
83 | (footnote-reference . org-texinfo-footnote-reference) | |
84 | (headline . org-texinfo-headline) | |
85 | (inline-src-block . org-texinfo-inline-src-block) | |
86 | (inlinetask . org-texinfo-inlinetask) | |
87 | (italic . org-texinfo-italic) | |
88 | (item . org-texinfo-item) | |
89 | (keyword . org-texinfo-keyword) | |
90 | (line-break . org-texinfo-line-break) | |
91 | (link . org-texinfo-link) | |
92 | (paragraph . org-texinfo-paragraph) | |
93 | (plain-list . org-texinfo-plain-list) | |
94 | (plain-text . org-texinfo-plain-text) | |
95 | (planning . org-texinfo-planning) | |
96 | (property-drawer . org-texinfo-property-drawer) | |
97 | (quote-block . org-texinfo-quote-block) | |
98 | (quote-section . org-texinfo-quote-section) | |
99 | (radio-target . org-texinfo-radio-target) | |
100 | (section . org-texinfo-section) | |
101 | (special-block . org-texinfo-special-block) | |
102 | (src-block . org-texinfo-src-block) | |
103 | (statistics-cookie . org-texinfo-statistics-cookie) | |
104 | (subscript . org-texinfo-subscript) | |
105 | (superscript . org-texinfo-superscript) | |
106 | (table . org-texinfo-table) | |
107 | (table-cell . org-texinfo-table-cell) | |
108 | (table-row . org-texinfo-table-row) | |
109 | (target . org-texinfo-target) | |
110 | (template . org-texinfo-template) | |
111 | (timestamp . org-texinfo-timestamp) | |
112 | (verbatim . org-texinfo-verbatim) | |
113 | (verse-block . org-texinfo-verse-block)) | |
114 | :export-block "TEXINFO" | |
115 | :filters-alist | |
116 | '((:filter-headline . org-texinfo-filter-section-blank-lines) | |
117 | (:filter-section . org-texinfo-filter-section-blank-lines)) | |
118 | :menu-entry | |
119 | '(?i "Export to Texinfo" | |
120 | ((?t "As TEXI file" org-texinfo-export-to-texinfo) | |
121 | (?i "As INFO file" org-texinfo-export-to-info))) | |
122 | :options-alist | |
123 | '((:texinfo-filename "TEXINFO_FILENAME" nil org-texinfo-filename t) | |
124 | (:texinfo-class "TEXINFO_CLASS" nil org-texinfo-default-class t) | |
125 | (:texinfo-header "TEXINFO_HEADER" nil nil newline) | |
126 | (:texinfo-post-header "TEXINFO_POST_HEADER" nil nil newline) | |
127 | (:subtitle "SUBTITLE" nil nil newline) | |
128 | (:subauthor "SUBAUTHOR" nil nil newline) | |
129 | (:texinfo-dircat "TEXINFO_DIR_CATEGORY" nil nil t) | |
130 | (:texinfo-dirtitle "TEXINFO_DIR_TITLE" nil nil t) | |
131 | (:texinfo-dirdesc "TEXINFO_DIR_DESC" nil nil t))) | |
132 | ||
133 | ||
134 | \f | |
135 | ;;; User Configurable Variables | |
136 | ||
137 | (defgroup org-export-texinfo nil | |
138 | "Options for exporting Org mode files to Texinfo." | |
139 | :tag "Org Export Texinfo" | |
140 | :version "24.4" | |
141 | :package-version '(Org . "8.0") | |
142 | :group 'org-export) | |
143 | ||
144 | ;;; Preamble | |
145 | ||
73d3db82 | 146 | (defcustom org-texinfo-filename "" |
271672fa BG |
147 | "Default filename for Texinfo output." |
148 | :group 'org-export-texinfo | |
149 | :type '(string :tag "Export Filename")) | |
150 | ||
151 | (defcustom org-texinfo-coding-system nil | |
152 | "Default document encoding for Texinfo output. | |
153 | ||
154 | If `nil' it will default to `buffer-file-coding-system'." | |
155 | :group 'org-export-texinfo | |
156 | :type 'coding-system) | |
157 | ||
158 | (defcustom org-texinfo-default-class "info" | |
159 | "The default Texinfo class." | |
160 | :group 'org-export-texinfo | |
161 | :type '(string :tag "Texinfo class")) | |
162 | ||
163 | (defcustom org-texinfo-classes | |
164 | '(("info" | |
165 | "\\input texinfo @c -*- texinfo -*-" | |
166 | ("@chapter %s" . "@unnumbered %s") | |
167 | ("@section %s" . "@unnumberedsec %s") | |
168 | ("@subsection %s" . "@unnumberedsubsec %s") | |
169 | ("@subsubsection %s" . "@unnumberedsubsubsec %s"))) | |
170 | "Alist of Texinfo classes and associated header and structure. | |
171 | If #+Texinfo_CLASS is set in the buffer, use its value and the | |
172 | associated information. Here is the structure of each cell: | |
173 | ||
174 | \(class-name | |
175 | header-string | |
176 | \(numbered-section . unnumbered-section\) | |
177 | ...\) | |
178 | ||
179 | The sectioning structure | |
180 | ------------------------ | |
181 | ||
182 | The sectioning structure of the class is given by the elements | |
183 | following the header string. For each sectioning level, a number | |
184 | of strings is specified. A %s formatter is mandatory in each | |
185 | section string and will be replaced by the title of the section. | |
186 | ||
187 | Instead of a list of sectioning commands, you can also specify | |
188 | a function name. That function will be called with two | |
189 | parameters, the \(reduced) level of the headline, and a predicate | |
190 | non-nil when the headline should be numbered. It must return | |
191 | a format string in which the section title will be added." | |
192 | :group 'org-export-texinfo | |
193 | :type '(repeat | |
194 | (list (string :tag "Texinfo class") | |
195 | (string :tag "Texinfo header") | |
196 | (repeat :tag "Levels" :inline t | |
197 | (choice | |
198 | (cons :tag "Heading" | |
199 | (string :tag " numbered") | |
200 | (string :tag "unnumbered")) | |
201 | (function :tag "Hook computing sectioning")))))) | |
202 | ||
203 | ;;; Headline | |
204 | ||
73d3db82 | 205 | (defcustom org-texinfo-format-headline-function 'ignore |
271672fa BG |
206 | "Function to format headline text. |
207 | ||
208 | This function will be called with 5 arguments: | |
209 | TODO the todo keyword (string or nil). | |
210 | TODO-TYPE the type of todo (symbol: `todo', `done', nil) | |
211 | PRIORITY the priority of the headline (integer or nil) | |
212 | TEXT the main headline text (string). | |
213 | TAGS the tags as a list of strings (list of strings or nil). | |
214 | ||
215 | The function result will be used in the section format string. | |
216 | ||
217 | As an example, one could set the variable to the following, in | |
218 | order to reproduce the default set-up: | |
219 | ||
220 | \(defun org-texinfo-format-headline (todo todo-type priority text tags) | |
221 | \"Default format function for a headline.\" | |
222 | \(concat (when todo | |
223 | \(format \"\\\\textbf{\\\\textsc{\\\\textsf{%s}}} \" todo)) | |
224 | \(when priority | |
225 | \(format \"\\\\framebox{\\\\#%c} \" priority)) | |
226 | text | |
227 | \(when tags | |
228 | \(format \"\\\\hfill{}\\\\textsc{%s}\" | |
229 | \(mapconcat 'identity tags \":\"))))" | |
230 | :group 'org-export-texinfo | |
231 | :type 'function) | |
232 | ||
233 | ;;; Node listing (menu) | |
234 | ||
235 | (defcustom org-texinfo-node-description-column 32 | |
236 | "Column at which to start the description in the node | |
237 | listings. | |
238 | ||
239 | If a node title is greater than this length, the description will | |
240 | be placed after the end of the title." | |
241 | :group 'org-export-texinfo | |
242 | :type 'integer) | |
243 | ||
244 | ;;; Footnotes | |
245 | ;; | |
246 | ;; Footnotes are inserted directly | |
247 | ||
248 | ;;; Timestamps | |
249 | ||
250 | (defcustom org-texinfo-active-timestamp-format "@emph{%s}" | |
251 | "A printf format string to be applied to active timestamps." | |
252 | :group 'org-export-texinfo | |
253 | :type 'string) | |
254 | ||
255 | (defcustom org-texinfo-inactive-timestamp-format "@emph{%s}" | |
256 | "A printf format string to be applied to inactive timestamps." | |
257 | :group 'org-export-texinfo | |
258 | :type 'string) | |
259 | ||
260 | (defcustom org-texinfo-diary-timestamp-format "@emph{%s}" | |
261 | "A printf format string to be applied to diary timestamps." | |
262 | :group 'org-export-texinfo | |
263 | :type 'string) | |
264 | ||
265 | ;;; Links | |
266 | ||
267 | (defcustom org-texinfo-link-with-unknown-path-format "@indicateurl{%s}" | |
268 | "Format string for links with unknown path type." | |
269 | :group 'org-export-texinfo | |
270 | :type 'string) | |
271 | ||
272 | ;;; Tables | |
273 | ||
274 | (defcustom org-texinfo-tables-verbatim nil | |
275 | "When non-nil, tables are exported verbatim." | |
276 | :group 'org-export-texinfo | |
277 | :type 'boolean) | |
278 | ||
279 | (defcustom org-texinfo-table-scientific-notation "%s\\,(%s)" | |
280 | "Format string to display numbers in scientific notation. | |
281 | The format should have \"%s\" twice, for mantissa and exponent | |
282 | \(i.e. \"%s\\\\times10^{%s}\"). | |
283 | ||
284 | When nil, no transformation is made." | |
285 | :group 'org-export-texinfo | |
286 | :type '(choice | |
287 | (string :tag "Format string") | |
288 | (const :tag "No formatting"))) | |
289 | ||
290 | (defcustom org-texinfo-def-table-markup "@samp" | |
3c8b09ca BG |
291 | "Default setting for @table environments." |
292 | :group 'org-export-texinfo | |
293 | :type 'string) | |
271672fa BG |
294 | |
295 | ;;; Text markup | |
296 | ||
297 | (defcustom org-texinfo-text-markup-alist '((bold . "@strong{%s}") | |
298 | (code . code) | |
299 | (italic . "@emph{%s}") | |
300 | (verbatim . verb) | |
301 | (comment . "@c %s")) | |
302 | "Alist of Texinfo expressions to convert text markup. | |
303 | ||
304 | The key must be a symbol among `bold', `italic' and `comment'. | |
305 | The value is a formatting string to wrap fontified text with. | |
306 | ||
307 | Value can also be set to the following symbols: `verb' and | |
308 | `code'. For the former, Org will use \"@verb\" to | |
309 | create a format string and select a delimiter character that | |
310 | isn't in the string. For the latter, Org will use \"@code\" | |
311 | to typeset and try to protect special characters. | |
312 | ||
313 | If no association can be found for a given markup, text will be | |
314 | returned as-is." | |
315 | :group 'org-export-texinfo | |
316 | :type 'alist | |
317 | :options '(bold code italic verbatim comment)) | |
318 | ||
319 | ;;; Drawers | |
320 | ||
73d3db82 BG |
321 | (defcustom org-texinfo-format-drawer-function |
322 | (lambda (name contents) contents) | |
271672fa BG |
323 | "Function called to format a drawer in Texinfo code. |
324 | ||
325 | The function must accept two parameters: | |
326 | NAME the drawer name, like \"LOGBOOK\" | |
327 | CONTENTS the contents of the drawer. | |
328 | ||
329 | The function should return the string to be exported. | |
330 | ||
73d3db82 | 331 | The default function simply returns the value of CONTENTS." |
271672fa | 332 | :group 'org-export-texinfo |
73d3db82 BG |
333 | :version "24.4" |
334 | :package-version '(Org . "8.3") | |
271672fa BG |
335 | :type 'function) |
336 | ||
337 | ;;; Inlinetasks | |
338 | ||
73d3db82 | 339 | (defcustom org-texinfo-format-inlinetask-function 'ignore |
271672fa BG |
340 | "Function called to format an inlinetask in Texinfo code. |
341 | ||
342 | The function must accept six parameters: | |
343 | TODO the todo keyword, as a string | |
344 | TODO-TYPE the todo type, a symbol among `todo', `done' and nil. | |
345 | PRIORITY the inlinetask priority, as a string | |
346 | NAME the inlinetask name, as a string. | |
347 | TAGS the inlinetask tags, as a list of strings. | |
348 | CONTENTS the contents of the inlinetask, as a string. | |
349 | ||
350 | The function should return the string to be exported. | |
351 | ||
352 | For example, the variable could be set to the following function | |
da5ecfa9 | 353 | in order to mimic default behavior: |
271672fa BG |
354 | |
355 | \(defun org-texinfo-format-inlinetask \(todo type priority name tags contents\) | |
356 | \"Format an inline task element for Texinfo export.\" | |
357 | \(let ((full-title | |
358 | \(concat | |
359 | \(when todo | |
360 | \(format \"@strong{%s} \" todo)) | |
361 | \(when priority (format \"#%c \" priority)) | |
362 | title | |
363 | \(when tags | |
364 | \(format \":%s:\" | |
365 | \(mapconcat 'identity tags \":\"))))) | |
366 | \(format (concat \"@center %s\n\n\" | |
367 | \"%s\" | |
368 | \"\n\")) | |
369 | full-title contents))" | |
370 | :group 'org-export-texinfo | |
371 | :type 'function) | |
372 | ||
373 | ;;; Src blocks | |
374 | ;; | |
375 | ;; Src Blocks are example blocks, except for LISP | |
376 | ||
377 | ;;; Compilation | |
378 | ||
379 | (defcustom org-texinfo-info-process | |
380 | '("makeinfo %f") | |
381 | "Commands to process a Texinfo file to an INFO file. | |
382 | This is list of strings, each of them will be given to the shell | |
383 | as a command. %f in the command will be replaced by the full | |
384 | file name, %b by the file base name \(i.e without extension) and | |
385 | %o by the base directory of the file." | |
386 | :group 'org-export-texinfo | |
387 | :type '(repeat :tag "Shell command sequence" | |
388 | (string :tag "Shell command"))) | |
389 | ||
390 | (defcustom org-texinfo-logfiles-extensions | |
391 | '("aux" "toc" "cp" "fn" "ky" "pg" "tp" "vr") | |
392 | "The list of file extensions to consider as Texinfo logfiles. | |
393 | The logfiles will be remove if `org-texinfo-remove-logfiles' is | |
394 | non-nil." | |
395 | :group 'org-export-texinfo | |
396 | :type '(repeat (string :tag "Extension"))) | |
397 | ||
398 | (defcustom org-texinfo-remove-logfiles t | |
399 | "Non-nil means remove the logfiles produced by compiling a Texinfo file. | |
400 | By default, logfiles are files with these extensions: .aux, .toc, | |
401 | .cp, .fn, .ky, .pg and .tp. To define the set of logfiles to remove, | |
402 | set `org-texinfo-logfiles-extensions'." | |
403 | :group 'org-export-latex | |
404 | :type 'boolean) | |
405 | ||
406 | ||
407 | ;;; Constants | |
408 | (defconst org-texinfo-max-toc-depth 4 | |
409 | "Maximum depth for creation of detailed menu listings. Beyond | |
410 | this depth Texinfo will not recognize the nodes and will cause | |
411 | errors. Left as a constant in case this value ever changes.") | |
412 | ||
413 | (defconst org-texinfo-supported-coding-systems | |
414 | '("US-ASCII" "UTF-8" "ISO-8859-15" "ISO-8859-1" "ISO-8859-2" "koi8-r" "koi8-u") | |
415 | "List of coding systems supported by Texinfo, as strings. | |
416 | Specified coding system will be matched against these strings. | |
417 | If two strings share the same prefix (e.g. \"ISO-8859-1\" and | |
418 | \"ISO-8859-15\"), the most specific one has to be listed first.") | |
419 | ||
420 | \f | |
421 | ;;; Internal Functions | |
422 | ||
423 | (defun org-texinfo-filter-section-blank-lines (headline back-end info) | |
424 | "Filter controlling number of blank lines after a section." | |
425 | (let ((blanks (make-string 2 ?\n))) | |
426 | (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline))) | |
427 | ||
428 | (defun org-texinfo--find-verb-separator (s) | |
429 | "Return a character not used in string S. | |
430 | This is used to choose a separator for constructs like \\verb." | |
431 | (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}")) | |
432 | (loop for c across ll | |
433 | when (not (string-match (regexp-quote (char-to-string c)) s)) | |
434 | return (char-to-string c)))) | |
435 | ||
436 | (defun org-texinfo--make-option-string (options) | |
437 | "Return a comma separated string of keywords and values. | |
438 | OPTIONS is an alist where the key is the options keyword as | |
439 | a string, and the value a list containing the keyword value, or | |
440 | nil." | |
441 | (mapconcat (lambda (pair) | |
442 | (concat (first pair) | |
443 | (when (> (length (second pair)) 0) | |
444 | (concat "=" (second pair))))) | |
445 | options | |
446 | ",")) | |
447 | ||
448 | (defun org-texinfo--text-markup (text markup) | |
449 | "Format TEXT depending on MARKUP text markup. | |
450 | See `org-texinfo-text-markup-alist' for details." | |
451 | (let ((fmt (cdr (assq markup org-texinfo-text-markup-alist)))) | |
452 | (cond | |
453 | ;; No format string: Return raw text. | |
454 | ((not fmt) text) | |
455 | ((eq 'verb fmt) | |
456 | (let ((separator (org-texinfo--find-verb-separator text))) | |
457 | (concat "@verb{" separator text separator "}"))) | |
458 | ((eq 'code fmt) | |
459 | (let ((start 0) | |
460 | (rtn "") | |
461 | char) | |
462 | (while (string-match "[@{}]" text) | |
463 | (setq char (match-string 0 text)) | |
464 | (if (> (match-beginning 0) 0) | |
465 | (setq rtn (concat rtn (substring text 0 (match-beginning 0))))) | |
466 | (setq text (substring text (1+ (match-beginning 0)))) | |
467 | (setq char (concat "@" char) | |
468 | rtn (concat rtn char))) | |
469 | (setq text (concat rtn text) | |
470 | fmt "@code{%s}") | |
471 | (format fmt text))) | |
472 | ;; Else use format string. | |
473 | (t (format fmt text))))) | |
474 | ||
475 | (defun org-texinfo--get-node (headline info) | |
476 | "Return node entry associated to HEADLINE. | |
477 | INFO is a plist used as a communication channel." | |
478 | (let ((menu-title (org-export-get-alt-title headline info))) | |
479 | (org-texinfo--sanitize-menu | |
480 | (replace-regexp-in-string | |
481 | "%" "%%" | |
482 | (if menu-title (org-export-data menu-title info) | |
483 | (org-texinfo--sanitize-headline | |
484 | (org-element-property :title headline) info)))))) | |
485 | ||
486 | ;;; Headline sanitizing | |
487 | ||
488 | (defun org-texinfo--sanitize-headline (headline info) | |
489 | "Remove all formatting from the text of a headline for use in | |
490 | node and menu listing." | |
491 | (mapconcat 'identity | |
492 | (org-texinfo--sanitize-headline-contents headline info) " ")) | |
493 | ||
494 | (defun org-texinfo--sanitize-headline-contents (headline info) | |
495 | "Retrieve the content of the headline. | |
496 | ||
497 | Any content that can contain further formatting is checked | |
498 | recursively, to ensure that nested content is also properly | |
499 | retrieved." | |
500 | (loop for contents in headline append | |
501 | (cond | |
502 | ;; already a string | |
503 | ((stringp contents) | |
504 | (list (replace-regexp-in-string " $" "" contents))) | |
505 | ;; Is exported as-is (value) | |
506 | ((org-element-map contents '(verbatim code) | |
507 | (lambda (value) (org-element-property :value value)) info)) | |
508 | ;; Has content and recurse into the content | |
509 | ((org-element-contents contents) | |
510 | (org-texinfo--sanitize-headline-contents | |
511 | (org-element-contents contents) info))))) | |
512 | ||
513 | ;;; Menu sanitizing | |
514 | ||
515 | (defun org-texinfo--sanitize-menu (title) | |
516 | "Remove invalid characters from TITLE for use in menus and | |
517 | nodes. | |
518 | ||
519 | Based on Texinfo specifications, the following must be removed: | |
520 | @ { } ( ) : . ," | |
521 | (replace-regexp-in-string "[@{}():,.]" "" title)) | |
522 | ||
523 | ;;; Content sanitizing | |
524 | ||
525 | (defun org-texinfo--sanitize-content (text) | |
526 | "Ensure characters are properly escaped when used in headlines or blocks. | |
527 | ||
528 | Escape characters are: @ { }" | |
529 | (replace-regexp-in-string "\\\([@{}]\\\)" "@\\1" text)) | |
530 | ||
531 | ;;; Menu creation | |
532 | ||
533 | (defun org-texinfo--build-menu (tree level info &optional detailed) | |
534 | "Create the @menu/@end menu information from TREE at headline | |
535 | level LEVEL. | |
536 | ||
537 | TREE contains the parse-tree to work with, either of the entire | |
538 | document or of a specific parent headline. LEVEL indicates what | |
539 | level of headlines to look at when generating the menu. INFO is | |
540 | a plist containing contextual information. | |
541 | ||
542 | Detailed determines whether to build a single level of menu, or | |
543 | recurse into all children as well." | |
544 | (let ((menu (org-texinfo--generate-menu-list tree level info)) | |
545 | output text-menu) | |
546 | (cond | |
547 | (detailed | |
548 | ;; Looping is done within the menu generation. | |
549 | (setq text-menu (org-texinfo--generate-detailed menu level info))) | |
550 | (t | |
551 | (setq text-menu (org-texinfo--generate-menu-items menu info)))) | |
552 | (when text-menu | |
553 | (setq output (org-texinfo--format-menu text-menu)) | |
554 | (mapconcat 'identity output "\n")))) | |
555 | ||
556 | (defun org-texinfo--generate-detailed (menu level info) | |
557 | "Generate a detailed listing of all subheadings within MENU starting at LEVEL. | |
558 | ||
559 | MENU is the parse-tree to work with. LEVEL is the starting level | |
560 | for the menu headlines and from which recursion occurs. INFO is | |
561 | a plist containing contextual information." | |
562 | (when level | |
563 | (let ((max-depth (min org-texinfo-max-toc-depth | |
564 | (plist-get info :headline-levels)))) | |
565 | (when (> max-depth level) | |
566 | (loop for headline in menu append | |
567 | (let* ((title (org-texinfo--menu-headlines headline info)) | |
568 | ;; Create list of menu entries for the next level | |
569 | (sublist (org-texinfo--generate-menu-list | |
570 | headline (1+ level) info)) | |
571 | ;; Generate the menu items for that level. If | |
572 | ;; there are none omit that heading completely, | |
573 | ;; otherwise join the title to it's related entries. | |
574 | (submenu (if (org-texinfo--generate-menu-items sublist info) | |
575 | (append (list title) | |
576 | (org-texinfo--generate-menu-items sublist info)) | |
577 | 'nil)) | |
578 | ;; Start the process over the next level down. | |
579 | (recursion (org-texinfo--generate-detailed sublist (1+ level) info))) | |
580 | (setq recursion (append submenu recursion)) | |
581 | recursion)))))) | |
582 | ||
583 | (defun org-texinfo--generate-menu-list (tree level info) | |
584 | "Generate the list of headlines that are within a given level | |
585 | of the tree for further formatting. | |
586 | ||
587 | TREE is the parse-tree containing the headlines. LEVEL is the | |
588 | headline level to generate a list of. INFO is a plist holding | |
589 | contextual information." | |
590 | (org-element-map tree 'headline | |
591 | (lambda (head) | |
592 | (and (= (org-export-get-relative-level head info) level) | |
593 | ;; Do not take note of footnotes or copying headlines. | |
594 | (not (org-element-property :COPYING head)) | |
595 | (not (org-element-property :footnote-section-p head)) | |
596 | ;; Collect headline. | |
597 | head)) | |
598 | info)) | |
599 | ||
600 | (defun org-texinfo--generate-menu-items (items info) | |
601 | "Generate a list of headline information from the listing ITEMS. | |
602 | ||
603 | ITEMS is a list of the headlines to be converted into entries. | |
604 | INFO is a plist containing contextual information. | |
605 | ||
606 | Returns a list containing the following information from each | |
607 | headline: length, title, description. This is used to format the | |
608 | menu using `org-texinfo--format-menu'." | |
609 | (loop for headline in items collect | |
610 | (let* ((menu-title (org-texinfo--sanitize-menu | |
611 | (org-export-data | |
612 | (org-export-get-alt-title headline info) | |
613 | info))) | |
614 | (title (org-texinfo--sanitize-menu | |
615 | (org-texinfo--sanitize-headline | |
616 | (org-element-property :title headline) info))) | |
617 | (descr (org-export-data | |
618 | (org-element-property :DESCRIPTION headline) | |
619 | info)) | |
620 | (menu-entry (if (string= "" menu-title) title menu-title)) | |
621 | (len (length menu-entry)) | |
622 | (output (list len menu-entry descr))) | |
623 | output))) | |
624 | ||
625 | (defun org-texinfo--menu-headlines (headline info) | |
626 | "Retrieve the title from HEADLINE. | |
627 | ||
628 | INFO is a plist holding contextual information. | |
629 | ||
630 | Return the headline as a list of (length title description) with | |
631 | length of -1 and nil description. This is used in | |
632 | `org-texinfo--format-menu' to identify headlines as opposed to | |
633 | entries." | |
634 | (let ((title (org-export-data | |
635 | (org-element-property :title headline) info))) | |
636 | (list -1 title 'nil))) | |
637 | ||
638 | (defun org-texinfo--format-menu (text-menu) | |
639 | "Format the TEXT-MENU items to be properly printed in the menu. | |
640 | ||
641 | Each entry in the menu should be provided as (length title | |
642 | description). | |
643 | ||
644 | Headlines in the detailed menu are given length -1 to ensure they | |
645 | are never confused with other entries. They also have no | |
646 | description. | |
647 | ||
648 | Other menu items are output as: | |
649 | Title:: description | |
650 | ||
651 | With the spacing between :: and description based on the length | |
652 | of the longest menu entry." | |
653 | ||
654 | (let (output) | |
655 | (setq output | |
656 | (mapcar (lambda (name) | |
657 | (let* ((title (nth 1 name)) | |
658 | (desc (nth 2 name)) | |
659 | (length (nth 0 name)) | |
660 | (column (max | |
661 | ;;6 is "* " ":: " for inserted text | |
662 | length | |
663 | (- | |
664 | org-texinfo-node-description-column | |
665 | 6))) | |
666 | (spacing (- column length) | |
667 | )) | |
668 | (if (> length -1) | |
669 | (concat "* " title ":: " | |
670 | (make-string spacing ?\s) | |
671 | (if desc | |
672 | (concat desc))) | |
673 | (concat "\n" title "\n")))) | |
674 | text-menu)) | |
675 | output)) | |
676 | ||
677 | ;;; Template | |
678 | ||
679 | (defun org-texinfo-template (contents info) | |
680 | "Return complete document string after Texinfo conversion. | |
681 | CONTENTS is the transcoded contents string. INFO is a plist | |
682 | holding export options." | |
683 | (let* ((title (org-export-data (plist-get info :title) info)) | |
684 | (info-filename (or (plist-get info :texinfo-filename) | |
685 | (file-name-nondirectory | |
686 | (org-export-output-file-name ".info")))) | |
687 | (author (org-export-data (plist-get info :author) info)) | |
688 | (lang (org-export-data (plist-get info :language) info)) | |
689 | (texinfo-header (plist-get info :texinfo-header)) | |
690 | (texinfo-post-header (plist-get info :texinfo-post-header)) | |
691 | (subtitle (plist-get info :subtitle)) | |
692 | (subauthor (plist-get info :subauthor)) | |
693 | (class (plist-get info :texinfo-class)) | |
694 | (header (nth 1 (assoc class org-texinfo-classes))) | |
695 | (copying | |
696 | (org-element-map (plist-get info :parse-tree) 'headline | |
697 | (lambda (hl) (and (org-element-property :COPYING hl) hl)) info t)) | |
698 | (dircat (plist-get info :texinfo-dircat)) | |
699 | (dirtitle (plist-get info :texinfo-dirtitle)) | |
700 | (dirdesc (plist-get info :texinfo-dirdesc)) | |
701 | ;; Spacing to align description (column 32 - 3 for `* ' and | |
702 | ;; `.' in text. | |
703 | (dirspacing (- 29 (length dirtitle))) | |
704 | (menu (org-texinfo-make-menu info 'main)) | |
705 | (detail-menu (org-texinfo-make-menu info 'detailed))) | |
706 | (concat | |
707 | ;; Header | |
708 | header "\n" | |
709 | "@c %**start of header\n" | |
710 | ;; Filename and Title | |
711 | "@setfilename " info-filename "\n" | |
712 | "@settitle " title "\n" | |
713 | ;; Coding system. | |
714 | (format | |
715 | "@documentencoding %s\n" | |
716 | (catch 'coding-system | |
717 | (let ((case-fold-search t) | |
718 | (name (symbol-name (or org-texinfo-coding-system | |
719 | buffer-file-coding-system)))) | |
720 | (dolist (system org-texinfo-supported-coding-systems "UTF-8") | |
721 | (when (org-string-match-p (regexp-quote system) name) | |
722 | (throw 'coding-system system)))))) | |
723 | "\n" | |
724 | (format "@documentlanguage %s\n" lang) | |
725 | "\n\n" | |
726 | "@c Version and Contact Info\n" | |
727 | "@set AUTHOR " author "\n" | |
728 | ||
729 | ;; Additional Header Options set by `#+TEXINFO_HEADER | |
730 | (if texinfo-header | |
731 | (concat "\n" | |
732 | texinfo-header | |
733 | "\n")) | |
734 | ||
735 | "@c %**end of header\n" | |
736 | "@finalout\n" | |
737 | "\n\n" | |
738 | ||
739 | ;; Additional Header Options set by #+TEXINFO_POST_HEADER | |
740 | (if texinfo-post-header | |
741 | (concat "\n" | |
742 | texinfo-post-header | |
743 | "\n")) | |
744 | ||
745 | ;; Copying | |
746 | "@copying\n" | |
747 | ;; Only export the content of the headline, do not need the | |
748 | ;; initial headline. | |
749 | (org-export-data (nth 2 copying) info) | |
750 | "@end copying\n" | |
751 | "\n\n" | |
752 | ||
753 | ;; Info directory information | |
754 | ;; Only supply if both title and category are provided | |
755 | (if (and dircat dirtitle) | |
756 | (concat "@dircategory " dircat "\n" | |
757 | "@direntry\n" | |
758 | "* " dirtitle "." | |
759 | (make-string dirspacing ?\s) | |
760 | dirdesc "\n" | |
761 | "@end direntry\n")) | |
762 | "\n\n" | |
763 | ||
764 | ;; Title | |
765 | "@titlepage\n" | |
766 | "@title " title "\n\n" | |
767 | (if subtitle | |
768 | (concat "@subtitle " subtitle "\n")) | |
769 | "@author " author "\n" | |
770 | (if subauthor | |
771 | (concat subauthor "\n")) | |
772 | "\n" | |
773 | "@c The following two commands start the copyright page.\n" | |
774 | "@page\n" | |
775 | "@vskip 0pt plus 1filll\n" | |
776 | "@insertcopying\n" | |
777 | "@end titlepage\n\n" | |
778 | "@c Output the table of contents at the beginning.\n" | |
779 | "@contents\n\n" | |
780 | ||
781 | ;; Configure Top Node when not for Tex | |
782 | "@ifnottex\n" | |
783 | "@node Top\n" | |
784 | "@top " title " Manual\n" | |
785 | "@insertcopying\n" | |
786 | "@end ifnottex\n\n" | |
787 | ||
788 | ;; Do not output menus if they are empty | |
789 | (if menu | |
790 | ;; Menu | |
791 | (concat "@menu\n" | |
792 | menu | |
793 | "\n\n" | |
794 | ;; Detailed Menu | |
795 | (if detail-menu | |
796 | (concat "@detailmenu\n" | |
797 | " --- The Detailed Node Listing ---\n" | |
798 | detail-menu | |
799 | "\n\n" | |
800 | "@end detailmenu\n")) | |
801 | "@end menu\n")) | |
802 | "\n\n" | |
803 | ||
804 | ;; Document's body. | |
805 | contents | |
806 | "\n" | |
807 | ;; Creator. | |
808 | (let ((creator-info (plist-get info :with-creator))) | |
809 | (cond | |
810 | ((not creator-info) "") | |
811 | ((eq creator-info 'comment) | |
812 | (format "@c %s\n" (plist-get info :creator))) | |
813 | (t (concat (plist-get info :creator) "\n")))) | |
814 | ;; Document end. | |
815 | "\n@bye"))) | |
816 | ||
817 | ||
818 | \f | |
819 | ;;; Transcode Functions | |
820 | ||
821 | ;;; Bold | |
822 | ||
823 | (defun org-texinfo-bold (bold contents info) | |
824 | "Transcode BOLD from Org to Texinfo. | |
825 | CONTENTS is the text with bold markup. INFO is a plist holding | |
826 | contextual information." | |
827 | (org-texinfo--text-markup contents 'bold)) | |
828 | ||
829 | ;;; Center Block | |
830 | ||
831 | (defun org-texinfo-center-block (center-block contents info) | |
832 | "Transcode a CENTER-BLOCK element from Org to Texinfo. | |
833 | CONTENTS holds the contents of the block. INFO is a plist used | |
834 | as a communication channel." | |
835 | contents) | |
836 | ||
837 | ;;; Clock | |
838 | ||
839 | (defun org-texinfo-clock (clock contents info) | |
840 | "Transcode a CLOCK element from Org to Texinfo. | |
841 | CONTENTS is nil. INFO is a plist holding contextual | |
842 | information." | |
843 | (concat | |
844 | "@noindent" | |
845 | (format "@strong{%s} " org-clock-string) | |
846 | (format org-texinfo-inactive-timestamp-format | |
847 | (concat (org-translate-time | |
848 | (org-element-property :raw-value | |
849 | (org-element-property :value clock))) | |
850 | (let ((time (org-element-property :duration clock))) | |
851 | (and time (format " (%s)" time))))) | |
852 | "@*")) | |
853 | ||
854 | ;;; Code | |
855 | ||
856 | (defun org-texinfo-code (code contents info) | |
857 | "Transcode a CODE object from Org to Texinfo. | |
858 | CONTENTS is nil. INFO is a plist used as a communication | |
859 | channel." | |
860 | (org-texinfo--text-markup (org-element-property :value code) 'code)) | |
861 | ||
862 | ;;; Comment | |
863 | ||
864 | (defun org-texinfo-comment (comment contents info) | |
865 | "Transcode a COMMENT object from Org to Texinfo. | |
866 | CONTENTS is the text in the comment. INFO is a plist holding | |
867 | contextual information." | |
868 | (org-texinfo--text-markup (org-element-property :value comment) 'comment)) | |
869 | ||
870 | ;;; Comment Block | |
871 | ||
872 | (defun org-texinfo-comment-block (comment-block contents info) | |
873 | "Transcode a COMMENT-BLOCK object from Org to Texinfo. | |
874 | CONTENTS is the text within the block. INFO is a plist holding | |
875 | contextual information." | |
876 | (format "@ignore\n%s@end ignore" (org-element-property :value comment-block))) | |
877 | ||
878 | ;;; Drawer | |
879 | ||
880 | (defun org-texinfo-drawer (drawer contents info) | |
881 | "Transcode a DRAWER element from Org to Texinfo. | |
882 | CONTENTS holds the contents of the block. INFO is a plist | |
883 | holding contextual information." | |
884 | (let* ((name (org-element-property :drawer-name drawer)) | |
73d3db82 BG |
885 | (output (funcall org-texinfo-format-drawer-function |
886 | name contents))) | |
271672fa BG |
887 | output)) |
888 | ||
889 | ;;; Dynamic Block | |
890 | ||
891 | (defun org-texinfo-dynamic-block (dynamic-block contents info) | |
892 | "Transcode a DYNAMIC-BLOCK element from Org to Texinfo. | |
893 | CONTENTS holds the contents of the block. INFO is a plist | |
894 | holding contextual information. See `org-export-data'." | |
895 | contents) | |
896 | ||
897 | ;;; Entity | |
898 | ||
899 | (defun org-texinfo-entity (entity contents info) | |
900 | "Transcode an ENTITY object from Org to Texinfo. | |
901 | CONTENTS are the definition itself. INFO is a plist holding | |
902 | contextual information." | |
903 | (let ((ent (org-element-property :latex entity))) | |
904 | (if (org-element-property :latex-math-p entity) (format "@math{%s}" ent) ent))) | |
905 | ||
906 | ;;; Example Block | |
907 | ||
908 | (defun org-texinfo-example-block (example-block contents info) | |
909 | "Transcode an EXAMPLE-BLOCK element from Org to Texinfo. | |
910 | CONTENTS is nil. INFO is a plist holding contextual | |
911 | information." | |
912 | (format "@verbatim\n%s@end verbatim" | |
913 | (org-export-format-code-default example-block info))) | |
914 | ||
915 | ;;; Export Block | |
916 | ||
917 | (defun org-texinfo-export-block (export-block contents info) | |
918 | "Transcode a EXPORT-BLOCK element from Org to Texinfo. | |
919 | CONTENTS is nil. INFO is a plist holding contextual information." | |
920 | (when (string= (org-element-property :type export-block) "TEXINFO") | |
921 | (org-remove-indentation (org-element-property :value export-block)))) | |
922 | ||
923 | ;;; Export Snippet | |
924 | ||
925 | (defun org-texinfo-export-snippet (export-snippet contents info) | |
926 | "Transcode a EXPORT-SNIPPET object from Org to Texinfo. | |
927 | CONTENTS is nil. INFO is a plist holding contextual information." | |
928 | (when (eq (org-export-snippet-backend export-snippet) 'texinfo) | |
929 | (org-element-property :value export-snippet))) | |
930 | ||
931 | ;;; Fixed Width | |
932 | ||
933 | (defun org-texinfo-fixed-width (fixed-width contents info) | |
934 | "Transcode a FIXED-WIDTH element from Org to Texinfo. | |
935 | CONTENTS is nil. INFO is a plist holding contextual information." | |
936 | (format "@example\n%s\n@end example" | |
937 | (org-remove-indentation | |
938 | (org-texinfo--sanitize-content | |
939 | (org-element-property :value fixed-width))))) | |
940 | ||
941 | ;;; Footnote Reference | |
942 | ;; | |
943 | ||
944 | (defun org-texinfo-footnote-reference (footnote contents info) | |
945 | "Create a footnote reference for FOOTNOTE. | |
946 | ||
947 | FOOTNOTE is the footnote to define. CONTENTS is nil. INFO is a | |
948 | plist holding contextual information." | |
949 | (let ((def (org-export-get-footnote-definition footnote info))) | |
950 | (format "@footnote{%s}" | |
951 | (org-trim (org-export-data def info))))) | |
952 | ||
953 | ;;; Headline | |
954 | ||
955 | (defun org-texinfo-headline (headline contents info) | |
956 | "Transcode a HEADLINE element from Org to Texinfo. | |
957 | CONTENTS holds the contents of the headline. INFO is a plist | |
958 | holding contextual information." | |
959 | (let* ((class (plist-get info :texinfo-class)) | |
960 | (level (org-export-get-relative-level headline info)) | |
961 | (numberedp (org-export-numbered-headline-p headline info)) | |
d1389828 | 962 | (class-sectioning (assoc class org-texinfo-classes)) |
271672fa BG |
963 | ;; Find the index type, if any |
964 | (index (org-element-property :INDEX headline)) | |
965 | ;; Check if it is an appendix | |
966 | (appendix (org-element-property :APPENDIX headline)) | |
967 | ;; Retrieve headline text | |
968 | (text (org-texinfo--sanitize-headline | |
969 | (org-element-property :title headline) info)) | |
970 | ;; Create node info, to insert it before section formatting. | |
971 | ;; Use custom menu title if present | |
972 | (node (format "@node %s\n" (org-texinfo--get-node headline info))) | |
973 | ;; Menus must be generated with first child, otherwise they | |
974 | ;; will not nest properly | |
975 | (menu (let* ((first (org-export-first-sibling-p headline info)) | |
976 | (parent (org-export-get-parent-headline headline)) | |
977 | (title (org-texinfo--sanitize-headline | |
978 | (org-element-property :title parent) info)) | |
979 | heading listing | |
980 | (tree (plist-get info :parse-tree))) | |
981 | (if first | |
982 | (org-element-map (plist-get info :parse-tree) 'headline | |
983 | (lambda (ref) | |
984 | (if (member title (org-element-property :title ref)) | |
985 | (push ref heading))) | |
986 | info t)) | |
987 | (setq listing (org-texinfo--build-menu | |
988 | (car heading) level info)) | |
989 | (if listing | |
990 | (setq listing (replace-regexp-in-string | |
991 | "%" "%%" listing) | |
992 | listing (format | |
993 | "\n@menu\n%s\n@end menu\n\n" listing)) | |
994 | 'nil))) | |
995 | ;; Section formatting will set two placeholders: one for the | |
996 | ;; title and the other for the contents. | |
997 | (section-fmt | |
d1389828 PE |
998 | (let ((sec (if (and (symbolp (nth 2 class-sectioning)) |
999 | (fboundp (nth 2 class-sectioning))) | |
1000 | (funcall (nth 2 class-sectioning) level numberedp) | |
1001 | (nth (1+ level) class-sectioning)))) | |
271672fa BG |
1002 | (cond |
1003 | ;; No section available for that LEVEL. | |
1004 | ((not sec) nil) | |
1005 | ;; Section format directly returned by a function. | |
1006 | ((stringp sec) sec) | |
1007 | ;; (numbered-section . unnumbered-section) | |
1008 | ((not (consp (cdr sec))) | |
1009 | (cond | |
1010 | ;;If an index, always unnumbered | |
1011 | (index | |
1012 | (concat menu node (cdr sec) "\n%s")) | |
1013 | (appendix | |
1014 | (concat menu node (replace-regexp-in-string | |
1015 | "unnumbered" | |
1016 | "appendix" | |
1017 | (cdr sec)) "\n%s")) | |
1018 | ;; Otherwise number as needed. | |
1019 | (t | |
1020 | (concat menu node | |
1021 | (funcall | |
1022 | (if numberedp #'car #'cdr) sec) "\n%s"))))))) | |
1023 | (todo | |
1024 | (and (plist-get info :with-todo-keywords) | |
1025 | (let ((todo (org-element-property :todo-keyword headline))) | |
1026 | (and todo (org-export-data todo info))))) | |
1027 | (todo-type (and todo (org-element-property :todo-type headline))) | |
1028 | (tags (and (plist-get info :with-tags) | |
1029 | (org-export-get-tags headline info))) | |
1030 | (priority (and (plist-get info :with-priority) | |
1031 | (org-element-property :priority headline))) | |
1032 | ;; Create the headline text along with a no-tag version. The | |
1033 | ;; latter is required to remove tags from table of contents. | |
1034 | (full-text (org-texinfo--sanitize-content | |
73d3db82 | 1035 | (if (not (eq org-texinfo-format-headline-function 'ignore)) |
271672fa BG |
1036 | ;; User-defined formatting function. |
1037 | (funcall org-texinfo-format-headline-function | |
1038 | todo todo-type priority text tags) | |
1039 | ;; Default formatting. | |
1040 | (concat | |
1041 | (when todo | |
1042 | (format "@strong{%s} " todo)) | |
1043 | (when priority (format "@emph{#%s} " priority)) | |
1044 | text | |
1045 | (when tags | |
1046 | (format " :%s:" | |
1047 | (mapconcat 'identity tags ":"))))))) | |
1048 | (full-text-no-tag | |
1049 | (org-texinfo--sanitize-content | |
73d3db82 | 1050 | (if (not (eq org-texinfo-format-headline-function 'ignore)) |
271672fa BG |
1051 | ;; User-defined formatting function. |
1052 | (funcall org-texinfo-format-headline-function | |
1053 | todo todo-type priority text nil) | |
1054 | ;; Default formatting. | |
1055 | (concat | |
1056 | (when todo (format "@strong{%s} " todo)) | |
1057 | (when priority (format "@emph{#%c} " priority)) | |
1058 | text)))) | |
1059 | (pre-blanks | |
1060 | (make-string (org-element-property :pre-blank headline) 10))) | |
1061 | (cond | |
1062 | ;; Case 1: This is a footnote section: ignore it. | |
1063 | ((org-element-property :footnote-section-p headline) nil) | |
1064 | ;; Case 2: This is the `copying' section: ignore it | |
1065 | ;; This is used elsewhere. | |
1066 | ((org-element-property :COPYING headline) nil) | |
1067 | ;; Case 3: An index. If it matches one of the known indexes, | |
1068 | ;; print it as such following the contents, otherwise | |
1069 | ;; print the contents and leave the index up to the user. | |
1070 | (index | |
1071 | (format | |
1072 | section-fmt full-text | |
1073 | (concat pre-blanks contents "\n" | |
1074 | (if (member index '("cp" "fn" "ky" "pg" "tp" "vr")) | |
1075 | (concat "@printindex " index))))) | |
1076 | ;; Case 4: This is a deep sub-tree: export it as a list item. | |
1077 | ;; Also export as items headlines for which no section | |
1078 | ;; format has been found. | |
1079 | ((or (not section-fmt) (org-export-low-level-p headline info)) | |
1080 | ;; Build the real contents of the sub-tree. | |
1081 | (let ((low-level-body | |
1082 | (concat | |
1083 | ;; If the headline is the first sibling, start a list. | |
1084 | (when (org-export-first-sibling-p headline info) | |
1085 | (format "@%s\n" (if numberedp 'enumerate 'itemize))) | |
1086 | ;; Itemize headline | |
1087 | "@item\n" full-text "\n" pre-blanks contents))) | |
1088 | ;; If headline is not the last sibling simply return | |
1089 | ;; LOW-LEVEL-BODY. Otherwise, also close the list, before any | |
1090 | ;; blank line. | |
1091 | (if (not (org-export-last-sibling-p headline info)) low-level-body | |
1092 | (replace-regexp-in-string | |
1093 | "[ \t\n]*\\'" | |
1094 | (format "\n@end %s" (if numberedp 'enumerate 'itemize)) | |
1095 | low-level-body)))) | |
1096 | ;; Case 5: Standard headline. Export it as a section. | |
1097 | (t | |
1098 | (cond | |
1099 | ((not (and tags (eq (plist-get info :with-tags) 'not-in-toc))) | |
1100 | ;; Regular section. Use specified format string. | |
1101 | (format (replace-regexp-in-string "%]" "%%]" section-fmt) full-text | |
1102 | (concat pre-blanks contents))) | |
1103 | ((string-match "\\`@\\(.*?\\){" section-fmt) | |
1104 | ;; If tags should be removed from table of contents, insert | |
1105 | ;; title without tags as an alternative heading in sectioning | |
1106 | ;; command. | |
1107 | (format (replace-match (concat (match-string 1 section-fmt) "[%s]") | |
1108 | nil nil section-fmt 1) | |
1109 | ;; Replace square brackets with parenthesis since | |
1110 | ;; square brackets are not supported in optional | |
1111 | ;; arguments. | |
1112 | (replace-regexp-in-string | |
1113 | "\\[" "(" | |
1114 | (replace-regexp-in-string | |
1115 | "\\]" ")" | |
1116 | full-text-no-tag)) | |
1117 | full-text | |
1118 | (concat pre-blanks contents))) | |
1119 | (t | |
1120 | ;; Impossible to add an alternative heading. Fallback to | |
1121 | ;; regular sectioning format string. | |
1122 | (format (replace-regexp-in-string "%]" "%%]" section-fmt) full-text | |
1123 | (concat pre-blanks contents)))))))) | |
1124 | ||
1125 | ;;; Inline Src Block | |
1126 | ||
1127 | (defun org-texinfo-inline-src-block (inline-src-block contents info) | |
1128 | "Transcode an INLINE-SRC-BLOCK element from Org to Texinfo. | |
1129 | CONTENTS holds the contents of the item. INFO is a plist holding | |
1130 | contextual information." | |
1131 | (let* ((code (org-element-property :value inline-src-block)) | |
1132 | (separator (org-texinfo--find-verb-separator code))) | |
1133 | (concat "@verb{" separator code separator "}"))) | |
1134 | ||
1135 | ;;; Inlinetask | |
1136 | ||
1137 | (defun org-texinfo-inlinetask (inlinetask contents info) | |
1138 | "Transcode an INLINETASK element from Org to Texinfo. | |
1139 | CONTENTS holds the contents of the block. INFO is a plist | |
1140 | holding contextual information." | |
1141 | (let ((title (org-export-data (org-element-property :title inlinetask) info)) | |
1142 | (todo (and (plist-get info :with-todo-keywords) | |
1143 | (let ((todo (org-element-property :todo-keyword inlinetask))) | |
1144 | (and todo (org-export-data todo info))))) | |
1145 | (todo-type (org-element-property :todo-type inlinetask)) | |
1146 | (tags (and (plist-get info :with-tags) | |
1147 | (org-export-get-tags inlinetask info))) | |
1148 | (priority (and (plist-get info :with-priority) | |
1149 | (org-element-property :priority inlinetask)))) | |
1150 | ;; If `org-texinfo-format-inlinetask-function' is provided, call it | |
1151 | ;; with appropriate arguments. | |
73d3db82 | 1152 | (if (not (eq org-texinfo-format-inlinetask-function 'ignore)) |
271672fa BG |
1153 | (funcall org-texinfo-format-inlinetask-function |
1154 | todo todo-type priority title tags contents) | |
1155 | ;; Otherwise, use a default template. | |
1156 | (let ((full-title | |
1157 | (concat | |
1158 | (when todo (format "@strong{%s} " todo)) | |
1159 | (when priority (format "#%c " priority)) | |
1160 | title | |
1161 | (when tags (format ":%s:" | |
1162 | (mapconcat 'identity tags ":")))))) | |
1163 | (format (concat "@center %s\n\n" | |
1164 | "%s" | |
1165 | "\n") | |
1166 | full-title contents))))) | |
1167 | ||
1168 | ;;; Italic | |
1169 | ||
1170 | (defun org-texinfo-italic (italic contents info) | |
1171 | "Transcode ITALIC from Org to Texinfo. | |
1172 | CONTENTS is the text with italic markup. INFO is a plist holding | |
1173 | contextual information." | |
1174 | (org-texinfo--text-markup contents 'italic)) | |
1175 | ||
1176 | ;;; Item | |
1177 | ||
1178 | (defun org-texinfo-item (item contents info) | |
1179 | "Transcode an ITEM element from Org to Texinfo. | |
1180 | CONTENTS holds the contents of the item. INFO is a plist holding | |
1181 | contextual information." | |
1182 | (let* ((tag (org-element-property :tag item)) | |
1183 | (desc (org-export-data tag info))) | |
1184 | (concat "\n@item " (if tag desc) "\n" | |
1185 | (and contents (org-trim contents)) "\n"))) | |
1186 | ||
1187 | ;;; Keyword | |
1188 | ||
1189 | (defun org-texinfo-keyword (keyword contents info) | |
1190 | "Transcode a KEYWORD element from Org to Texinfo. | |
1191 | CONTENTS is nil. INFO is a plist holding contextual information." | |
1192 | (let ((key (org-element-property :key keyword)) | |
1193 | (value (org-element-property :value keyword))) | |
1194 | (cond | |
1195 | ((string= key "TEXINFO") value) | |
1196 | ((string= key "CINDEX") (format "@cindex %s" value)) | |
1197 | ((string= key "FINDEX") (format "@findex %s" value)) | |
1198 | ((string= key "KINDEX") (format "@kindex %s" value)) | |
1199 | ((string= key "PINDEX") (format "@pindex %s" value)) | |
1200 | ((string= key "TINDEX") (format "@tindex %s" value)) | |
1201 | ((string= key "VINDEX") (format "@vindex %s" value))))) | |
1202 | ||
1203 | ;;; Line Break | |
1204 | ||
1205 | (defun org-texinfo-line-break (line-break contents info) | |
1206 | "Transcode a LINE-BREAK object from Org to Texinfo. | |
1207 | CONTENTS is nil. INFO is a plist holding contextual information." | |
1208 | "@*\n") | |
1209 | ||
1210 | ;;; Link | |
1211 | ||
1212 | (defun org-texinfo-link (link desc info) | |
1213 | "Transcode a LINK object from Org to Texinfo. | |
1214 | ||
1215 | DESC is the description part of the link, or the empty string. | |
1216 | INFO is a plist holding contextual information. See | |
1217 | `org-export-data'." | |
1218 | (let* ((type (org-element-property :type link)) | |
1219 | (raw-path (org-element-property :path link)) | |
1220 | ;; Ensure DESC really exists, or set it to nil. | |
1221 | (desc (and (not (string= desc "")) desc)) | |
1222 | (path (cond | |
1223 | ((member type '("http" "https" "ftp")) | |
1224 | (concat type ":" raw-path)) | |
30cb51f1 BG |
1225 | ((and (string= type "file") (file-name-absolute-p raw-path)) |
1226 | (concat "file:" raw-path)) | |
271672fa BG |
1227 | (t raw-path))) |
1228 | (email (if (string= type "mailto") | |
1229 | (let ((text (replace-regexp-in-string | |
1230 | "@" "@@" raw-path))) | |
1231 | (concat text (if desc (concat "," desc)))))) | |
1232 | protocol) | |
1233 | (cond | |
1234 | ;; Links pointing to a headline: Find destination and build | |
1235 | ;; appropriate referencing command. | |
1236 | ((member type '("custom-id" "id")) | |
1237 | (let ((destination (org-export-resolve-id-link link info))) | |
1238 | (case (org-element-type destination) | |
1239 | ;; Id link points to an external file. | |
1240 | (plain-text | |
1241 | (if desc (format "@uref{file://%s,%s}" destination desc) | |
1242 | (format "@uref{file://%s}" destination))) | |
1243 | ;; LINK points to a headline. Use the headline as the NODE target | |
1244 | (headline | |
1245 | (format "@ref{%s,%s}" | |
1246 | (org-texinfo--get-node destination info) | |
1247 | (or desc ""))) | |
1248 | (otherwise | |
1249 | (let ((path (org-export-solidify-link-text path))) | |
1250 | (if (not desc) (format "@ref{%s}" path) | |
1251 | (format "@ref{%s,,%s}" path desc))))))) | |
1252 | ((member type '("info")) | |
1253 | (let* ((info-path (split-string path "[:#]")) | |
1254 | (info-manual (car info-path)) | |
1255 | (info-node (or (cadr info-path) "top")) | |
1256 | (title (or desc ""))) | |
1257 | (format "@ref{%s,%s,,%s,}" info-node title info-manual))) | |
1258 | ((member type '("fuzzy")) | |
1259 | (let ((destination (org-export-resolve-fuzzy-link link info))) | |
1260 | (case (org-element-type destination) | |
1261 | ;; Id link points to an external file. | |
1262 | (plain-text | |
1263 | (if desc (format "@uref{file://%s,%s}" destination desc) | |
1264 | (format "@uref{file://%s}" destination))) | |
1265 | ;; LINK points to a headline. Use the headline as the NODE target | |
1266 | (headline | |
1267 | (format "@ref{%s,%s}" | |
1268 | (org-texinfo--get-node destination info) | |
1269 | (or desc ""))) | |
1270 | (otherwise | |
1271 | (let ((path (org-export-solidify-link-text path))) | |
1272 | (if (not desc) (format "@ref{%s}" path) | |
1273 | (format "@ref{%s,,%s}" path desc))))))) | |
1274 | ;; Special case for email addresses | |
1275 | ||
1276 | (format "@email{%s}" email)) | |
1277 | ;; External link with a description part. | |
1278 | ((and path desc) (format "@uref{%s,%s}" path desc)) | |
1279 | ;; External link without a description part. | |
1280 | (path (format "@uref{%s}" path)) | |
1281 | ;; No path, only description. Try to do something useful. | |
1282 | (t (format org-texinfo-link-with-unknown-path-format desc))))) | |
1283 | ||
1284 | ||
1285 | ;;; Menu | |
1286 | ||
1287 | (defun org-texinfo-make-menu (info level) | |
1288 | "Create the menu for inclusion in the texifo document. | |
1289 | ||
1290 | INFO is the parsed buffer that contains the headlines. LEVEL | |
1291 | determines whether to make the main menu, or the detailed menu. | |
1292 | ||
1293 | This is only used for generating the primary menu. In-Node menus | |
1294 | are generated directly." | |
1295 | (let ((parse (plist-get info :parse-tree))) | |
1296 | (cond | |
1297 | ;; Generate the main menu | |
1298 | ((eq level 'main) (org-texinfo--build-menu parse 1 info)) | |
1299 | ;; Generate the detailed (recursive) menu | |
1300 | ((eq level 'detailed) | |
1301 | ;; Requires recursion | |
1302 | ;;(org-texinfo--build-detailed-menu parse top info) | |
1303 | (org-texinfo--build-menu parse 1 info 'detailed))))) | |
1304 | ||
1305 | ;;; Paragraph | |
1306 | ||
1307 | (defun org-texinfo-paragraph (paragraph contents info) | |
1308 | "Transcode a PARAGRAPH element from Org to Texinfo. | |
1309 | CONTENTS is the contents of the paragraph, as a string. INFO is | |
1310 | the plist used as a communication channel." | |
1311 | contents) | |
1312 | ||
1313 | ;;; Plain List | |
1314 | ||
1315 | (defun org-texinfo-plain-list (plain-list contents info) | |
1316 | "Transcode a PLAIN-LIST element from Org to Texinfo. | |
1317 | CONTENTS is the contents of the list. INFO is a plist holding | |
1318 | contextual information." | |
1319 | (let* ((attr (org-export-read-attribute :attr_texinfo plain-list)) | |
1320 | (indic (or (plist-get attr :indic) | |
1321 | org-texinfo-def-table-markup)) | |
1322 | (type (org-element-property :type plain-list)) | |
1323 | (table-type (plist-get attr :table-type)) | |
1324 | ;; Ensure valid texinfo table type. | |
1325 | (table-type (if (member table-type '("ftable" "vtable")) table-type | |
1326 | "table")) | |
1327 | (list-type (cond | |
1328 | ((eq type 'ordered) "enumerate") | |
1329 | ((eq type 'unordered) "itemize") | |
1330 | ((eq type 'descriptive) table-type)))) | |
1331 | (format "@%s%s\n@end %s" | |
1332 | (if (eq type 'descriptive) | |
1333 | (concat list-type " " indic) | |
1334 | list-type) | |
1335 | contents | |
1336 | list-type))) | |
1337 | ||
1338 | ;;; Plain Text | |
1339 | ||
1340 | (defun org-texinfo-plain-text (text info) | |
1341 | "Transcode a TEXT string from Org to Texinfo. | |
1342 | TEXT is the string to transcode. INFO is a plist holding | |
1343 | contextual information." | |
1344 | ;; First protect @, { and }. | |
1345 | (let ((output (org-texinfo--sanitize-content text))) | |
1346 | ;; Activate smart quotes. Be sure to provide original TEXT string | |
1347 | ;; since OUTPUT may have been modified. | |
1348 | (when (plist-get info :with-smart-quotes) | |
1349 | (setq output | |
1350 | (org-export-activate-smart-quotes output :texinfo info text))) | |
1351 | ;; LaTeX into @LaTeX{} and TeX into @TeX{} | |
1352 | (let ((case-fold-search nil) | |
1353 | (start 0)) | |
1354 | (while (string-match "\\(\\(?:La\\)?TeX\\)" output start) | |
1355 | (setq output (replace-match | |
1356 | (format "@%s{}" (match-string 1 output)) nil t output) | |
1357 | start (match-end 0)))) | |
1358 | ;; Convert special strings. | |
1359 | (when (plist-get info :with-special-strings) | |
1360 | (while (string-match (regexp-quote "...") output) | |
1361 | (setq output (replace-match "@dots{}" nil t output)))) | |
1362 | ;; Handle break preservation if required. | |
1363 | (when (plist-get info :preserve-breaks) | |
1364 | (setq output (replace-regexp-in-string | |
1365 | "\\(\\\\\\\\\\)?[ \t]*\n" " @*\n" output))) | |
1366 | ;; Return value. | |
1367 | output)) | |
1368 | ||
1369 | ;;; Planning | |
1370 | ||
1371 | (defun org-texinfo-planning (planning contents info) | |
1372 | "Transcode a PLANNING element from Org to Texinfo. | |
1373 | CONTENTS is nil. INFO is a plist holding contextual | |
1374 | information." | |
1375 | (concat | |
1376 | "@noindent" | |
1377 | (mapconcat | |
1378 | 'identity | |
1379 | (delq nil | |
1380 | (list | |
1381 | (let ((closed (org-element-property :closed planning))) | |
1382 | (when closed | |
1383 | (concat | |
1384 | (format "@strong{%s} " org-closed-string) | |
1385 | (format org-texinfo-inactive-timestamp-format | |
1386 | (org-translate-time | |
1387 | (org-element-property :raw-value closed)))))) | |
1388 | (let ((deadline (org-element-property :deadline planning))) | |
1389 | (when deadline | |
1390 | (concat | |
1391 | (format "@strong{%s} " org-deadline-string) | |
1392 | (format org-texinfo-active-timestamp-format | |
1393 | (org-translate-time | |
1394 | (org-element-property :raw-value deadline)))))) | |
1395 | (let ((scheduled (org-element-property :scheduled planning))) | |
1396 | (when scheduled | |
1397 | (concat | |
1398 | (format "@strong{%s} " org-scheduled-string) | |
1399 | (format org-texinfo-active-timestamp-format | |
1400 | (org-translate-time | |
1401 | (org-element-property :raw-value scheduled)))))))) | |
1402 | " ") | |
1403 | "@*")) | |
1404 | ||
1405 | ;;; Property Drawer | |
1406 | ||
1407 | (defun org-texinfo-property-drawer (property-drawer contents info) | |
1408 | "Transcode a PROPERTY-DRAWER element from Org to Texinfo. | |
1409 | CONTENTS is nil. INFO is a plist holding contextual | |
1410 | information." | |
1411 | ;; The property drawer isn't exported but we want separating blank | |
1412 | ;; lines nonetheless. | |
1413 | "") | |
1414 | ||
1415 | ;;; Quote Block | |
1416 | ||
1417 | (defun org-texinfo-quote-block (quote-block contents info) | |
1418 | "Transcode a QUOTE-BLOCK element from Org to Texinfo. | |
1419 | CONTENTS holds the contents of the block. INFO is a plist | |
1420 | holding contextual information." | |
1421 | (let* ((title (org-element-property :name quote-block)) | |
1422 | (start-quote (concat "@quotation" | |
1423 | (if title | |
1424 | (format " %s" title))))) | |
1425 | (format "%s\n%s@end quotation" start-quote contents))) | |
1426 | ||
1427 | ;;; Quote Section | |
1428 | ||
1429 | (defun org-texinfo-quote-section (quote-section contents info) | |
1430 | "Transcode a QUOTE-SECTION element from Org to Texinfo. | |
1431 | CONTENTS is nil. INFO is a plist holding contextual information." | |
1432 | (let ((value (org-remove-indentation | |
1433 | (org-element-property :value quote-section)))) | |
1434 | (when value (format "@verbatim\n%s@end verbatim" value)))) | |
1435 | ||
1436 | ;;; Radio Target | |
1437 | ||
1438 | (defun org-texinfo-radio-target (radio-target text info) | |
1439 | "Transcode a RADIO-TARGET object from Org to Texinfo. | |
1440 | TEXT is the text of the target. INFO is a plist holding | |
1441 | contextual information." | |
1442 | (format "@anchor{%s}%s" | |
1443 | (org-export-solidify-link-text | |
1444 | (org-element-property :value radio-target)) | |
1445 | text)) | |
1446 | ||
1447 | ;;; Section | |
1448 | ||
1449 | (defun org-texinfo-section (section contents info) | |
1450 | "Transcode a SECTION element from Org to Texinfo. | |
1451 | CONTENTS holds the contents of the section. INFO is a plist | |
1452 | holding contextual information." | |
1453 | contents) | |
1454 | ||
1455 | ;;; Special Block | |
1456 | ||
1457 | (defun org-texinfo-special-block (special-block contents info) | |
1458 | "Transcode a SPECIAL-BLOCK element from Org to Texinfo. | |
1459 | CONTENTS holds the contents of the block. INFO is a plist used | |
1460 | as a communication channel." | |
1461 | contents) | |
1462 | ||
1463 | ;;; Src Block | |
1464 | ||
1465 | (defun org-texinfo-src-block (src-block contents info) | |
1466 | "Transcode a SRC-BLOCK element from Org to Texinfo. | |
1467 | CONTENTS holds the contents of the item. INFO is a plist holding | |
1468 | contextual information." | |
1469 | (let* ((lang (org-element-property :language src-block)) | |
1470 | (lisp-p (string-match-p "lisp" lang)) | |
1471 | (src-contents (org-texinfo--sanitize-content | |
1472 | (org-export-format-code-default src-block info)))) | |
1473 | (cond | |
1474 | ;; Case 1. Lisp Block | |
1475 | (lisp-p | |
1476 | (format "@lisp\n%s@end lisp" | |
1477 | src-contents)) | |
1478 | ;; Case 2. Other blocks | |
1479 | (t | |
1480 | (format "@example\n%s@end example" | |
1481 | src-contents))))) | |
1482 | ||
1483 | ;;; Statistics Cookie | |
1484 | ||
1485 | (defun org-texinfo-statistics-cookie (statistics-cookie contents info) | |
1486 | "Transcode a STATISTICS-COOKIE object from Org to Texinfo. | |
1487 | CONTENTS is nil. INFO is a plist holding contextual information." | |
1488 | (org-element-property :value statistics-cookie)) | |
1489 | ||
1490 | ;;; Subscript | |
1491 | ||
1492 | (defun org-texinfo-subscript (subscript contents info) | |
1493 | "Transcode a SUBSCRIPT object from Org to Texinfo. | |
1494 | CONTENTS is the contents of the object. INFO is a plist holding | |
1495 | contextual information." | |
1496 | (format "@math{_%s}" contents)) | |
1497 | ||
1498 | ;;; Superscript | |
1499 | ||
1500 | (defun org-texinfo-superscript (superscript contents info) | |
1501 | "Transcode a SUPERSCRIPT object from Org to Texinfo. | |
1502 | CONTENTS is the contents of the object. INFO is a plist holding | |
1503 | contextual information." | |
1504 | (format "@math{^%s}" contents)) | |
1505 | ||
1506 | ;;; Table | |
1507 | ;; | |
1508 | ;; `org-texinfo-table' is the entry point for table transcoding. It | |
1509 | ;; takes care of tables with a "verbatim" attribute. Otherwise, it | |
1510 | ;; delegates the job to either `org-texinfo-table--table.el-table' or | |
1511 | ;; `org-texinfo-table--org-table' functions, depending of the type of | |
1512 | ;; the table. | |
1513 | ;; | |
1514 | ;; `org-texinfo-table--align-string' is a subroutine used to build | |
1515 | ;; alignment string for Org tables. | |
1516 | ||
1517 | (defun org-texinfo-table (table contents info) | |
1518 | "Transcode a TABLE element from Org to Texinfo. | |
1519 | CONTENTS is the contents of the table. INFO is a plist holding | |
1520 | contextual information." | |
1521 | (cond | |
1522 | ;; Case 1: verbatim table. | |
1523 | ((or org-texinfo-tables-verbatim | |
1524 | (let ((attr (mapconcat 'identity | |
1525 | (org-element-property :attr_latex table) | |
1526 | " "))) | |
1527 | (and attr (string-match "\\<verbatim\\>" attr)))) | |
1528 | (format "@verbatim \n%s\n@end verbatim" | |
1529 | ;; Re-create table, without affiliated keywords. | |
1530 | (org-trim | |
1531 | (org-element-interpret-data | |
1532 | `(table nil ,@(org-element-contents table)))))) | |
1533 | ;; Case 2: table.el table. Convert it using appropriate tools. | |
1534 | ((eq (org-element-property :type table) 'table.el) | |
1535 | (org-texinfo-table--table.el-table table contents info)) | |
1536 | ;; Case 3: Standard table. | |
1537 | (t (org-texinfo-table--org-table table contents info)))) | |
1538 | ||
1539 | (defun org-texinfo-table-column-widths (table info) | |
1540 | "Determine the largest table cell in each column to process alignment. | |
1541 | ||
1542 | TABLE is the table element to transcode. INFO is a plist used as | |
1543 | a communication channel." | |
1544 | (let* ((rows (org-element-map table 'table-row 'identity info)) | |
1545 | (collected (loop for row in rows collect | |
1546 | (org-element-map row 'table-cell 'identity info))) | |
1547 | (number-cells (length (car collected))) | |
1548 | cells counts) | |
1549 | (loop for row in collected do | |
1550 | (push (mapcar (lambda (ref) | |
1551 | (let* ((start (org-element-property :contents-begin ref)) | |
1552 | (end (org-element-property :contents-end ref)) | |
1553 | (length (- end start))) | |
1554 | length)) row) cells)) | |
1555 | (setq cells (org-remove-if 'null cells)) | |
1556 | (push (loop for count from 0 to (- number-cells 1) collect | |
1557 | (loop for item in cells collect | |
1558 | (nth count item))) counts) | |
1559 | (mapconcat (lambda (size) | |
1560 | (make-string size ?a)) (mapcar (lambda (ref) | |
1561 | (apply 'max `(,@ref))) (car counts)) | |
1562 | "} {"))) | |
1563 | ||
1564 | (defun org-texinfo-table--org-table (table contents info) | |
1565 | "Return appropriate Texinfo code for an Org table. | |
1566 | ||
1567 | TABLE is the table type element to transcode. CONTENTS is its | |
1568 | contents, as a string. INFO is a plist used as a communication | |
1569 | channel. | |
1570 | ||
1571 | This function assumes TABLE has `org' as its `:type' attribute." | |
1572 | (let* ((attr (org-export-read-attribute :attr_texinfo table)) | |
1573 | (col-width (plist-get attr :columns)) | |
1574 | (columns (if col-width | |
1575 | (format "@columnfractions %s" | |
1576 | col-width) | |
1577 | (format "{%s}" | |
1578 | (org-texinfo-table-column-widths | |
1579 | table info))))) | |
1580 | ;; Prepare the final format string for the table. | |
1581 | (cond | |
1582 | ;; Longtable. | |
1583 | ;; Others. | |
1584 | (t (concat | |
1585 | (format "@multitable %s\n%s@end multitable" | |
1586 | columns | |
1587 | contents)))))) | |
1588 | ||
1589 | (defun org-texinfo-table--table.el-table (table contents info) | |
1590 | "Returns nothing. | |
1591 | ||
1592 | Rather than return an invalid table, nothing is returned." | |
1593 | 'nil) | |
1594 | ||
1595 | ;;; Table Cell | |
1596 | ||
1597 | (defun org-texinfo-table-cell (table-cell contents info) | |
1598 | "Transcode a TABLE-CELL element from Org to Texinfo. | |
1599 | CONTENTS is the cell contents. INFO is a plist used as | |
1600 | a communication channel." | |
1601 | (concat (if (and contents | |
1602 | org-texinfo-table-scientific-notation | |
1603 | (string-match orgtbl-exp-regexp contents)) | |
1604 | ;; Use appropriate format string for scientific | |
1605 | ;; notation. | |
1606 | (format org-texinfo-table-scientific-notation | |
1607 | (match-string 1 contents) | |
1608 | (match-string 2 contents)) | |
1609 | contents) | |
1610 | (when (org-export-get-next-element table-cell info) "\n@tab "))) | |
1611 | ||
1612 | ;;; Table Row | |
1613 | ||
1614 | (defun org-texinfo-table-row (table-row contents info) | |
1615 | "Transcode a TABLE-ROW element from Org to Texinfo. | |
1616 | CONTENTS is the contents of the row. INFO is a plist used as | |
1617 | a communication channel." | |
1618 | ;; Rules are ignored since table separators are deduced from | |
1619 | ;; borders of the current row. | |
1620 | (when (eq (org-element-property :type table-row) 'standard) | |
1621 | (let ((rowgroup-tag | |
1622 | (cond | |
1623 | ;; Case 1: Belongs to second or subsequent rowgroup. | |
1624 | ((not (= 1 (org-export-table-row-group table-row info))) | |
1625 | "@item ") | |
1626 | ;; Case 2: Row is from first rowgroup. Table has >=1 rowgroups. | |
1627 | ((org-export-table-has-header-p | |
1628 | (org-export-get-parent-table table-row) info) | |
1629 | "@headitem ") | |
1630 | ;; Case 3: Row is from first and only row group. | |
1631 | (t "@item ")))) | |
1632 | (when (eq (org-element-property :type table-row) 'standard) | |
1633 | (concat rowgroup-tag contents "\n"))))) | |
1634 | ||
1635 | ;;; Target | |
1636 | ||
1637 | (defun org-texinfo-target (target contents info) | |
1638 | "Transcode a TARGET object from Org to Texinfo. | |
1639 | CONTENTS is nil. INFO is a plist holding contextual | |
1640 | information." | |
1641 | (format "@anchor{%s}" | |
1642 | (org-export-solidify-link-text (org-element-property :value target)))) | |
1643 | ||
1644 | ;;; Timestamp | |
1645 | ||
1646 | (defun org-texinfo-timestamp (timestamp contents info) | |
1647 | "Transcode a TIMESTAMP object from Org to Texinfo. | |
1648 | CONTENTS is nil. INFO is a plist holding contextual | |
1649 | information." | |
1650 | (let ((value (org-texinfo-plain-text | |
1651 | (org-timestamp-translate timestamp) info))) | |
1652 | (case (org-element-property :type timestamp) | |
1653 | ((active active-range) | |
1654 | (format org-texinfo-active-timestamp-format value)) | |
1655 | ((inactive inactive-range) | |
1656 | (format org-texinfo-inactive-timestamp-format value)) | |
1657 | (t (format org-texinfo-diary-timestamp-format value))))) | |
1658 | ||
1659 | ;;; Verbatim | |
1660 | ||
1661 | (defun org-texinfo-verbatim (verbatim contents info) | |
1662 | "Transcode a VERBATIM object from Org to Texinfo. | |
1663 | CONTENTS is nil. INFO is a plist used as a communication | |
1664 | channel." | |
1665 | (org-texinfo--text-markup (org-element-property :value verbatim) 'verbatim)) | |
1666 | ||
1667 | ;;; Verse Block | |
1668 | ||
1669 | (defun org-texinfo-verse-block (verse-block contents info) | |
1670 | "Transcode a VERSE-BLOCK element from Org to Texinfo. | |
1671 | CONTENTS is verse block contents. INFO is a plist holding | |
1672 | contextual information." | |
1673 | ;; In a verse environment, add a line break to each newline | |
1674 | ;; character and change each white space at beginning of a line | |
1675 | ;; into a space of 1 em. Also change each blank line with | |
1676 | ;; a vertical space of 1 em. | |
1677 | (progn | |
1678 | (setq contents (replace-regexp-in-string | |
1679 | "^ *\\\\\\\\$" "\\\\vspace*{1em}" | |
1680 | (replace-regexp-in-string | |
1681 | "\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n" contents))) | |
1682 | (while (string-match "^[ \t]+" contents) | |
1683 | (let ((new-str (format "\\hspace*{%dem}" | |
1684 | (length (match-string 0 contents))))) | |
1685 | (setq contents (replace-match new-str nil t contents)))) | |
1686 | (format "\\begin{verse}\n%s\\end{verse}" contents))) | |
1687 | ||
1688 | \f | |
1689 | ;;; Interactive functions | |
1690 | ||
1691 | (defun org-texinfo-export-to-texinfo | |
1692 | (&optional async subtreep visible-only body-only ext-plist) | |
1693 | "Export current buffer to a Texinfo file. | |
1694 | ||
1695 | If narrowing is active in the current buffer, only export its | |
1696 | narrowed part. | |
1697 | ||
1698 | If a region is active, export that region. | |
1699 | ||
1700 | A non-nil optional argument ASYNC means the process should happen | |
1701 | asynchronously. The resulting file should be accessible through | |
1702 | the `org-export-stack' interface. | |
1703 | ||
1704 | When optional argument SUBTREEP is non-nil, export the sub-tree | |
1705 | at point, extracting information from the headline properties | |
1706 | first. | |
1707 | ||
1708 | When optional argument VISIBLE-ONLY is non-nil, don't export | |
1709 | contents of hidden elements. | |
1710 | ||
1711 | When optional argument BODY-ONLY is non-nil, only write code | |
1712 | between \"\\begin{document}\" and \"\\end{document}\". | |
1713 | ||
1714 | EXT-PLIST, when provided, is a property list with external | |
1715 | parameters overriding Org default settings, but still inferior to | |
1716 | file-local settings. | |
1717 | ||
1718 | Return output file's name." | |
1719 | (interactive) | |
1720 | (let ((outfile (org-export-output-file-name ".texi" subtreep)) | |
1721 | (org-export-coding-system `,org-texinfo-coding-system)) | |
1722 | (org-export-to-file 'texinfo outfile | |
1723 | async subtreep visible-only body-only ext-plist))) | |
1724 | ||
1725 | (defun org-texinfo-export-to-info | |
1726 | (&optional async subtreep visible-only body-only ext-plist) | |
1727 | "Export current buffer to Texinfo then process through to INFO. | |
1728 | ||
1729 | If narrowing is active in the current buffer, only export its | |
1730 | narrowed part. | |
1731 | ||
1732 | If a region is active, export that region. | |
1733 | ||
1734 | A non-nil optional argument ASYNC means the process should happen | |
1735 | asynchronously. The resulting file should be accessible through | |
1736 | the `org-export-stack' interface. | |
1737 | ||
1738 | When optional argument SUBTREEP is non-nil, export the sub-tree | |
1739 | at point, extracting information from the headline properties | |
1740 | first. | |
1741 | ||
1742 | When optional argument VISIBLE-ONLY is non-nil, don't export | |
1743 | contents of hidden elements. | |
1744 | ||
1745 | When optional argument BODY-ONLY is non-nil, only write code | |
1746 | between \"\\begin{document}\" and \"\\end{document}\". | |
1747 | ||
1748 | EXT-PLIST, when provided, is a property list with external | |
1749 | parameters overriding Org default settings, but still inferior to | |
1750 | file-local settings. | |
1751 | ||
1752 | When optional argument PUB-DIR is set, use it as the publishing | |
1753 | directory. | |
1754 | ||
1755 | Return INFO file's name." | |
1756 | (interactive) | |
1757 | (let ((outfile (org-export-output-file-name ".texi" subtreep)) | |
1758 | (org-export-coding-system `,org-texinfo-coding-system)) | |
1759 | (org-export-to-file 'texinfo outfile | |
1760 | async subtreep visible-only body-only ext-plist | |
1761 | (lambda (file) (org-texinfo-compile file))))) | |
1762 | ||
1763 | ;;;###autoload | |
1764 | (defun org-texinfo-publish-to-texinfo (plist filename pub-dir) | |
1765 | "Publish an org file to Texinfo. | |
1766 | ||
1767 | FILENAME is the filename of the Org file to be published. PLIST | |
1768 | is the property list for the given project. PUB-DIR is the | |
1769 | publishing directory. | |
1770 | ||
1771 | Return output file name." | |
1772 | (org-publish-org-to 'texinfo filename ".texi" plist pub-dir)) | |
1773 | ||
1774 | ;;;###autoload | |
1775 | (defun org-texinfo-convert-region-to-texinfo () | |
1776 | "Assume the current region has org-mode syntax, and convert it to Texinfo. | |
1777 | This can be used in any buffer. For example, you can write an | |
1778 | itemized list in org-mode syntax in an Texinfo buffer and use | |
1779 | this command to convert it." | |
1780 | (interactive) | |
1781 | (org-export-replace-region-by 'texinfo)) | |
1782 | ||
1783 | (defun org-texinfo-compile (file) | |
1784 | "Compile a texinfo file. | |
1785 | ||
1786 | FILE is the name of the file being compiled. Processing is | |
1787 | done through the command specified in `org-texinfo-info-process'. | |
1788 | ||
1789 | Return INFO file name or an error if it couldn't be produced." | |
1790 | (let* ((base-name (file-name-sans-extension (file-name-nondirectory file))) | |
1791 | (full-name (file-truename file)) | |
1792 | (out-dir (file-name-directory file)) | |
1793 | ;; Properly set working directory for compilation. | |
1794 | (default-directory (if (file-name-absolute-p file) | |
1795 | (file-name-directory full-name) | |
1796 | default-directory)) | |
1797 | errors) | |
1798 | (message (format "Processing Texinfo file %s..." file)) | |
1799 | (save-window-excursion | |
1800 | (cond | |
1801 | ;; A function is provided: Apply it. | |
1802 | ((functionp org-texinfo-info-process) | |
1803 | (funcall org-texinfo-info-process (shell-quote-argument file))) | |
1804 | ;; A list is provided: Replace %b, %f and %o with appropriate | |
1805 | ;; values in each command before applying it. Output is | |
1806 | ;; redirected to "*Org INFO Texinfo Output*" buffer. | |
1807 | ((consp org-texinfo-info-process) | |
1808 | (let ((outbuf (get-buffer-create "*Org INFO Texinfo Output*"))) | |
1809 | (mapc | |
1810 | (lambda (command) | |
1811 | (shell-command | |
1812 | (replace-regexp-in-string | |
1813 | "%b" (shell-quote-argument base-name) | |
1814 | (replace-regexp-in-string | |
1815 | "%f" (shell-quote-argument full-name) | |
1816 | (replace-regexp-in-string | |
1817 | "%o" (shell-quote-argument out-dir) command t t) t t) t t) | |
1818 | outbuf)) | |
1819 | org-texinfo-info-process) | |
1820 | ;; Collect standard errors from output buffer. | |
1821 | (setq errors (org-texinfo-collect-errors outbuf)))) | |
1822 | (t (error "No valid command to process to Info"))) | |
1823 | (let ((infofile (concat out-dir base-name ".info"))) | |
1824 | ;; Check for process failure. Provide collected errors if | |
1825 | ;; possible. | |
1826 | (if (not (file-exists-p infofile)) | |
1827 | (error (concat (format "INFO file %s wasn't produced" infofile) | |
1828 | (when errors (concat ": " errors)))) | |
1829 | ;; Else remove log files, when specified, and signal end of | |
1830 | ;; process to user, along with any error encountered. | |
1831 | (when org-texinfo-remove-logfiles | |
1832 | (dolist (ext org-texinfo-logfiles-extensions) | |
1833 | (let ((file (concat out-dir base-name "." ext))) | |
1834 | (when (file-exists-p file) (delete-file file))))) | |
1835 | (message (concat "Process completed" | |
1836 | (if (not errors) "." | |
1837 | (concat " with errors: " errors))))) | |
1838 | ;; Return output file name. | |
1839 | infofile)))) | |
1840 | ||
1841 | (defun org-texinfo-collect-errors (buffer) | |
1842 | "Collect some kind of errors from \"makeinfo\" command output. | |
1843 | ||
1844 | BUFFER is the buffer containing output. | |
1845 | ||
1846 | Return collected error types as a string, or nil if there was | |
1847 | none." | |
1848 | (with-current-buffer buffer | |
1849 | (save-excursion | |
1850 | (goto-char (point-min)) | |
1851 | ;; Find final "makeinfo" run. | |
1852 | (when t | |
1853 | (let ((case-fold-search t) | |
1854 | (errors "")) | |
1855 | (when (save-excursion | |
1856 | (re-search-forward "perhaps incorrect sectioning?" nil t)) | |
1857 | (setq errors (concat errors " [incorrect sectioning]"))) | |
1858 | (when (save-excursion | |
1859 | (re-search-forward "missing close brace" nil t)) | |
1860 | (setq errors (concat errors " [syntax error]"))) | |
1861 | (when (save-excursion | |
1862 | (re-search-forward "Unknown command" nil t)) | |
1863 | (setq errors (concat errors " [undefined @command]"))) | |
1864 | (when (save-excursion | |
1865 | (re-search-forward "No matching @end" nil t)) | |
1866 | (setq errors (concat errors " [block incomplete]"))) | |
1867 | (when (save-excursion | |
1868 | (re-search-forward "requires a sectioning" nil t)) | |
1869 | (setq errors (concat errors " [invalid section command]"))) | |
1870 | (when (save-excursion | |
1871 | (re-search-forward "\\[unexpected\]" nil t)) | |
1872 | (setq errors (concat errors " [unexpected error]"))) | |
1873 | (when (save-excursion | |
1874 | (re-search-forward "misplaced " nil t)) | |
1875 | (setq errors (concat errors " [syntax error]"))) | |
1876 | (and (org-string-nw-p errors) (org-trim errors))))))) | |
1877 | ||
1878 | ||
1879 | (provide 'ox-texinfo) | |
1880 | ||
1881 | ;; Local variables: | |
1882 | ;; generated-autoload-file: "org-loaddefs.el" | |
1883 | ;; End: | |
1884 | ||
1885 | ;;; ox-texinfo.el ends here |