org-element: Rename `time-stamp' object type into `timestamp'
[org-mode/org-mode-NeilSmithlineMods.git] / contrib / lisp / org-e-ascii.el
blobd6bc1588c99ce6ff79894172063583cb7db17467
1 ;;; org-e-ascii.el --- ASCII Back-End For Org Export Engine
3 ;; Copyright (C) 2012 Free Software Foundation, Inc.
5 ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com>
6 ;; Keywords: outlines, hypermedia, calendar, wp
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation, either version 3 of the License, or
11 ;; (at your option) any later version.
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ;;; Commentary:
23 ;; This library implements an ASCII back-end for Org generic exporter.
25 ;; To test it, run
27 ;; M-: (org-export-to-buffer 'e-ascii "*Test e-ASCII*") RET
29 ;; in an Org mode buffer then switch to that buffer to see the ASCII
30 ;; export. See contrib/lisp/org-export.el for more details on how
31 ;; this exporter works.
33 ;;; Code:
35 (eval-when-compile (require 'cl))
37 (declare-function org-element-contents "org-element" (element))
38 (declare-function org-element-property "org-element" (property element))
39 (declare-function org-element-normalize-string "org-element" (s))
40 (declare-function org-element-map "org-element"
41 (data types fun &optional info first-match))
43 (declare-function org-export-collect-footnote-definitions
44 "org-export" (data info))
45 (declare-function org-export-collect-headlines "org-export" (info &optional n))
46 (declare-function org-export-collect-listings "org-export" (info))
47 (declare-function org-export-collect-tables "org-export" (info))
48 (declare-function org-export-data "org-export" (data info))
49 (declare-function org-export-expand-macro "org-export" (macro info))
50 (declare-function org-export-format-code-default "org-export" (element info))
51 (declare-function org-export-get-coderef-format "org-export" (path desc))
52 (declare-function org-export-get-footnote-number "org-export" (footnote info))
53 (declare-function org-export-get-headline-number "org-export" (headline info))
54 (declare-function org-export-get-ordinal "org-export"
55 (element info &optional types predicate))
56 (declare-function org-export-get-parent-headline "org-export" (blob info))
57 (declare-function org-export-get-relative-level "org-export" (headline info))
58 (declare-function org-export-low-level-p "org-export" (headline info))
59 (declare-function org-export-output-file-name "org-export"
60 (extension &optional subtreep pub-dir))
61 (declare-function org-export-resolve-coderef "org-export" (ref info))
62 (declare-function org-export-resolve-fuzzy-link "org-export" (link info))
63 (declare-function org-export-resolve-id-link "org-export" (link info))
64 (declare-function org-export-resolve-ref-link "org-export" (link info))
65 (declare-function org-export-secondary-string "org-export" (secondary info))
66 (declare-function
67 org-export-to-file "org-export"
68 (backend file &optional subtreep visible-only body-only ext-plist))
72 ;;; Internal Variables
74 ;; The following setting won't allow to modify preferred charset
75 ;; through a buffer keyword or an option item, but, since the property
76 ;; will appear in communication channel nonetheless, it allows to
77 ;; override `org-e-ascii-charset' variable on the fly by the ext-plist
78 ;; mechanism.
80 ;; We also install a filter for headlines and sections, in order to
81 ;; control blank lines separating them in output string.
83 (defconst org-e-ascii-option-alist
84 '((:ascii-charset nil nil org-e-ascii-charset))
85 "Alist between ASCII export properties and ways to set them.
86 See `org-export-option-alist' for more information on the
87 structure or the values.")
89 (defconst org-e-ascii-filters-alist
90 '((:filter-headline . org-e-ascii-filter-headline-blank-lines)
91 (:filter-section . org-e-ascii-filter-headline-blank-lines))
92 "Alist between filters keywords and back-end specific filters.
93 See `org-export-filters-alist' for more information.")
95 (defconst org-e-ascii-dictionary
96 '(("Footnotes\n"
97 ("en"
98 :ascii "Footnotes\n"
99 :latin1 "Footnotes\n"
100 :utf-8 "Footnotes\n")
101 ("fr"
102 :ascii "Notes de bas de page\n"
103 :latin1 "Notes de bas de page\n"
104 :utf-8 "Notes de bas de page\n"))
105 ("Listing %d: %s"
106 ("en"
107 :ascii "Listing %d: %s"
108 :latin1 "Listing %d: %s"
109 :utf-8 "Listing %d: %s")
110 ("fr"
111 :ascii "Programme %d : %s"
112 :latin1 "Programme %d : %s"
113 :utf-8 "Programme nº %d : %s"))
114 ("List Of Listings\n"
115 ("en"
116 :ascii "List Of Listings\n"
117 :latin1 "List Of Listings\n"
118 :utf-8 "List Of Listings\n")
119 ("fr"
120 :ascii "Liste des programmes\n"
121 :latin1 "Liste des programmes\n"
122 :utf-8 "Liste des programmes\n"))
123 ("List Of Tables\n"
124 ("en"
125 :ascii "List Of Tables\n"
126 :latin1 "List Of Tables\n"
127 :utf-8 "List Of Tables\n")
128 ("fr"
129 :ascii "Liste des tableaux\n"
130 :latin1 "Liste des tableaux\n"
131 :utf-8 "Liste des tableaux\n"))
132 ("Listing %d: "
133 ("en"
134 :ascii "Listing %d: "
135 :latin1 "Listing %d: "
136 :utf-8 "Listing %d: ")
137 ("fr"
138 :ascii "Programme %d : "
139 :latin1 "Programme %d : "
140 :utf-8 "Programme nº %d : "))
141 ("Table Of Contents\n"
142 ("en"
143 :ascii "Table Of Contents\n"
144 :latin1 "Table Of Contents\n"
145 :utf-8 "Table Of Contents\n")
146 ("fr"
147 :ascii "Sommaire\n"
148 :latin1 "Table des matières\n"
149 :utf-8 "Table des matières\n"))
150 ("Table %d: %s"
151 ("en"
152 :ascii "Table %d: %s"
153 :latin1 "Table %d: %s"
154 :utf-8 "Table %d: %s")
155 ("fr"
156 :ascii "Tableau %d : %s"
157 :latin1 "Tableau %d : %s"
158 :utf-8 "Tableau nº %d : %s"))
159 ("See section %s"
160 ("en"
161 :ascii "See section %s"
162 :latin1 "See section %s"
163 :utf-8 "See section %s")
164 ("fr"
165 :ascii "cf. section %s"
166 :latin1 "cf. section %s"
167 :utf-8 "cf. section %s"))
168 ("Table %d: "
169 ("en"
170 :ascii "Table %d: "
171 :latin1 "Table %d: "
172 :utf-8 "Table %d: ")
173 ("fr"
174 :ascii "Tableau %d : "
175 :latin1 "Tableau %d : "
176 :utf-8 "Tableau nº %d : "))
177 ("Unknown reference"
178 ("en"
179 :ascii "Unknown reference"
180 :latin1 "Unknown reference"
181 :utf-8 "Unknown reference")
182 ("fr"
183 :ascii "Destination inconnue"
184 :latin1 "Référence inconnue"
185 :utf-8 "Référence inconnue")))
186 "Dictionary for ASCII back-end.
188 Alist whose car is the string to translate and cdr is an alist
189 whose car is the language string and cdr is a plist whose
190 properties are possible charsets and value the translated term.
192 It is used as a database for `org-e-ascii--translate'.")
196 ;;; User Configurable Variables
198 (defgroup org-export-e-ascii nil
199 "Options for exporting Org mode files to ASCII."
200 :tag "Org Export ASCII"
201 :group 'org-export)
203 (defcustom org-e-ascii-text-width 72
204 "Maximum width of exported text.
205 This number includes margin size, as set in
206 `org-e-ascii-global-margin'."
207 :group 'org-export-e-ascii
208 :type 'integer)
210 (defcustom org-e-ascii-global-margin 0
211 "Width of the left margin, in number of characters."
212 :group 'org-export-e-ascii
213 :type 'integer)
215 (defcustom org-e-ascii-inner-margin 2
216 "Width of the inner margin, in number of characters.
217 Inner margin is applied between each headline."
218 :group 'org-export-e-ascii
219 :type 'integer)
221 (defcustom org-e-ascii-quote-margin 6
222 "Width of margin used for quoting text, in characters.
223 This margin is applied on both sides of the text."
224 :group 'org-export-e-ascii
225 :type 'integer)
227 (defcustom org-e-ascii-inlinetask-width 30
228 "Width of inline tasks, in number of characters.
229 This number ignores any margin."
230 :group 'org-export-e-ascii
231 :type 'integer)
233 (defcustom org-e-ascii-headline-spacing '(1 . 2)
234 "Number of blank lines inserted around headlines.
236 This variable can be set to a cons cell. In that case, its car
237 represents the number of blank lines present before headline
238 contents whereas its cdr reflects the number of blank lines after
239 contents.
241 A nil value replicates the number of blank lines found in the
242 original Org buffer at the same place."
243 :group 'org-export-e-ascii
244 :type '(choice
245 (const :tag "Replicate original spacing" nil)
246 (cons :tag "Set an uniform spacing"
247 (integer :tag "Number of blank lines before contents")
248 (integer :tag "Number of blank lines after contents"))))
250 (defcustom org-e-ascii-charset 'ascii
251 "The charset allowed to represent various elements and objects.
252 Possible values are:
253 `ascii' Only use plain ASCII characters
254 `latin1' Include Latin-1 characters
255 `utf-8' Use all UTF-8 characters"
256 :group 'org-export-e-ascii
257 :type '(choice
258 (const :tag "ASCII" ascii)
259 (const :tag "Latin-1" latin1)
260 (const :tag "UTF-8" utf-8)))
262 (defcustom org-e-ascii-underline '((ascii ?= ?~ ?-)
263 (latin1 ?= ?~ ?-)
264 (utf-8 ?═ ?─ ?╌ ?┄ ?┈))
265 "Characters for underlining headings in ASCII export.
267 Alist whose key is a symbol among `ascii', `latin1' and `utf-8'
268 and whose value is a list of characters.
270 For each supported charset, this variable associates a sequence
271 of underline characters. In a sequence, the characters will be
272 used in order for headlines level 1, 2, ... If no character is
273 available for a given level, the headline won't be underlined."
274 :group 'org-export-e-ascii
275 :type '(list
276 (cons :tag "Underline characters sequence"
277 (const :tag "ASCII charset" ascii)
278 (repeat character))
279 (cons :tag "Underline characters sequence"
280 (const :tag "Latin-1 charset" latin1)
281 (repeat character))
282 (cons :tag "Underline characters sequence"
283 (const :tag "UTF-8 charset" utf-8)
284 (repeat character))))
286 (defcustom org-e-ascii-bullets '((ascii ?* ?+ ?-)
287 (latin1 ?§ ?¶)
288 (utf-8 ?◊))
289 "Bullet characters for headlines converted to lists in ASCII export.
291 Alist whose key is a symbol among `ascii', `latin1' and `utf-8'
292 and whose value is a list of characters.
294 The first character is used for the first level considered as low
295 level, and so on. If there are more levels than characters given
296 here, the list will be repeated.
298 Note that this variable doesn't affect plain lists
299 representation."
300 :group 'org-export-e-ascii
301 :type '(list
302 (cons :tag "Bullet characters for low level headlines"
303 (const :tag "ASCII charset" ascii)
304 (repeat character))
305 (cons :tag "Bullet characters for low level headlines"
306 (const :tag "Latin-1 charset" latin1)
307 (repeat character))
308 (cons :tag "Bullet characters for low level headlines"
309 (const :tag "UTF-8 charset" utf-8)
310 (repeat character))))
312 (defcustom org-e-ascii-links-to-notes t
313 "Non-nil means convert links to notes before the next headline.
314 When nil, the link will be exported in place. If the line
315 becomes long in this way, it will be wrapped."
316 :group 'org-export-e-ascii
317 :type 'boolean)
319 (defcustom org-e-ascii-table-keep-all-vertical-lines nil
320 "Non-nil means keep all vertical lines in ASCII tables.
321 When nil, vertical lines will be removed except for those needed
322 for column grouping."
323 :group 'org-export-e-ascii
324 :type 'boolean)
326 (defcustom org-e-ascii-table-widen-columns t
327 "Non-nil means widen narrowed columns for export.
328 When nil, narrowed columns will look in ASCII export just like in
329 Org mode, i.e. with \"=>\" as ellipsis."
330 :group 'org-export-e-ascii
331 :type 'boolean)
333 (defcustom org-e-ascii-caption-above nil
334 "When non-nil, place caption string before the element.
335 Otherwise, place it right after it."
336 :group 'org-export-e-ascii
337 :type 'boolean)
339 (defcustom org-e-ascii-verbatim-format "`%s'"
340 "Format string used for verbatim text and inline code."
341 :group 'org-export-e-ascii
342 :type 'string)
344 (defcustom org-e-ascii-format-drawer-function nil
345 "Function called to format a drawer in ASCII.
347 The function must accept two parameters:
348 NAME the drawer name, like \"LOGBOOK\"
349 CONTENTS the contents of the drawer.
350 WIDTH the text width within the drawer.
352 The function should return either the string to be exported or
353 nil to ignore the drawer.
355 For example, the variable could be set to the following function
356 in order to mimic default behaviour:
358 \(defun org-e-ascii-format-drawer-default \(name contents width\)
359 \"Format a drawer element for ASCII export.\"
360 contents\)"
361 :group 'org-export-e-ascii
362 :type 'function)
364 (defcustom org-e-ascii-format-inlinetask-function nil
365 "Function called to format an inlinetask in ASCII.
367 The function must accept six parameters:
368 TODO the todo keyword, as a string
369 TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
370 PRIORITY the inlinetask priority, as a string
371 NAME the inlinetask name, as a string.
372 TAGS the inlinetask tags, as a string.
373 CONTENTS the contents of the inlinetask, as a string.
375 The function should return either the string to be exported or
376 nil to ignore the inline task.
378 For example, the variable could be set to the following function
379 in order to mimic default behaviour:
381 \(defun org-e-ascii-format-inlinetask-default
382 \(todo type priority name tags contents\)
383 \"Format an inline task element for ASCII export.\"
384 \(let* \(\(utf8p \(eq \(plist-get info :ascii-charset\) 'utf-8\)\)
385 \(width org-e-ascii-inlinetask-width\)
386 \(org-e-ascii--indent-string
387 \(concat
388 ;; Top line, with an additional blank line if not in UTF-8.
389 \(make-string width \(if utf8p ?━ ?_\)\) \"\\n\"
390 \(unless utf8p \(concat \(make-string width ? \) \"\\n\"\)\)
391 ;; Add title. Fill it if wider than inlinetask.
392 \(let \(\(title \(org-e-ascii--build-title inlinetask info width\)\)\)
393 \(if \(<= \(length title\) width\) title
394 \(org-e-ascii--fill-string title width info\)\)\)
395 \"\\n\"
396 ;; If CONTENTS is not empty, insert it along with
397 ;; a separator.
398 \(when \(org-string-nw-p contents\)
399 \(concat \(make-string width \(if utf8p ?─ ?-\)\) \"\\n\" contents\)\)
400 ;; Bottom line.
401 \(make-string width \(if utf8p ?━ ?_\)\)\)
402 ;; Flush the inlinetask to the right.
403 \(- \(plist-get info :ascii-width\)
404 \(plist-get info :ascii-margin\)
405 \(plist-get info :ascii-inner-margin\)
406 \(org-e-ascii--current-text-width inlinetask info\)\)"
407 :group 'org-export-e-ascii
408 :type 'function)
412 ;;; Internal Functions
414 ;; Internal functions fall into three categories.
416 ;; The first one is about text formatting. The core function is
417 ;; `org-e-ascii--current-text-width', which determines the current
418 ;; text width allowed to a given element. In other words, it helps
419 ;; keeping each line width within maximum text width defined in
420 ;; `org-e-ascii-text-width'. Once this information is known,
421 ;; `org-e-ascii--fill-string', `org-e-ascii--justify-string',
422 ;; `org-e-ascii--box-string' and `org-e-ascii--indent-string' can
423 ;; operate on a given output string.
425 ;; The second category contains functions handling elements listings,
426 ;; triggered by "#+TOC:" keyword. As such, `org-e-ascii--build-toc'
427 ;; returns a complete table of contents, `org-e-ascii--list-listings'
428 ;; returns a list of referenceable src-block elements, and
429 ;; `org-e-ascii--list-tables' does the same for table elements.
431 ;; The third category includes general helper functions.
432 ;; `org-e-ascii--build-title' creates the title for a given headline
433 ;; or inlinetask element. `org-e-ascii--build-caption' returns the
434 ;; caption string associated to a table or a src-block.
435 ;; `org-e-ascii--describe-links' creates notes about links for
436 ;; insertion at the end of a section. It uses
437 ;; `org-e-ascii--unique-links' to get the list of links to describe.
438 ;; Eventually, `org-e-ascii--translate' reads `org-e-ascii-dictionary'
439 ;; to internationalize output.
442 (defun org-e-ascii--fill-string (s text-width info &optional justify)
443 "Fill a string with specified text-width and return it.
445 S is the string being filled. TEXT-WIDTH is an integer
446 specifying maximum length of a line. INFO is the plist used as
447 a communication channel.
449 Optional argument JUSTIFY can specify any type of justification
450 among `left', `center', `right' or `full'. A nil value is
451 equivalent to `left'. For a justification that doesn't also fill
452 string, see `org-e-ascii--justify-string'.
454 Return nil if S isn't a string."
455 ;; Don't fill paragraph when break should be preserved.
456 (cond ((not (stringp s)) nil)
457 ((plist-get info :preserve-breaks) s)
458 (t (with-temp-buffer
459 (let ((fill-column text-width)
460 (use-hard-newlines t))
461 (insert s)
462 (fill-region (point-min) (point-max) justify))
463 (buffer-string)))))
465 (defun org-e-ascii--justify-string (s text-width how)
466 "Justify string S.
467 TEXT-WIDTH is an integer specifying maximum length of a line.
468 HOW determines the type of justification: it can be `left',
469 `right', `full' or `center'."
470 (with-temp-buffer
471 (insert s)
472 (goto-char (point-min))
473 (let ((fill-column text-width))
474 (while (< (point) (point-max))
475 (justify-current-line how)
476 (forward-line)))
477 (buffer-string)))
479 (defun org-e-ascii--indent-string (s width)
480 "Indent string S by WIDTH white spaces.
481 Empty lines are not indented."
482 (when (stringp s)
483 (replace-regexp-in-string
484 "\\(^\\)\\(?:.*\\S-\\)" (make-string width ? ) s nil nil 1)))
486 (defun org-e-ascii--box-string (s info)
487 "Return string S with a partial box to its left.
488 INFO is a plist used as a communicaton channel."
489 (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
490 (format (if utf8p "╭────\n%s\n╰────" ",----\n%s\n`----")
491 (replace-regexp-in-string
492 "^" (if utf8p "│ " "| ")
493 ;; Remove last newline character.
494 (replace-regexp-in-string "\n[ \t]*\\'" "" s)))))
496 (defun org-e-ascii--current-text-width (element info)
497 "Return maximum text width for ELEMENT's contents.
498 INFO is a plist used as a communication channel."
499 (case (org-element-type element)
500 ;; Elements with an absolute width: `headline' and `inlinetask'.
501 (inlinetask org-e-ascii-inlinetask-width)
502 ('headline
503 (- org-e-ascii-text-width
504 (let ((low-level-rank (org-export-low-level-p element info)))
505 (if low-level-rank (* low-level-rank 2) org-e-ascii-global-margin))))
506 ;; Elements with a relative width: store maximum text width in
507 ;; TOTAL-WIDTH.
508 (otherwise
509 (let* ((genealogy (cons element (org-export-get-genealogy element info)))
510 ;; Total width is determined by the presence, or not, of an
511 ;; inline task among ELEMENT parents.
512 (total-width
513 (if (loop for parent in genealogy
514 thereis (eq (org-element-type parent) 'inlinetask))
515 org-e-ascii-inlinetask-width
516 ;; No inlinetask: Remove global margin from text width.
517 (- org-e-ascii-text-width
518 org-e-ascii-global-margin
519 (let ((parent (org-export-get-parent-headline element info)))
520 ;; Inner margin doesn't apply to text before first
521 ;; headline.
522 (if (not parent) 0
523 (let ((low-level-rank
524 (org-export-low-level-p parent info)))
525 ;; Inner margin doesn't apply to contents of
526 ;; low level headlines, since they've got their
527 ;; own indentation mechanism.
528 (if low-level-rank (* low-level-rank 2)
529 org-e-ascii-inner-margin))))))))
530 (- total-width
531 ;; Each `quote-block', `quote-section' and `verse-block' above
532 ;; narrows text width by twice the standard margin size.
533 (+ (* (loop for parent in genealogy
534 when (memq (org-element-type parent)
535 '(quote-block quote-section verse-block))
536 count parent)
537 2 org-e-ascii-quote-margin)
538 ;; Text width within a plain-list is restricted by
539 ;; indentation of current item. If that's the case,
540 ;; compute it with the help of `:structure' property from
541 ;; parent item, if any.
542 (let ((parent-item
543 (if (eq (org-element-type element) 'item) element
544 (loop for parent in genealogy
545 when (eq (org-element-type parent) 'item)
546 return parent))))
547 (if (not parent-item) 0
548 ;; Compute indentation offset of the current item,
549 ;; that is the sum of the difference between its
550 ;; indentation and the indentation of the top item in
551 ;; the list and current item bullet's length. Also
552 ;; remove tag length (for description lists) or bullet
553 ;; length.
554 (let ((struct (org-element-property :structure parent-item))
555 (beg-item (org-element-property :begin parent-item)))
556 (+ (- (org-list-get-ind beg-item struct)
557 (org-list-get-ind
558 (org-list-get-top-point struct) struct))
559 (length
560 (or (org-list-get-tag beg-item struct)
561 (org-list-get-bullet beg-item struct)))))))))))))
563 (defun org-e-ascii--build-title
564 (element info text-width &optional underline notags)
565 "Format ELEMENT title and return it.
567 ELEMENT is either an `headline' or `inlinetask' element. INFO is
568 a plist used as a communication channel. TEXT-WIDTH is an
569 integer representing the maximum length of a line.
571 When optional argument UNDERLINE is non-nil, underline title,
572 without the tags, according to `org-e-ascii-underline'
573 specifications.
575 if optional argument NOTAGS is nil, no tags will be added to the
576 title."
577 (let* ((headlinep (eq (org-element-type element) 'headline))
578 (numbers
579 ;; Numbering is specific to headlines.
580 (and headlinep (org-export-numbered-headline-p element info)
581 ;; All tests passed: build numbering string.
582 (concat
583 (mapconcat
584 #'number-to-string
585 (org-export-get-headline-number element info) ".")
586 " ")))
587 (text (org-export-secondary-string
588 (org-element-property :title element) info))
589 (todo
590 (and (plist-get info :with-todo-keywords)
591 (let ((todo (org-element-property :todo-keyword element)))
592 (and todo
593 (concat (org-export-secondary-string todo info) " ")))))
594 (tags (and (not notags)
595 (plist-get info :with-tags)
596 (org-element-property :tags element)))
597 (priority
598 (and (plist-get info :with-priority)
599 (concat (org-element-property :priority element) " ")))
600 (first-part (concat numbers todo priority text)))
601 (concat
602 first-part
603 ;; Align tags, if any.
604 (when tags
605 (format
606 (format " %%%ds"
607 (max (- text-width (1+ (length first-part))) (length tags)))
608 tags))
609 ;; Maybe underline text, if ELEMENT type is `headline' and an
610 ;; underline character has been defined.
611 (when (and underline headlinep)
612 (let ((under-char
613 (nth (1- (org-export-get-relative-level element info))
614 (cdr (assq (plist-get info :ascii-charset)
615 org-e-ascii-underline)))))
616 (and under-char
617 (concat "\n"
618 (make-string (length first-part) under-char))))))))
620 (defun org-e-ascii--build-caption (element info)
621 "Return caption string for ELEMENT, if applicable.
623 INFO is a plist used as a communication channel.
625 The caption string contains the sequence number of ELEMENT if it
626 has a name affiliated keyword, along with the real caption, if
627 any. Return nil when ELEMENT has no affiliated caption or name
628 keyword."
629 (let ((caption (org-element-property :caption element))
630 (name (org-element-property :name element)))
631 (when (or caption name)
632 ;; Get sequence number of current src-block among every
633 ;; src-block with either a caption or a name.
634 (let ((reference
635 (org-export-get-ordinal
636 element info nil
637 (lambda (el info) (or (org-element-property :caption el)
638 (org-element-property :name el)))))
639 (title-fmt (org-e-ascii--translate
640 (case (org-element-type element)
641 (table "Table %d: %s")
642 (src-block "Listing %d: %s")) info)))
643 (org-e-ascii--fill-string
644 (format
645 title-fmt reference
646 (if (not caption) name
647 (org-export-secondary-string (car caption) info)))
648 (org-e-ascii--current-text-width element info) info)))))
650 (defun org-e-ascii--build-toc (info &optional n keyword)
651 "Return a table of contents.
653 INFO is a plist used as a communication channel.
655 Optional argument N, when non-nil, is an integer specifying the
656 depth of the table.
658 Optional argument KEYWORD specifies the TOC keyword, if any, from
659 which the table of contents generation has been initiated."
660 (let ((title (org-e-ascii--translate "Table Of Contents\n" info)))
661 (concat
662 title
663 (make-string (1- (length title))
664 (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
665 "\n\n"
666 (let ((text-width
667 (if keyword (org-e-ascii--current-text-width keyword info)
668 (- org-e-ascii-text-width org-e-ascii-global-margin))))
669 (mapconcat
670 (lambda (headline)
671 (let* ((level (org-export-get-relative-level headline info))
672 (indent (* (1- level) 3)))
673 (concat
674 (unless (zerop indent) (concat (make-string (1- indent) ?.) " "))
675 (org-e-ascii--build-title
676 headline info (- text-width indent) nil
677 (eq (plist-get info :with-tags) 'not-in-toc)))))
678 (org-export-collect-headlines info n) "\n")))))
680 (defun org-e-ascii--list-listings (keyword info)
681 "Return a list of listings.
683 KEYWORD is the keyword that initiated the list of listings
684 generation. INFO is a plist used as a communication channel."
685 (let ((title (org-e-ascii--translate "List Of Listings\n" info)))
686 (concat
687 title
688 (make-string (1- (length title))
689 (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
690 "\n\n"
691 (let ((text-width
692 (if keyword (org-e-ascii--current-text-width keyword info)
693 (- org-e-ascii-text-width org-e-ascii-global-margin)))
694 ;; Use a counter instead of retreiving ordinal of each
695 ;; src-block.
696 (count 0))
697 (mapconcat
698 (lambda (src-block)
699 ;; Store initial text so its length can be computed. This is
700 ;; used to properly align caption right to it in case of
701 ;; filling (like contents of a description list item).
702 (let ((initial-text
703 (format (org-e-ascii--translate "Listing %d: " info)
704 (incf count))))
705 (concat
706 initial-text
707 (org-trim
708 (org-e-ascii--indent-string
709 (org-e-ascii--fill-string
710 (let ((caption (org-element-property :caption src-block)))
711 (if (not caption) (org-element-property :name src-block)
712 (org-export-secondary-string
713 ;; Use short name in priority, if available.
714 (or (cdr caption) (car caption)) info)))
715 (- text-width (length initial-text)) info)
716 (length initial-text))))))
717 (org-export-collect-listings info) "\n")))))
719 (defun org-e-ascii--list-tables (keyword info)
720 "Return a list of listings.
722 KEYWORD is the keyword that initiated the list of listings
723 generation. INFO is a plist used as a communication channel."
724 (let ((title (org-e-ascii--translate "List Of Tables\n" info)))
725 (concat
726 title
727 (make-string (1- (length title))
728 (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
729 "\n\n"
730 (let ((text-width
731 (if keyword (org-e-ascii--current-text-width keyword info)
732 (- org-e-ascii-text-width org-e-ascii-global-margin)))
733 ;; Use a counter instead of retreiving ordinal of each
734 ;; src-block.
735 (count 0))
736 (mapconcat
737 (lambda (table)
738 ;; Store initial text so its length can be computed. This is
739 ;; used to properly align caption right to it in case of
740 ;; filling (like contents of a description list item).
741 (let ((initial-text
742 (format (org-e-ascii--translate "Table %d: " info)
743 (incf count))))
744 (concat
745 initial-text
746 (org-trim
747 (org-e-ascii--indent-string
748 (org-e-ascii--fill-string
749 (let ((caption (org-element-property :caption table)))
750 (if (not caption) (org-element-property :name table)
751 ;; Use short name in priority, if available.
752 (org-export-secondary-string
753 (or (cdr caption) (car caption)) info)))
754 (- text-width (length initial-text)) info)
755 (length initial-text))))))
756 (org-export-collect-tables info) "\n")))))
758 (defun org-e-ascii--unique-links (element info)
759 "Return a list of unique link references in ELEMENT.
761 ELEMENT is either an headline element or a section element. INFO
762 is a plist used as a communication channel.
764 It covers links that may be found current headline's title, in
765 the following section and in any inlinetask's title there."
766 (let* (seen
767 (unique-link-p
768 (function
769 ;; Return LINK if it wasn't referenced so far, or nil.
770 ;; Update SEEN links along the way.
771 (lambda (link)
772 (let ((footprint
773 (cons (org-element-property :raw-link link)
774 (org-element-contents link))))
775 (unless (member footprint seen)
776 (push footprint seen) link)))))
777 (harvest-links-in-title
778 (function
779 ;; Return a list of all unique links in ELEMENT. ELEMENT
780 ;; may be an headline or an inlinetask element.
781 (lambda (element)
782 (let (acc)
783 (dolist (obj (org-element-property :title element) acc)
784 (when (eq (org-element-type obj) 'link)
785 (let ((link (funcall unique-link-p obj)))
786 (and link (push link acc)))))))))
787 ;; Retrieve HEADLINE's section, if it exists.
788 (section (if (eq (org-element-type element) 'section) element
789 (let ((sec (car (org-element-contents element))))
790 (and (eq (org-element-type sec) 'section) sec))))
791 (headline (if (eq (org-element-type element) 'headline) element
792 (org-export-get-parent-headline element info))))
793 (append
794 ;; Links that may be in HEADLINE's title.
795 (funcall harvest-links-in-title headline)
796 ;; Get all links in SECTION.
797 (org-element-map
798 section 'link (lambda (link) (funcall unique-link-p link)) info))))
800 (defun org-e-ascii--describe-links (links width info)
801 "Return a string describing a list of links.
803 LINKS is a list of link type objects, as returned by
804 `org-e-ascii--unique-links'. WIDTH is the text width allowed for
805 the output string. INFO is a plist used as a communication
806 channel."
807 (mapconcat
808 (lambda (link)
809 (let ((type (org-element-property :type link))
810 (anchor (let ((desc (org-element-contents link)))
811 (if (not desc) (org-element-property :raw-link link)
812 (org-export-secondary-string desc info)))))
813 (cond
814 ;; Coderefs, radio links and fuzzy links are ignored.
815 ((member type '("coderef" "radio" "fuzzy")) nil)
816 ;; Id and custom-id links: Headlines refer to their numbering.
817 ((member type '("custom-id" "id"))
818 (let ((dest (org-export-resolve-id-link link info)))
819 (concat
820 (org-e-ascii--fill-string
821 (format
822 "[%s] %s"
823 anchor
824 (if (not dest) (org-e-ascii--translate "Unknown reference" info)
825 (format
826 (org-e-ascii--translate "See section %s" info)
827 (mapconcat 'number-to-string
828 (org-export-get-headline-number dest info) "."))))
829 width info) "\n\n")))
830 ;; Do not add a link that cannot be resolved and doesn't have
831 ;; any description: destination is already visible in the
832 ;; paragraph.
833 ((not (org-element-contents link)) nil)
835 (concat
836 (org-e-ascii--fill-string
837 (format "[%s] %s" anchor (org-element-property :raw-link link))
838 width info)
839 "\n\n")))))
840 links ""))
844 ;;; Template
846 (defun org-e-ascii-template--document-title (info)
847 "Return document title, as a string.
848 INFO is a plist used as a communication channel."
849 (let ((text-width org-e-ascii-text-width)
850 (title (org-export-secondary-string (plist-get info :title) info))
851 (author (and (plist-get info :with-author)
852 (let ((auth (plist-get info :author)))
853 (and auth (org-export-secondary-string auth info)))))
854 (email (and (plist-get info :with-email)
855 (org-export-secondary-string (plist-get info :email) info)))
856 (date (plist-get info :date)))
857 ;; There are two types of title blocks depending on the presence
858 ;; of a title to display.
859 (if (string= title "")
860 ;; Title block without a title. DATE is positioned at the top
861 ;; right of the document, AUTHOR to the top left and EMAIL
862 ;; just below.
863 (cond
864 ((and (org-string-nw-p date) (org-string-nw-p author))
865 (concat
866 author
867 (make-string (- text-width (length date) (length author)) ? )
868 date
869 (when (org-string-nw-p email) (concat "\n" email))
870 "\n\n\n"))
871 ((and (org-string-nw-p date) (org-string-nw-p email))
872 (concat
873 email
874 (make-string (- text-width (length date) (length email)) ? )
875 date "\n\n\n"))
876 ((org-string-nw-p date)
877 (concat
878 (org-e-ascii--justify-string date text-width 'right)
879 "\n\n\n"))
880 ((and (org-string-nw-p author) (org-string-nw-p email))
881 (concat author "\n" email "\n\n\n"))
882 ((org-string-nw-p author) (concat author "\n\n\n"))
883 ((org-string-nw-p email) (concat email "\n\n\n")))
884 ;; Title block with a title. Document's TITLE, along with the
885 ;; AUTHOR and its EMAIL are both overlined and an underlined,
886 ;; centered. Date is just below, also centered.
887 (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))
888 ;; Format TITLE. It may be filled if it is too wide,
889 ;; that is wider than the two thirds of the total width.
890 (title-len (min (length title) (/ (* 2 text-width) 3)))
891 (formatted-title (org-e-ascii--fill-string title title-len info))
892 (line
893 (make-string
894 (min (+ (max title-len (length author) (length email)) 2)
895 text-width) (if utf8p ?━ ?_))))
896 (org-e-ascii--justify-string
897 (concat line "\n"
898 (unless utf8p "\n")
899 (upcase formatted-title)
900 (cond
901 ((and (org-string-nw-p author) (org-string-nw-p email))
902 (concat (if utf8p "\n\n\n" "\n\n") author "\n" email))
903 ((org-string-nw-p author)
904 (concat (if utf8p "\n\n\n" "\n\n") author))
905 ((org-string-nw-p email)
906 (concat (if utf8p "\n\n\n" "\n\n") email)))
907 "\n" line
908 (when (org-string-nw-p date) (concat "\n\n\n" date))
909 "\n\n\n") text-width 'center)))))
911 (defun org-e-ascii-template (contents info)
912 "Return complete document string after ASCII conversion.
913 CONTENTS is the transcoded contents string. INFO is a plist
914 holding export options."
915 (org-element-normalize-string
916 (org-e-ascii--indent-string
917 (let ((text-width (- org-e-ascii-text-width org-e-ascii-global-margin)))
918 ;; 1. Build title block.
919 (concat
920 (org-e-ascii-template--document-title info)
921 ;; 2. Table of contents.
922 (let ((depth (plist-get info :with-toc)))
923 (when depth
924 (concat
925 (org-e-ascii--build-toc info (and (wholenump depth) depth))
926 "\n\n\n")))
927 ;; 3. Document's body.
928 contents
929 ;; 4. Footnote definitions.
930 (let ((definitions (org-export-collect-footnote-definitions
931 (plist-get info :parse-tree) info))
932 ;; Insert full links right inside the footnote definition
933 ;; as they have no chance to be inserted later.
934 (org-e-ascii-links-to-notes nil))
935 (when definitions
936 (concat
937 "\n\n\n"
938 (let ((title (org-e-ascii--translate "Footnotes\n" info)))
939 (concat
940 title
941 (make-string
942 (1- (length title))
943 (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))))
944 "\n\n"
945 (mapconcat
946 (lambda (ref)
947 (let ((id (format "[%s] " (car ref))))
948 ;; Distinguish between inline definitions and
949 ;; full-fledged definitions.
950 (org-trim
951 (let ((def (nth 2 ref)))
952 (if (eq (org-element-type def) 'org-data)
953 ;; Full-fledged definition: footnote ID is
954 ;; inserted inside the first parsed paragraph
955 ;; (FIRST), if any, to be sure filling will
956 ;; take it into consideration.
957 (let ((first (car (org-element-contents def))))
958 (if (not (eq (org-element-type first) 'paragraph))
959 (concat id "\n" (org-export-data def info))
960 (push id (nthcdr 2 first))
961 (org-export-data def info)))
962 ;; Fill paragraph once footnote ID is inserted in
963 ;; order to have a correct length for first line.
964 (org-e-ascii--fill-string
965 (concat id (org-export-secondary-string def info))
966 text-width info))))))
967 definitions "\n\n"))))
968 ;; 5. Creator. Ignore `comment' value as there are no comments in
969 ;; ASCII. Justify it to the bottom right.
970 (let ((creator-info (plist-get info :with-creator)))
971 (unless (or (not creator-info) (eq creator-info 'comment))
972 (concat
973 "\n\n\n"
974 (org-e-ascii--fill-string
975 (plist-get info :creator) text-width info 'right))))))
976 org-e-ascii-global-margin)))
978 (defun org-e-ascii--translate (s info)
979 "Translate string S.
981 INFO is a plist used as a communication channel.
983 Translation depends on `:language' property and allowed charset.
984 If no translation in found for a given language and a given
985 charset, fall-back to S."
986 (let* ((charset (intern (format ":%s" (plist-get info :ascii-charset))))
987 (lang (plist-get info :language))
988 (translations (cdr (assoc s org-e-ascii-dictionary))))
989 (or (plist-get (cdr (assoc lang translations)) charset) s)))
993 ;;; Transcode Functions
995 ;;;; Babel Call
997 ;; Babel Calls are ignored.
1000 ;;;; Bold
1002 (defun org-e-ascii-bold (bold contents info)
1003 "Transcode BOLD from Org to ASCII.
1004 CONTENTS is the text with bold markup. INFO is a plist holding
1005 contextual information."
1006 (format "*%s*" contents))
1009 ;;;; Center Block
1011 (defun org-e-ascii-center-block (center-block contents info)
1012 "Transcode a CENTER-BLOCK element from Org to ASCII.
1013 CONTENTS holds the contents of the block. INFO is a plist
1014 holding contextual information."
1015 (org-e-ascii--justify-string
1016 contents (org-e-ascii--current-text-width center-block info) 'center))
1019 ;;;; Clock
1021 (defun org-e-ascii-clock (clock contents info)
1022 "Transcode a CLOCK object from Org to ASCII.
1023 CONTENTS is nil. INFO is a plist holding contextual
1024 information."
1025 (concat org-clock-string " "
1026 (org-translate-time (org-element-property :value clock))
1027 (let ((time (org-element-property :time clock)))
1028 (and time
1029 (concat " => "
1030 (apply 'format
1031 "%2s:%02s"
1032 (org-split-string time ":")))))))
1035 ;;;; Code
1037 (defun org-e-ascii-code (code contents info)
1038 "Return a CODE object from Org to ASCII.
1039 CONTENTS is nil. INFO is a plist holding contextual
1040 information."
1041 (format org-e-ascii-verbatim-format (org-element-property :value code)))
1044 ;;;; Comment
1046 ;; Comments are ignored.
1049 ;;;; Comment Block
1051 ;; Comment Blocks are ignored.
1054 ;;;; Drawer
1056 (defun org-e-ascii-drawer (drawer contents info)
1057 "Transcode a DRAWER element from Org to ASCII.
1058 CONTENTS holds the contents of the block. INFO is a plist
1059 holding contextual information."
1060 (let ((name (org-element-property :drawer-name drawer))
1061 (width (org-e-ascii--current-text-width drawer info)))
1062 (if (functionp org-e-ascii-format-drawer-function)
1063 (funcall org-e-ascii-format-drawer-function name contents width)
1064 ;; If there's no user defined function: simply
1065 ;; display contents of the drawer.
1066 contents)))
1069 ;;;; Dynamic Block
1071 (defun org-e-ascii-dynamic-block (dynamic-block contents info)
1072 "Transcode a DYNAMIC-BLOCK element from Org to ASCII.
1073 CONTENTS holds the contents of the block. INFO is a plist
1074 holding contextual information. See `org-export-data'."
1075 contents)
1078 ;;;; Entity
1080 (defun org-e-ascii-entity (entity contents info)
1081 "Transcode an ENTITY object from Org to ASCII.
1082 CONTENTS are the definition itself. INFO is a plist holding
1083 contextual information."
1084 (org-element-property
1085 (intern (concat ":" (symbol-name (plist-get info :ascii-charset))))
1086 entity))
1089 ;;;; Example Block
1091 (defun org-e-ascii-example-block (example-block contents info)
1092 "Transcode a EXAMPLE-BLOCK element from Org to ASCII.
1093 CONTENTS is nil. INFO is a plist holding contextual information."
1094 (org-e-ascii--box-string
1095 (org-export-format-code-default example-block info) info))
1098 ;;;; Export Snippet
1100 (defun org-e-ascii-export-snippet (export-snippet contents info)
1101 "Transcode a EXPORT-SNIPPET object from Org to ASCII.
1102 CONTENTS is nil. INFO is a plist holding contextual information."
1103 (when (eq (org-export-snippet-backend export-snippet) 'e-ascii)
1104 (org-element-property :value export-snippet)))
1107 ;;;; Export Block
1109 (defun org-e-ascii-export-block (export-block contents info)
1110 "Transcode a EXPORT-BLOCK element from Org to ASCII.
1111 CONTENTS is nil. INFO is a plist holding contextual information."
1112 (when (string= (org-element-property :type export-block) "ascii")
1113 (org-remove-indentation (org-element-property :value export-block))))
1116 ;;;; Fixed Width
1118 (defun org-e-ascii-fixed-width (fixed-width contents info)
1119 "Transcode a FIXED-WIDTH element from Org to ASCII.
1120 CONTENTS is nil. INFO is a plist holding contextual information."
1121 (org-e-ascii--box-string
1122 (replace-regexp-in-string
1123 "^[ \t]*: ?" "" (org-element-property :value fixed-width)) info))
1126 ;;;; Footnote Definition
1128 ;; Footnote Definitions are ignored. They are compiled at the end of
1129 ;; the document, by `org-e-ascii-template'.
1132 ;;;; Footnote Reference
1134 (defun org-e-ascii-footnote-reference (footnote-reference contents info)
1135 "Transcode a FOOTNOTE-REFERENCE element from Org to ASCII.
1136 CONTENTS is nil. INFO is a plist holding contextual information."
1137 (format "[%s]" (org-export-get-footnote-number footnote-reference info)))
1140 ;;;; Headline
1142 (defun org-e-ascii-headline (headline contents info)
1143 "Transcode an HEADLINE element from Org to ASCII.
1144 CONTENTS holds the contents of the headline. INFO is a plist
1145 holding contextual information."
1146 ;; Don't export footnote section, which will be handled at the end
1147 ;; of the template.
1148 (unless (org-element-property :footnote-section-p headline)
1149 (let* ((low-level-rank (org-export-low-level-p headline info))
1150 (width (org-e-ascii--current-text-width headline info))
1151 ;; Blank lines between headline and its contents.
1152 ;; `org-e-ascii-headline-spacing', when set, overwrites
1153 ;; original buffer's spacing.
1154 (pre-blanks
1155 (make-string
1156 (if org-e-ascii-headline-spacing (car org-e-ascii-headline-spacing)
1157 (org-element-property :pre-blank headline)) ?\n))
1158 ;; Even if HEADLINE has no section, there might be some
1159 ;; links in its title that we shouldn't forget to describe.
1160 (links
1161 (unless (eq (caar (org-element-contents headline)) 'section)
1162 (org-e-ascii--describe-links
1163 (org-e-ascii--unique-links headline info) width info))))
1164 ;; Deep subtree: export it as a list item.
1165 (if low-level-rank
1166 (concat
1167 ;; Bullet.
1168 (let ((bullets (cdr (assq (plist-get info :ascii-charset)
1169 org-e-ascii-bullets))))
1170 (char-to-string
1171 (nth (mod (1- low-level-rank) (length bullets)) bullets)))
1173 ;; Title.
1174 (org-e-ascii--build-title headline info width) "\n"
1175 ;; Contents, indented by length of bullet.
1176 pre-blanks
1177 (org-e-ascii--indent-string
1178 (concat contents
1179 (when (org-string-nw-p links) (concat "\n\n" links)))
1181 ;; Else: Standard headline.
1182 (concat
1183 (org-e-ascii--build-title headline info width 'underline)
1184 "\n" pre-blanks
1185 (concat (when (org-string-nw-p links) links) contents))))))
1188 ;;;; Horizontal Rule
1190 (defun org-e-ascii-horizontal-rule (horizontal-rule contents info)
1191 "Transcode an HORIZONTAL-RULE object from Org to ASCII.
1192 CONTENTS is nil. INFO is a plist holding contextual
1193 information."
1194 (let ((attr
1195 (read
1196 (format
1197 "(%s)"
1198 (mapconcat
1199 #'identity
1200 (org-element-property :attr_ascii horizontal-rule)
1201 " ")))))
1202 (make-string (or (and (wholenump (plist-get attr :width))
1203 (plist-get attr :width))
1204 (org-e-ascii--current-text-width horizontal-rule info))
1205 (if (eq (plist-get info :ascii-charset) 'utf-8) ?― ?-))))
1208 ;;;; Inline Babel Call
1210 ;; Inline Babel Calls are ignored.
1213 ;;;; Inline Src Block
1215 (defun org-e-ascii-inline-src-block (inline-src-block contents info)
1216 "Transcode an INLINE-SRC-BLOCK element from Org to ASCII.
1217 CONTENTS holds the contents of the item. INFO is a plist holding
1218 contextual information."
1219 (format org-e-ascii-verbatim-format
1220 (org-element-property :value inline-src-block)))
1223 ;;;; Inlinetask
1225 (defun org-e-ascii-inlinetask (inlinetask contents info)
1226 "Transcode an INLINETASK element from Org to ASCII.
1227 CONTENTS holds the contents of the block. INFO is a plist
1228 holding contextual information."
1229 (let ((width (org-e-ascii--current-text-width inlinetask info))
1230 (title (org-export-secondary-string
1231 (org-element-property :title inlinetask) info))
1232 (todo (and (plist-get info :with-todo-keywords)
1233 (let ((todo (org-element-property
1234 :todo-keyword inlinetask)))
1235 (and todo
1236 (org-export-secondary-string todo info)))))
1237 (todo-type (org-element-property :todo-type inlinetask))
1238 (tags (and (plist-get info :with-tags)
1239 (org-element-property :tags inlinetask)))
1240 (priority (and (plist-get info :with-priority)
1241 (org-element-property :priority inlinetask))))
1242 ;; If `org-e-ascii-format-inlinetask-function' is provided, call it
1243 ;; with appropriate arguments.
1244 (if (functionp org-e-ascii-format-inlinetask-function)
1245 (funcall org-e-ascii-format-inlinetask-function
1246 todo todo-type priority title tags contents width)
1247 ;; Otherwise, use a default template.
1248 (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
1249 (org-e-ascii--indent-string
1250 (concat
1251 ;; Top line, with an additional blank line if not in UTF-8.
1252 (make-string width (if utf8p ?━ ?_)) "\n"
1253 (unless utf8p (concat (make-string width ? ) "\n"))
1254 ;; Add title. Fill it if wider than inlinetask.
1255 (let ((title (org-e-ascii--build-title inlinetask info width)))
1256 (if (<= (length title) width) title
1257 (org-e-ascii--fill-string title width info)))
1258 "\n"
1259 ;; If CONTENTS is not empty, insert it along with
1260 ;; a separator.
1261 (when (org-string-nw-p contents)
1262 (concat (make-string width (if utf8p ?─ ?-)) "\n" contents))
1263 ;; Bottom line.
1264 (make-string width (if utf8p ?━ ?_)))
1265 ;; Flush the inlinetask to the right.
1266 (- org-e-ascii-text-width org-e-ascii-global-margin
1267 (if (not (org-export-get-parent-headline inlinetask info)) 0
1268 org-e-ascii-inner-margin)
1269 (org-e-ascii--current-text-width inlinetask info)))))))
1271 ;;;; Italic
1273 (defun org-e-ascii-italic (italic contents info)
1274 "Transcode italic from Org to ASCII.
1275 CONTENTS is the text with italic markup. INFO is a plist holding
1276 contextual information."
1277 (format "/%s/" contents))
1280 ;;;; Item
1282 (defun org-e-ascii-item (item contents info)
1283 "Transcode an ITEM element from Org to ASCII.
1284 CONTENTS holds the contents of the item. INFO is a plist holding
1285 contextual information."
1286 (let ((bullet
1287 ;; First parent of ITEM is always the plain-list. Get
1288 ;; `:type' property from it.
1289 (org-list-bullet-string
1290 (case (org-element-property :type (org-export-get-parent item info))
1291 (descriptive
1292 (concat
1293 (org-export-secondary-string
1294 (org-element-property :tag item) info)
1295 ": "))
1296 (ordered
1297 ;; Return correct number for ITEM, paying attention to
1298 ;; counters.
1299 (let* ((struct (org-element-property :structure item))
1300 (bul (org-element-property :bullet item))
1301 (num
1302 (number-to-string
1303 (car (last (org-list-get-item-number
1304 (org-element-property :begin item)
1305 struct
1306 (org-list-prevs-alist struct)
1307 (org-list-parents-alist struct)))))))
1308 (replace-regexp-in-string "[0-9]+" num bul)))
1309 (t (let ((bul (org-element-property :bullet item)))
1310 ;; Change bullets into more visible form if UTF-8 is active.
1311 (if (not (eq (plist-get info :ascii-charset) 'utf-8)) bul
1312 (replace-regexp-in-string
1313 "-" "•"
1314 (replace-regexp-in-string
1315 "+" "⁃"
1316 (replace-regexp-in-string "*" "‣" bul))))))))))
1317 (concat
1318 bullet
1319 ;; Contents: Pay attention to indentation. Note: check-boxes are
1320 ;; already taken care of at the paragraph level so they don't
1321 ;; interfere with indentation.
1322 (let ((contents (org-e-ascii--indent-string contents (length bullet))))
1323 (if (eq (caar (org-element-contents item)) 'paragraph)
1324 (org-trim contents)
1325 (concat "\n" contents))))))
1328 ;;;; Keyword
1330 (defun org-e-ascii-keyword (keyword contents info)
1331 "Transcode a KEYWORD element from Org to ASCII.
1332 CONTENTS is nil. INFO is a plist holding contextual
1333 information."
1334 (let ((key (org-element-property :key keyword))
1335 (value (org-element-property :value keyword)))
1336 (cond
1337 ((string= key "ASCII") value)
1338 ((string= key "TOC")
1339 (let ((value (downcase value)))
1340 (cond
1341 ((string-match "\\<headlines\\>" value)
1342 (let ((depth (or (and (string-match "[0-9]+" value)
1343 (string-to-number (match-string 0 value)))
1344 (plist-get info :with-toc))))
1345 (org-e-ascii--build-toc
1346 info (and (wholenump depth) depth) keyword)))
1347 ((string= "tables" value)
1348 (org-e-ascii--list-tables keyword info))
1349 ((string= "listings" value)
1350 (org-e-ascii--list-listings keyword info))))))))
1353 ;;;; Latex Environment
1355 (defun org-e-ascii-latex-environment (latex-environment contents info)
1356 "Transcode a LATEX-ENVIRONMENT element from Org to ASCII.
1357 CONTENTS is nil. INFO is a plist holding contextual
1358 information."
1359 (org-remove-indentation (org-element-property :value latex-environment)))
1362 ;;;; Latex Fragment
1364 (defun org-e-ascii-latex-fragment (latex-fragment contents info)
1365 "Transcode a LATEX-FRAGMENT object from Org to ASCII.
1366 CONTENTS is nil. INFO is a plist holding contextual
1367 information."
1368 (org-element-property :value latex-fragment))
1371 ;;;; Line Break
1373 (defun org-e-ascii-line-break (line-break contents info)
1374 "Transcode a LINE-BREAK object from Org to ASCII.
1375 CONTENTS is nil. INFO is a plist holding contextual
1376 information." hard-newline)
1379 ;;;; Link
1381 (defun org-e-ascii-link (link desc info)
1382 "Transcode a LINK object from Org to ASCII.
1384 DESC is the description part of the link, or the empty string.
1385 INFO is a plist holding contextual information."
1386 (let ((raw-link (org-element-property :raw-link link))
1387 (type (org-element-property :type link)))
1388 (cond
1389 ((string= type "coderef")
1390 (let ((ref (org-element-property :path link)))
1391 (format (org-export-get-coderef-format ref desc)
1392 (org-export-resolve-coderef ref info))))
1393 ;; Do not apply a special syntax on radio links. Though, parse
1394 ;; and transcode path to have a proper display of contents.
1395 ((string= type "radio")
1396 (org-export-secondary-string
1397 (org-element-parse-secondary-string
1398 (org-element-property :path link)
1399 (cdr (assq 'radio-target org-element-object-restrictions)))
1400 info))
1401 ;; Do not apply a special syntax on fuzzy links pointing to
1402 ;; targets.
1403 ((string= type "fuzzy")
1404 (let ((destination (org-export-resolve-fuzzy-link link info)))
1405 ;; Ignore invisible "#+target: path".
1406 (unless (eq (org-element-type destination) 'keyword)
1407 (if (org-string-nw-p desc) desc
1408 (when destination
1409 (let ((number (org-export-get-ordinal destination info)))
1410 (when number
1411 (if (atom number) (number-to-string number)
1412 (mapconcat 'number-to-string number ".")))))))))
1414 (if (not (org-string-nw-p desc)) (format "[%s]" raw-link)
1415 (concat
1416 (format "[%s]" desc)
1417 (unless org-e-ascii-links-to-notes (format " (%s)" raw-link))))))))
1420 ;;;; Macro
1422 (defun org-e-ascii-macro (macro contents info)
1423 "Transcode a MACRO element from Org to ASCII.
1424 CONTENTS is nil. INFO is a plist holding contextual
1425 information."
1426 (org-export-expand-macro macro info))
1429 ;;;; Paragraph
1431 (defun org-e-ascii-paragraph (paragraph contents info)
1432 "Transcode a PARAGRAPH element from Org to ASCII.
1433 CONTENTS is the contents of the paragraph, as a string. INFO is
1434 the plist used as a communication channel."
1435 (org-e-ascii--fill-string
1436 (let ((parent (org-export-get-parent paragraph info)))
1437 ;; If PARAGRAPH is the first one in a list element, be sure to
1438 ;; add the check-box in front of it, before any filling. Later,
1439 ;; it would interfere with line width.
1440 (if (and (eq (org-element-type parent) 'item)
1441 (equal (car (org-element-contents parent)) paragraph))
1442 (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
1443 (concat (case (org-element-property :checkbox parent)
1444 (on (if utf8p "☑ " "[X] "))
1445 (off (if utf8p "☐ " "[ ] "))
1446 (trans (if utf8p "☒ " "[-] ")))
1447 contents))
1448 contents))
1449 (org-e-ascii--current-text-width paragraph info) info))
1452 ;;;; Plain List
1454 (defun org-e-ascii-plain-list (plain-list contents info)
1455 "Transcode a PLAIN-LIST element from Org to ASCII.
1456 CONTENTS is the contents of the list. INFO is a plist holding
1457 contextual information."
1458 contents)
1461 ;;;; Plain Text
1463 (defun org-e-ascii-plain-text (text info)
1464 "Transcode a TEXT string from Org to ASCII.
1465 INFO is a plist used as a communication channel."
1466 (if (not (and (eq (plist-get info :ascii-charset) 'utf-8)
1467 (plist-get info :with-special-strings)))
1468 text
1469 ;; Usual replacements in utf-8 with proper option set.
1470 (replace-regexp-in-string
1471 "\\.\\.\\." "…"
1472 (replace-regexp-in-string
1473 "--" "–"
1474 (replace-regexp-in-string "---" "—" text)))))
1477 ;;;; Planning
1479 (defun org-e-ascii-planning (planning contents info)
1480 "Transcode a PLANNING element from Org to ASCII.
1481 CONTENTS is nil. INFO is a plist used as a communication
1482 channel."
1483 (mapconcat
1484 'identity
1485 (delq nil
1486 (list (let ((closed (org-element-property :closed planning)))
1487 (when closed (concat org-closed-string " "
1488 (org-translate-time closed))))
1489 (let ((deadline (org-element-property :deadline planning)))
1490 (when deadline (concat org-deadline-string " "
1491 (org-translate-time deadline))))
1492 (let ((scheduled (org-element-property :scheduled planning)))
1493 (when scheduled (concat org-scheduled-string " "
1494 (org-translate-time scheduled))))))
1495 " "))
1498 ;;;; Property Drawer
1500 ;; Property drawers are ignored.
1503 ;;;; Quote Block
1505 (defun org-e-ascii-quote-block (quote-block contents info)
1506 "Transcode a QUOTE-BLOCK element from Org to ASCII.
1507 CONTENTS holds the contents of the block. INFO is a plist
1508 holding contextual information."
1509 (let ((width (org-e-ascii--current-text-width quote-block info)))
1510 (org-e-ascii--indent-string
1511 (org-remove-indentation
1512 (org-e-ascii--fill-string contents width info))
1513 org-e-ascii-quote-margin)))
1516 ;;;; Quote Section
1518 (defun org-e-ascii-quote-section (quote-section contents info)
1519 "Transcode a QUOTE-SECTION element from Org to ASCII.
1520 CONTENTS is nil. INFO is a plist holding contextual information."
1521 (let ((width (org-e-ascii--current-text-width quote-section info))
1522 (value
1523 (org-export-secondary-string
1524 (org-remove-indentation
1525 (org-element-property :value quote-section))
1526 info)))
1527 (org-e-ascii--indent-string
1528 value
1529 (+ org-e-ascii-quote-margin
1530 ;; Don't apply inner margin if parent headline is low level.
1531 (let ((headline (org-export-get-parent-headline quote-section info)))
1532 (if (org-export-low-level-p headline info) 0
1533 org-e-ascii-inner-margin))))))
1536 ;;;; Radio Target
1538 (defun org-e-ascii-radio-target (radio-target contents info)
1539 "Transcode a RADIO-TARGET object from Org to ASCII.
1540 CONTENTS is the contents of the target. INFO is a plist holding
1541 contextual information."
1542 contents)
1544 ;;;; Section
1546 (defun org-e-ascii-section (section contents info)
1547 "Transcode a SECTION element from Org to ASCII.
1548 CONTENTS is the contents of the section. INFO is a plist holding
1549 contextual information."
1550 (org-e-ascii--indent-string
1551 (concat
1552 contents
1553 (when org-e-ascii-links-to-notes
1554 ;; Add list of links at the end of SECTION.
1555 (let ((links (org-e-ascii--describe-links
1556 (org-e-ascii--unique-links section info)
1557 (org-e-ascii--current-text-width section info) info)))
1558 ;; Separate list of links and section contents.
1559 (when (org-string-nw-p links) (concat "\n\n" links)))))
1560 ;; Do not apply inner margin if parent headline is low level.
1561 (let ((headline (org-export-get-parent-headline section info)))
1562 (if (or (not headline) (org-export-low-level-p headline info)) 0
1563 org-e-ascii-inner-margin))))
1566 ;;;; Special Block
1568 (defun org-e-ascii-special-block (special-block contents info)
1569 "Transcode a SPECIAL-BLOCK element from Org to ASCII.
1570 CONTENTS holds the contents of the block. INFO is a plist
1571 holding contextual information."
1572 contents)
1575 ;;;; Src Block
1577 (defun org-e-ascii-src-block (src-block contents info)
1578 "Transcode a SRC-BLOCK element from Org to ASCII.
1579 CONTENTS holds the contents of the item. INFO is a plist holding
1580 contextual information."
1581 (let ((caption (org-e-ascii--build-caption src-block info)))
1582 (concat
1583 (when (and caption org-e-ascii-caption-above) (concat caption "\n"))
1584 (org-e-ascii--box-string
1585 (org-export-format-code-default src-block info) info)
1586 (when (and caption (not org-e-ascii-caption-above))
1587 (concat "\n" caption)))))
1589 ;;;; Statistics Cookie
1591 (defun org-e-ascii-statistics-cookie (statistics-cookie contents info)
1592 "Transcode a STATISTICS-COOKIE object from Org to ASCII.
1593 CONTENTS is nil. INFO is a plist holding contextual information."
1594 (org-element-property :value statistics-cookie))
1597 ;;;; Subscript
1599 (defun org-e-ascii-subscript (subscript contents info)
1600 "Transcode a SUBSCRIPT object from Org to ASCII.
1601 CONTENTS is the contents of the object. INFO is a plist holding
1602 contextual information."
1603 (if (org-element-property :use-brackets-p subscript)
1604 (format "_{%s}" contents)
1605 (format "_%s" contents)))
1608 ;;;; Superscript
1610 (defun org-e-ascii-superscript (superscript contents info)
1611 "Transcode a SUPERSCRIPT object from Org to ASCII.
1612 CONTENTS is the contents of the object. INFO is a plist holding
1613 contextual information."
1614 (if (org-element-property :use-brackets-p superscript)
1615 (format "_{%s}" contents)
1616 (format "_%s" contents)))
1619 ;;;; Strike-through
1621 (defun org-e-ascii-strike-through (strike-through contents info)
1622 "Transcode STRIKE-THROUGH from Org to ASCII.
1623 CONTENTS is text with strike-through markup. INFO is a plist
1624 holding contextual information."
1625 (format "+%s+" contents))
1628 ;;;; Table
1630 (defun org-e-ascii-table (table contents info)
1631 "Transcode a TABLE element from Org to ASCII.
1632 CONTENTS is nil. INFO is a plist holding contextual information."
1633 (let ((caption (org-e-ascii--build-caption table info)))
1634 (concat
1635 ;; Possibly add a caption string above.
1636 (when (and caption org-e-ascii-caption-above) (concat caption "\n"))
1637 ;; Insert table. Note: "table.el" tables are left unmodified.
1638 (if (eq (org-element-property :type table) 'org) contents
1639 (org-remove-indentation (org-element-property :value table)))
1640 ;; Possible add a caption string below.
1641 (when (and caption (not org-e-ascii-caption-above))
1642 (concat "\n" caption)))))
1645 ;;;; Table Cell
1647 (defun org-e-ascii--table-cell-width (table-cell info)
1648 "Return width of TABLE-CELL.
1650 Width of a cell is determined either by a width cookie in the
1651 same column as the cell, or by the length of its contents.
1653 When `org-e-ascii-table-widen-columns' is non-nil, width cookies
1654 are ignored. "
1655 (or (and (not org-e-ascii-table-widen-columns)
1656 (org-export-table-cell-width table-cell info))
1657 (let* ((max-width 0)
1658 (table (org-export-get-parent-table table-cell info))
1659 (specialp (org-export-table-has-special-column-p table))
1660 (col (cdr (org-export-table-cell-address table-cell info))))
1661 (org-element-map
1662 table 'table-row
1663 (lambda (row)
1664 (setq max-width
1665 (max (length
1666 (org-export-data
1667 (elt (if specialp (car (org-element-contents row))
1668 (org-element-contents row))
1669 col)
1670 info))
1671 max-width))))
1672 max-width)))
1674 (defun org-e-ascii-table-cell (table-cell contents info)
1675 "Transcode a TABLE-CELL object from Org to ASCII.
1676 CONTENTS is the cell contents. INFO is a plist used as
1677 a communication channel."
1678 ;; Determine column width. When `org-e-ascii-table-widen-columns'
1679 ;; is nil and some width cookie has set it, use that value.
1680 ;; Otherwise, compute the maximum width among transcoded data of
1681 ;; each cell in the column.
1682 (let ((width (org-e-ascii--table-cell-width table-cell info)))
1683 ;; When contents are too large, truncate them.
1684 (unless (or org-e-ascii-table-widen-columns (<= (length contents) width))
1685 (setq contents (concat (substring contents 0 (- width 2)) "=>")))
1686 ;; Align contents correctly within the cell.
1687 (let* ((indent-tabs-mode nil)
1688 (data
1689 (when contents
1690 (org-e-ascii--justify-string
1691 contents width
1692 (org-export-table-cell-alignment table-cell info)))))
1693 (setq contents (concat data (make-string (- width (length data)) ? ))))
1694 ;; Return cell.
1695 (concat (format " %s " contents)
1696 (when (memq 'right (org-export-table-cell-borders table-cell info))
1697 (if (eq (plist-get info :ascii-charset) 'utf-8) "│" "|")))))
1700 ;;;; Table Row
1702 (defun org-e-ascii-table-row (table-row contents info)
1703 "Transcode a TABLE-ROW element from Org to ASCII.
1704 CONTENTS is the row contents. INFO is a plist used as
1705 a communication channel."
1706 (when (eq (org-element-property :type table-row) 'standard)
1707 (let ((build-hline
1708 (function
1709 (lambda (lcorner horiz vert rcorner)
1710 (concat
1711 (apply
1712 'concat
1713 (org-element-map
1714 table-row 'table-cell
1715 (lambda (cell)
1716 (let ((width (org-e-ascii--table-cell-width cell info))
1717 (borders (org-export-table-cell-borders cell info)))
1718 (concat
1719 (when (and (memq 'left borders)
1720 (equal (org-element-map
1721 table-row 'table-cell 'identity info t)
1722 cell)))
1723 (make-string (+ 2 width) (string-to-char horiz))
1724 (cond
1725 ((not (memq 'right borders)) nil)
1726 ((equal (car (last (org-element-contents table-row)))
1727 cell)
1728 rcorner)
1729 (t vert)))))
1730 info)) "\n"))))
1731 (utf8p (eq (plist-get info :ascii-charset) 'utf-8))
1732 (borders (org-export-table-cell-borders
1733 (org-element-map table-row 'table-cell 'identity info t)
1734 info)))
1735 (concat (cond
1736 ((and (memq 'top borders) (or utf8p (memq 'above borders)))
1737 (if utf8p (funcall build-hline "┍" "━" "┯" "┑")
1738 (funcall build-hline "+" "-" "+" "+")))
1739 ((memq 'above borders)
1740 (if utf8p (funcall build-hline "├" "─" "┼" "┤")
1741 (funcall build-hline "+" "-" "+" "+"))))
1742 (when (memq 'left borders) (if utf8p "│" "|"))
1743 contents "\n"
1744 (when (and (memq 'bottom borders) (or utf8p (memq 'below borders)))
1745 (if utf8p (funcall build-hline "┕" "━" "┷" "┙")
1746 (funcall build-hline "+" "-" "+" "+")))))))
1749 ;;;; Target
1751 ;; Targets are invisible.
1754 ;;;; Timestamp
1756 (defun org-e-ascii-timestamp (timestamp contents info)
1757 "Transcode a TIMESTAMP object from Org to ASCII.
1758 CONTENTS is nil. INFO is a plist holding contextual information."
1759 (org-translate-time (org-element-property :value timestamp)))
1762 ;;;; Underline
1764 (defun org-e-ascii-underline (underline contents info)
1765 "Transcode UNDERLINE from Org to ASCII.
1766 CONTENTS is the text with underline markup. INFO is a plist
1767 holding contextual information."
1768 (format "_%s_" contents))
1771 ;;;; Verbatim
1773 (defun org-e-ascii-verbatim (verbatim contents info)
1774 "Return a VERBATIM object from Org to ASCII.
1775 CONTENTS is nil. INFO is a plist holding contextual information."
1776 (format org-e-ascii-verbatim-format
1777 (org-element-property :value verbatim)))
1780 ;;;; Verse Block
1782 (defun org-e-ascii-verse-block (verse-block contents info)
1783 "Transcode a VERSE-BLOCK element from Org to ASCII.
1784 CONTENTS is verse block contents. INFO is a plist holding
1785 contextual information."
1786 (let ((verse-width (org-e-ascii--current-text-width verse-block info)))
1787 (org-e-ascii--indent-string
1788 (org-e-ascii--justify-string contents verse-width 'left)
1789 org-e-ascii-quote-margin)))
1792 ;;; Filter
1794 (defun org-e-ascii-filter-headline-blank-lines (headline back-end info)
1795 "Filter controlling number of blank lines after an headline.
1797 HEADLINE is a string representing a transcoded headline.
1798 BACK-END is symbol specifying back-end used for export. INFO is
1799 plist containing the communication channel.
1801 This function only applies to `e-ascii' back-end. See
1802 `org-e-ascii-headline-spacing' for information.
1804 For any other back-end, HEADLINE is returned as-is."
1805 (if (not (and (eq back-end 'e-ascii) org-e-ascii-headline-spacing)) headline
1806 (let ((blanks (make-string (1+ (cdr org-e-ascii-headline-spacing)) ?\n)))
1807 (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline))))
1811 ;;; Interactive function
1813 (defun org-e-ascii-export-to-ascii
1814 (&optional subtreep visible-only body-only ext-plist pub-dir)
1815 "Export current buffer to a text file.
1817 If narrowing is active in the current buffer, only export its
1818 narrowed part.
1820 If a region is active, export that region.
1822 When optional argument SUBTREEP is non-nil, export the sub-tree
1823 at point, extracting information from the headline properties
1824 first.
1826 When optional argument VISIBLE-ONLY is non-nil, don't export
1827 contents of hidden elements.
1829 When optional argument BODY-ONLY is non-nil, strip title, table
1830 of contents and footnote definitions from output.
1832 EXT-PLIST, when provided, is a property list with external
1833 parameters overriding Org default settings, but still inferior to
1834 file-local settings.
1836 When optional argument PUB-DIR is set, use it as the publishing
1837 directory.
1839 Return output file's name."
1840 (interactive)
1841 (let ((outfile (org-export-output-file-name ".txt" subtreep pub-dir)))
1842 (org-export-to-file
1843 'e-ascii outfile subtreep visible-only body-only ext-plist)))
1846 (provide 'org-e-ascii)
1847 ;;; org-e-ascii.el ends here