commit | author | age
|
76bbd0
|
1 |
;;; ox-ascii.el --- ASCII Back-End for Org Export Engine -*- lexical-binding: t; -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2012-2018 Free Software Foundation, Inc. |
|
4 |
|
|
5 |
;; Author: Nicolas Goaziou <n.goaziou at gmail dot com> |
|
6 |
;; Keywords: outlines, hypermedia, calendar, wp |
|
7 |
|
|
8 |
;; This file is part of GNU Emacs. |
|
9 |
|
|
10 |
;; GNU Emacs is free software: you can redistribute it and/or modify |
|
11 |
;; it under the terms of the GNU General Public License as published by |
|
12 |
;; the Free Software Foundation, either version 3 of the License, or |
|
13 |
;; (at your option) any later version. |
|
14 |
|
|
15 |
;; GNU Emacs is distributed in the hope that it will be useful, |
|
16 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
17 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
18 |
;; GNU General Public License for more details. |
|
19 |
|
|
20 |
;; You should have received a copy of the GNU General Public License |
|
21 |
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. |
|
22 |
|
|
23 |
;;; Commentary: |
|
24 |
;; |
|
25 |
;; This library implements an ASCII back-end for Org generic exporter. |
|
26 |
;; See Org manual for more information. |
|
27 |
|
|
28 |
;;; Code: |
|
29 |
|
|
30 |
(require 'ox) |
|
31 |
(require 'ox-publish) |
|
32 |
(require 'cl-lib) |
|
33 |
|
|
34 |
(declare-function aa2u "ext:ascii-art-to-unicode" ()) |
|
35 |
|
|
36 |
;;; Define Back-End |
|
37 |
;; |
|
38 |
;; The following setting won't allow modifying preferred charset |
|
39 |
;; through a buffer keyword or an option item, but, since the property |
|
40 |
;; will appear in communication channel nonetheless, it allows |
|
41 |
;; overriding `org-ascii-charset' variable on the fly by the ext-plist |
|
42 |
;; mechanism. |
|
43 |
;; |
|
44 |
;; We also install a filter for headlines and sections, in order to |
|
45 |
;; control blank lines separating them in output string. |
|
46 |
|
|
47 |
(org-export-define-backend 'ascii |
|
48 |
'((bold . org-ascii-bold) |
|
49 |
(center-block . org-ascii-center-block) |
|
50 |
(clock . org-ascii-clock) |
|
51 |
(code . org-ascii-code) |
|
52 |
(drawer . org-ascii-drawer) |
|
53 |
(dynamic-block . org-ascii-dynamic-block) |
|
54 |
(entity . org-ascii-entity) |
|
55 |
(example-block . org-ascii-example-block) |
|
56 |
(export-block . org-ascii-export-block) |
|
57 |
(export-snippet . org-ascii-export-snippet) |
|
58 |
(fixed-width . org-ascii-fixed-width) |
|
59 |
(footnote-reference . org-ascii-footnote-reference) |
|
60 |
(headline . org-ascii-headline) |
|
61 |
(horizontal-rule . org-ascii-horizontal-rule) |
|
62 |
(inline-src-block . org-ascii-inline-src-block) |
|
63 |
(inlinetask . org-ascii-inlinetask) |
|
64 |
(inner-template . org-ascii-inner-template) |
|
65 |
(italic . org-ascii-italic) |
|
66 |
(item . org-ascii-item) |
|
67 |
(keyword . org-ascii-keyword) |
|
68 |
(latex-environment . org-ascii-latex-environment) |
|
69 |
(latex-fragment . org-ascii-latex-fragment) |
|
70 |
(line-break . org-ascii-line-break) |
|
71 |
(link . org-ascii-link) |
|
72 |
(node-property . org-ascii-node-property) |
|
73 |
(paragraph . org-ascii-paragraph) |
|
74 |
(plain-list . org-ascii-plain-list) |
|
75 |
(plain-text . org-ascii-plain-text) |
|
76 |
(planning . org-ascii-planning) |
|
77 |
(property-drawer . org-ascii-property-drawer) |
|
78 |
(quote-block . org-ascii-quote-block) |
|
79 |
(radio-target . org-ascii-radio-target) |
|
80 |
(section . org-ascii-section) |
|
81 |
(special-block . org-ascii-special-block) |
|
82 |
(src-block . org-ascii-src-block) |
|
83 |
(statistics-cookie . org-ascii-statistics-cookie) |
|
84 |
(strike-through . org-ascii-strike-through) |
|
85 |
(subscript . org-ascii-subscript) |
|
86 |
(superscript . org-ascii-superscript) |
|
87 |
(table . org-ascii-table) |
|
88 |
(table-cell . org-ascii-table-cell) |
|
89 |
(table-row . org-ascii-table-row) |
|
90 |
(target . org-ascii-target) |
|
91 |
(template . org-ascii-template) |
|
92 |
(timestamp . org-ascii-timestamp) |
|
93 |
(underline . org-ascii-underline) |
|
94 |
(verbatim . org-ascii-verbatim) |
|
95 |
(verse-block . org-ascii-verse-block)) |
|
96 |
:menu-entry |
|
97 |
'(?t "Export to Plain Text" |
|
98 |
((?A "As ASCII buffer" |
|
99 |
(lambda (a s v b) |
|
100 |
(org-ascii-export-as-ascii a s v b '(:ascii-charset ascii)))) |
|
101 |
(?a "As ASCII file" |
|
102 |
(lambda (a s v b) |
|
103 |
(org-ascii-export-to-ascii a s v b '(:ascii-charset ascii)))) |
|
104 |
(?L "As Latin1 buffer" |
|
105 |
(lambda (a s v b) |
|
106 |
(org-ascii-export-as-ascii a s v b '(:ascii-charset latin1)))) |
|
107 |
(?l "As Latin1 file" |
|
108 |
(lambda (a s v b) |
|
109 |
(org-ascii-export-to-ascii a s v b '(:ascii-charset latin1)))) |
|
110 |
(?U "As UTF-8 buffer" |
|
111 |
(lambda (a s v b) |
|
112 |
(org-ascii-export-as-ascii a s v b '(:ascii-charset utf-8)))) |
|
113 |
(?u "As UTF-8 file" |
|
114 |
(lambda (a s v b) |
|
115 |
(org-ascii-export-to-ascii a s v b '(:ascii-charset utf-8)))))) |
|
116 |
:filters-alist '((:filter-headline . org-ascii-filter-headline-blank-lines) |
|
117 |
(:filter-parse-tree org-ascii-filter-paragraph-spacing |
|
118 |
org-ascii-filter-comment-spacing) |
|
119 |
(:filter-section . org-ascii-filter-headline-blank-lines)) |
|
120 |
:options-alist |
|
121 |
'((:subtitle "SUBTITLE" nil nil parse) |
|
122 |
(:ascii-bullets nil nil org-ascii-bullets) |
|
123 |
(:ascii-caption-above nil nil org-ascii-caption-above) |
|
124 |
(:ascii-charset nil nil org-ascii-charset) |
|
125 |
(:ascii-global-margin nil nil org-ascii-global-margin) |
|
126 |
(:ascii-format-drawer-function nil nil org-ascii-format-drawer-function) |
|
127 |
(:ascii-format-inlinetask-function |
|
128 |
nil nil org-ascii-format-inlinetask-function) |
|
129 |
(:ascii-headline-spacing nil nil org-ascii-headline-spacing) |
|
130 |
(:ascii-indented-line-width nil nil org-ascii-indented-line-width) |
|
131 |
(:ascii-inlinetask-width nil nil org-ascii-inlinetask-width) |
|
132 |
(:ascii-inner-margin nil nil org-ascii-inner-margin) |
|
133 |
(:ascii-links-to-notes nil nil org-ascii-links-to-notes) |
|
134 |
(:ascii-list-margin nil nil org-ascii-list-margin) |
|
135 |
(:ascii-paragraph-spacing nil nil org-ascii-paragraph-spacing) |
|
136 |
(:ascii-quote-margin nil nil org-ascii-quote-margin) |
|
137 |
(:ascii-table-keep-all-vertical-lines |
|
138 |
nil nil org-ascii-table-keep-all-vertical-lines) |
|
139 |
(:ascii-table-use-ascii-art nil nil org-ascii-table-use-ascii-art) |
|
140 |
(:ascii-table-widen-columns nil nil org-ascii-table-widen-columns) |
|
141 |
(:ascii-text-width nil nil org-ascii-text-width) |
|
142 |
(:ascii-underline nil nil org-ascii-underline) |
|
143 |
(:ascii-verbatim-format nil nil org-ascii-verbatim-format))) |
|
144 |
|
|
145 |
|
|
146 |
|
|
147 |
;;; User Configurable Variables |
|
148 |
|
|
149 |
(defgroup org-export-ascii nil |
|
150 |
"Options for exporting Org mode files to ASCII." |
|
151 |
:tag "Org Export ASCII" |
|
152 |
:group 'org-export) |
|
153 |
|
|
154 |
(defcustom org-ascii-text-width 72 |
|
155 |
"Maximum width of exported text. |
|
156 |
This number includes margin size, as set in |
|
157 |
`org-ascii-global-margin'." |
|
158 |
:group 'org-export-ascii |
|
159 |
:version "24.4" |
|
160 |
:package-version '(Org . "8.0") |
|
161 |
:type 'integer) |
|
162 |
|
|
163 |
(defcustom org-ascii-global-margin 0 |
|
164 |
"Width of the left margin, in number of characters." |
|
165 |
:group 'org-export-ascii |
|
166 |
:version "24.4" |
|
167 |
:package-version '(Org . "8.0") |
|
168 |
:type 'integer) |
|
169 |
|
|
170 |
(defcustom org-ascii-inner-margin 2 |
|
171 |
"Width of the inner margin, in number of characters. |
|
172 |
Inner margin is applied between each headline." |
|
173 |
:group 'org-export-ascii |
|
174 |
:version "24.4" |
|
175 |
:package-version '(Org . "8.0") |
|
176 |
:type 'integer) |
|
177 |
|
|
178 |
(defcustom org-ascii-quote-margin 6 |
|
179 |
"Width of margin used for quoting text, in characters. |
|
180 |
This margin is applied on both sides of the text. It is also |
|
181 |
applied on the left side of contents in descriptive lists." |
|
182 |
:group 'org-export-ascii |
|
183 |
:version "24.4" |
|
184 |
:package-version '(Org . "8.0") |
|
185 |
:type 'integer) |
|
186 |
|
|
187 |
(defcustom org-ascii-list-margin 0 |
|
188 |
"Width of margin used for plain lists, in characters. |
|
189 |
This margin applies to top level list only, not to its |
|
190 |
sub-lists." |
|
191 |
:group 'org-export-ascii |
|
192 |
:version "26.1" |
|
193 |
:package-version '(Org . "8.3") |
|
194 |
:type 'integer) |
|
195 |
|
|
196 |
(defcustom org-ascii-inlinetask-width 30 |
|
197 |
"Width of inline tasks, in number of characters. |
|
198 |
This number ignores any margin." |
|
199 |
:group 'org-export-ascii |
|
200 |
:version "24.4" |
|
201 |
:package-version '(Org . "8.0") |
|
202 |
:type 'integer) |
|
203 |
|
|
204 |
(defcustom org-ascii-headline-spacing '(1 . 2) |
|
205 |
"Number of blank lines inserted around headlines. |
|
206 |
|
|
207 |
This variable can be set to a cons cell. In that case, its car |
|
208 |
represents the number of blank lines present before headline |
|
209 |
contents whereas its cdr reflects the number of blank lines after |
|
210 |
contents. |
|
211 |
|
|
212 |
A nil value replicates the number of blank lines found in the |
|
213 |
original Org buffer at the same place." |
|
214 |
:group 'org-export-ascii |
|
215 |
:version "24.4" |
|
216 |
:package-version '(Org . "8.0") |
|
217 |
:type '(choice |
|
218 |
(const :tag "Replicate original spacing" nil) |
|
219 |
(cons :tag "Set a uniform spacing" |
|
220 |
(integer :tag "Number of blank lines before contents") |
|
221 |
(integer :tag "Number of blank lines after contents")))) |
|
222 |
|
|
223 |
(defcustom org-ascii-indented-line-width 'auto |
|
224 |
"Additional indentation width for the first line in a paragraph. |
|
225 |
If the value is an integer, indent the first line of each |
|
226 |
paragraph by this width, unless it is located at the beginning of |
|
227 |
a section, in which case indentation is removed from that line. |
|
228 |
If it is the symbol `auto' preserve indentation from original |
|
229 |
document." |
|
230 |
:group 'org-export-ascii |
|
231 |
:version "24.4" |
|
232 |
:package-version '(Org . "8.0") |
|
233 |
:type '(choice |
|
234 |
(integer :tag "Number of white spaces characters") |
|
235 |
(const :tag "Preserve original width" auto))) |
|
236 |
|
|
237 |
(defcustom org-ascii-paragraph-spacing 'auto |
|
238 |
"Number of white lines between paragraphs. |
|
239 |
If the value is an integer, add this number of blank lines |
|
240 |
between contiguous paragraphs. If is it the symbol `auto', keep |
|
241 |
the same number of blank lines as in the original document." |
|
242 |
:group 'org-export-ascii |
|
243 |
:version "24.4" |
|
244 |
:package-version '(Org . "8.0") |
|
245 |
:type '(choice |
|
246 |
(integer :tag "Number of blank lines") |
|
247 |
(const :tag "Preserve original spacing" auto))) |
|
248 |
|
|
249 |
(defcustom org-ascii-charset 'ascii |
|
250 |
"The charset allowed to represent various elements and objects. |
|
251 |
Possible values are: |
|
252 |
`ascii' Only use plain ASCII characters |
|
253 |
`latin1' Include Latin-1 characters |
|
254 |
`utf-8' Use all UTF-8 characters" |
|
255 |
:group 'org-export-ascii |
|
256 |
:version "24.4" |
|
257 |
:package-version '(Org . "8.0") |
|
258 |
:type '(choice |
|
259 |
(const :tag "ASCII" ascii) |
|
260 |
(const :tag "Latin-1" latin1) |
|
261 |
(const :tag "UTF-8" utf-8))) |
|
262 |
|
|
263 |
(defcustom org-ascii-underline '((ascii ?= ?~ ?-) |
|
264 |
(latin1 ?= ?~ ?-) |
|
265 |
(utf-8 ?═ ?─ ?╌ ?┄ ?┈)) |
|
266 |
"Characters for underlining headings in ASCII export. |
|
267 |
|
|
268 |
Alist whose key is a symbol among `ascii', `latin1' and `utf-8' |
|
269 |
and whose value is a list of characters. |
|
270 |
|
|
271 |
For each supported charset, this variable associates a sequence |
|
272 |
of underline characters. In a sequence, the characters will be |
|
273 |
used in order for headlines level 1, 2, ... If no character is |
|
274 |
available for a given level, the headline won't be underlined." |
|
275 |
:group 'org-export-ascii |
|
276 |
:version "24.4" |
|
277 |
:package-version '(Org . "8.0") |
|
278 |
:type '(list |
|
279 |
(cons :tag "Underline characters sequence" |
|
280 |
(const :tag "ASCII charset" ascii) |
|
281 |
(repeat character)) |
|
282 |
(cons :tag "Underline characters sequence" |
|
283 |
(const :tag "Latin-1 charset" latin1) |
|
284 |
(repeat character)) |
|
285 |
(cons :tag "Underline characters sequence" |
|
286 |
(const :tag "UTF-8 charset" utf-8) |
|
287 |
(repeat character)))) |
|
288 |
|
|
289 |
(defcustom org-ascii-bullets '((ascii ?* ?+ ?-) |
|
290 |
(latin1 ?§ ?¶) |
|
291 |
(utf-8 ?◊)) |
|
292 |
"Bullet characters for headlines converted to lists in ASCII export. |
|
293 |
|
|
294 |
Alist whose key is a symbol among `ascii', `latin1' and `utf-8' |
|
295 |
and whose value is a list of characters. |
|
296 |
|
|
297 |
The first character is used for the first level considered as low |
|
298 |
level, and so on. If there are more levels than characters given |
|
299 |
here, the list will be repeated. |
|
300 |
|
|
301 |
Note that this variable doesn't affect plain lists |
|
302 |
representation." |
|
303 |
:group 'org-export-ascii |
|
304 |
:version "24.4" |
|
305 |
:package-version '(Org . "8.0") |
|
306 |
:type '(list |
|
307 |
(cons :tag "Bullet characters for low level headlines" |
|
308 |
(const :tag "ASCII charset" ascii) |
|
309 |
(repeat character)) |
|
310 |
(cons :tag "Bullet characters for low level headlines" |
|
311 |
(const :tag "Latin-1 charset" latin1) |
|
312 |
(repeat character)) |
|
313 |
(cons :tag "Bullet characters for low level headlines" |
|
314 |
(const :tag "UTF-8 charset" utf-8) |
|
315 |
(repeat character)))) |
|
316 |
|
|
317 |
(defcustom org-ascii-links-to-notes t |
|
318 |
"Non-nil means convert links to notes before the next headline. |
|
319 |
When nil, the link will be exported in place. If the line |
|
320 |
becomes long in this way, it will be wrapped." |
|
321 |
:group 'org-export-ascii |
|
322 |
:version "24.4" |
|
323 |
:package-version '(Org . "8.0") |
|
324 |
:type 'boolean) |
|
325 |
|
|
326 |
(defcustom org-ascii-table-keep-all-vertical-lines nil |
|
327 |
"Non-nil means keep all vertical lines in ASCII tables. |
|
328 |
When nil, vertical lines will be removed except for those needed |
|
329 |
for column grouping." |
|
330 |
:group 'org-export-ascii |
|
331 |
:version "24.4" |
|
332 |
:package-version '(Org . "8.0") |
|
333 |
:type 'boolean) |
|
334 |
|
|
335 |
(defcustom org-ascii-table-widen-columns t |
|
336 |
"Non-nil means widen narrowed columns for export. |
|
337 |
When nil, narrowed columns will look in ASCII export just like in |
|
338 |
Org mode, i.e. with \"=>\" as ellipsis." |
|
339 |
:group 'org-export-ascii |
|
340 |
:version "24.4" |
|
341 |
:package-version '(Org . "8.0") |
|
342 |
:type 'boolean) |
|
343 |
|
|
344 |
(defcustom org-ascii-table-use-ascii-art nil |
|
345 |
"Non-nil means \"table.el\" tables are turned into ASCII art. |
|
346 |
It only makes sense when export charset is `utf-8'. It is nil by |
|
347 |
default since it requires \"ascii-art-to-unicode.el\" package, |
|
348 |
available through, e.g., GNU ELPA." |
|
349 |
:group 'org-export-ascii |
|
350 |
:version "24.4" |
|
351 |
:package-version '(Org . "8.0") |
|
352 |
:type 'boolean) |
|
353 |
|
|
354 |
(defcustom org-ascii-caption-above nil |
|
355 |
"When non-nil, place caption string before the element. |
|
356 |
Otherwise, place it right after it." |
|
357 |
:group 'org-export-ascii |
|
358 |
:version "24.4" |
|
359 |
:package-version '(Org . "8.0") |
|
360 |
:type 'boolean) |
|
361 |
|
|
362 |
(defcustom org-ascii-verbatim-format "`%s'" |
|
363 |
"Format string used for verbatim text and inline code." |
|
364 |
:group 'org-export-ascii |
|
365 |
:version "24.4" |
|
366 |
:package-version '(Org . "8.0") |
|
367 |
:type 'string) |
|
368 |
|
|
369 |
(defcustom org-ascii-format-drawer-function |
|
370 |
(lambda (_name contents _width) contents) |
|
371 |
"Function called to format a drawer in ASCII. |
|
372 |
|
|
373 |
The function must accept three parameters: |
|
374 |
NAME the drawer name, like \"LOGBOOK\" |
|
375 |
CONTENTS the contents of the drawer. |
|
376 |
WIDTH the text width within the drawer. |
|
377 |
|
|
378 |
The function should return either the string to be exported or |
|
379 |
nil to ignore the drawer. |
|
380 |
|
|
381 |
The default value simply returns the value of CONTENTS." |
|
382 |
:group 'org-export-ascii |
|
383 |
:version "24.4" |
|
384 |
:package-version '(Org . "8.0") |
|
385 |
:type 'function) |
|
386 |
|
|
387 |
(defcustom org-ascii-format-inlinetask-function |
|
388 |
'org-ascii-format-inlinetask-default |
|
389 |
"Function called to format an inlinetask in ASCII. |
|
390 |
|
|
391 |
The function must accept nine parameters: |
|
392 |
TODO the todo keyword, as a string |
|
393 |
TODO-TYPE the todo type, a symbol among `todo', `done' and nil. |
|
394 |
PRIORITY the inlinetask priority, as a string |
|
395 |
NAME the inlinetask name, as a string. |
|
396 |
TAGS the inlinetask tags, as a list of strings. |
|
397 |
CONTENTS the contents of the inlinetask, as a string. |
|
398 |
WIDTH the width of the inlinetask, as a number. |
|
399 |
INLINETASK the inlinetask itself. |
|
400 |
INFO the info channel. |
|
401 |
|
|
402 |
The function should return either the string to be exported or |
|
403 |
nil to ignore the inline task." |
|
404 |
:group 'org-export-ascii |
|
405 |
:version "26.1" |
|
406 |
:package-version '(Org . "8.3") |
|
407 |
:type 'function) |
|
408 |
|
|
409 |
|
|
410 |
|
|
411 |
;;; Internal Functions |
|
412 |
|
|
413 |
;; Internal functions fall into three categories. |
|
414 |
|
|
415 |
;; The first one is about text formatting. The core functions are |
|
416 |
;; `org-ascii--current-text-width' and |
|
417 |
;; `org-ascii--current-justification', which determine, respectively, |
|
418 |
;; the current text width allowed to a given element and its expected |
|
419 |
;; justification. Once this information is known, |
|
420 |
;; `org-ascii--fill-string', `org-ascii--justify-lines', |
|
421 |
;; `org-ascii--justify-element' `org-ascii--box-string' and |
|
422 |
;; `org-ascii--indent-string' can operate on a given output string. |
|
423 |
;; In particular, justification happens at the regular (i.e., |
|
424 |
;; non-greater) element level, which means that when the exporting |
|
425 |
;; process reaches a container (e.g., a center block) content are |
|
426 |
;; already justified. |
|
427 |
|
|
428 |
;; The second category contains functions handling elements listings, |
|
429 |
;; triggered by "#+TOC:" keyword. As such, `org-ascii--build-toc' |
|
430 |
;; returns a complete table of contents, `org-ascii--list-listings' |
|
431 |
;; returns a list of referenceable src-block elements, and |
|
432 |
;; `org-ascii--list-tables' does the same for table elements. |
|
433 |
|
|
434 |
;; The third category includes general helper functions. |
|
435 |
;; `org-ascii--build-title' creates the title for a given headline |
|
436 |
;; or inlinetask element. `org-ascii--build-caption' returns the |
|
437 |
;; caption string associated to a table or a src-block. |
|
438 |
;; `org-ascii--describe-links' creates notes about links for |
|
439 |
;; insertion at the end of a section. It uses |
|
440 |
;; `org-ascii--unique-links' to get the list of links to describe. |
|
441 |
;; Eventually, `org-ascii--translate' translates a string according |
|
442 |
;; to language and charset specification. |
|
443 |
|
|
444 |
|
|
445 |
(defun org-ascii--fill-string (s text-width info &optional justify) |
|
446 |
"Fill a string with specified text-width and return it. |
|
447 |
|
|
448 |
S is the string being filled. TEXT-WIDTH is an integer |
|
449 |
specifying maximum length of a line. INFO is the plist used as |
|
450 |
a communication channel. |
|
451 |
|
|
452 |
Optional argument JUSTIFY can specify any type of justification |
|
453 |
among `left', `center', `right' or `full'. A nil value is |
|
454 |
equivalent to `left'. For a justification that doesn't also fill |
|
455 |
string, see `org-ascii--justify-lines' and |
|
456 |
`org-ascii--justify-block'. |
|
457 |
|
|
458 |
Return nil if S isn't a string." |
|
459 |
(when (stringp s) |
|
460 |
(let ((double-space-p sentence-end-double-space)) |
|
461 |
(with-temp-buffer |
|
462 |
(let ((fill-column text-width) |
|
463 |
(use-hard-newlines t) |
|
464 |
(sentence-end-double-space double-space-p)) |
|
465 |
(insert (if (plist-get info :preserve-breaks) |
|
466 |
(replace-regexp-in-string "\n" hard-newline s) |
|
467 |
s)) |
|
468 |
(fill-region (point-min) (point-max) justify)) |
|
469 |
(buffer-string))))) |
|
470 |
|
|
471 |
(defun org-ascii--justify-lines (s text-width how) |
|
472 |
"Justify all lines in string S. |
|
473 |
TEXT-WIDTH is an integer specifying maximum length of a line. |
|
474 |
HOW determines the type of justification: it can be `left', |
|
475 |
`right', `full' or `center'." |
|
476 |
(with-temp-buffer |
|
477 |
(insert s) |
|
478 |
(goto-char (point-min)) |
|
479 |
(let ((fill-column text-width) |
|
480 |
;; Disable `adaptive-fill-mode' so it doesn't prevent |
|
481 |
;; filling lines matching `adaptive-fill-regexp'. |
|
482 |
(adaptive-fill-mode nil)) |
|
483 |
(while (< (point) (point-max)) |
|
484 |
(justify-current-line how) |
|
485 |
(forward-line))) |
|
486 |
(buffer-string))) |
|
487 |
|
|
488 |
(defun org-ascii--justify-element (contents element info) |
|
489 |
"Justify CONTENTS of ELEMENT. |
|
490 |
INFO is a plist used as a communication channel. Justification |
|
491 |
is done according to the type of element. More accurately, |
|
492 |
paragraphs are filled and other elements are justified as blocks, |
|
493 |
that is according to the widest non blank line in CONTENTS." |
|
494 |
(if (not (org-string-nw-p contents)) contents |
|
495 |
(let ((text-width (org-ascii--current-text-width element info)) |
|
496 |
(how (org-ascii--current-justification element))) |
|
497 |
(cond |
|
498 |
((eq (org-element-type element) 'paragraph) |
|
499 |
;; Paragraphs are treated specially as they need to be filled. |
|
500 |
(org-ascii--fill-string contents text-width info how)) |
|
501 |
((eq how 'left) contents) |
|
502 |
(t (with-temp-buffer |
|
503 |
(insert contents) |
|
504 |
(goto-char (point-min)) |
|
505 |
(catch 'exit |
|
506 |
(let ((max-width 0)) |
|
507 |
;; Compute maximum width. Bail out if it is greater |
|
508 |
;; than page width, since no justification is |
|
509 |
;; possible. |
|
510 |
(save-excursion |
|
511 |
(while (not (eobp)) |
|
512 |
(unless (looking-at-p "[ \t]*$") |
|
513 |
(end-of-line) |
|
514 |
(let ((column (current-column))) |
|
515 |
(cond |
|
516 |
((>= column text-width) (throw 'exit contents)) |
|
517 |
((> column max-width) (setq max-width column))))) |
|
518 |
(forward-line))) |
|
519 |
;; Justify every line according to TEXT-WIDTH and |
|
520 |
;; MAX-WIDTH. |
|
521 |
(let ((offset (/ (- text-width max-width) |
|
522 |
(if (eq how 'right) 1 2)))) |
|
523 |
(if (zerop offset) (throw 'exit contents) |
|
524 |
(while (not (eobp)) |
|
525 |
(unless (looking-at-p "[ \t]*$") |
|
526 |
(indent-to-column offset)) |
|
527 |
(forward-line))))) |
|
528 |
(buffer-string)))))))) |
|
529 |
|
|
530 |
(defun org-ascii--indent-string (s width) |
|
531 |
"Indent string S by WIDTH white spaces. |
|
532 |
Empty lines are not indented." |
|
533 |
(when (stringp s) |
|
534 |
(replace-regexp-in-string |
|
535 |
"\\(^\\)[ \t]*\\S-" (make-string width ?\s) s nil nil 1))) |
|
536 |
|
|
537 |
(defun org-ascii--box-string (s info) |
|
538 |
"Return string S with a partial box to its left. |
|
539 |
INFO is a plist used as a communication channel." |
|
540 |
(let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))) |
|
541 |
(format (if utf8p "┌────\n%s\n└────" ",----\n%s\n`----") |
|
542 |
(replace-regexp-in-string |
|
543 |
"^" (if utf8p "│ " "| ") |
|
544 |
;; Remove last newline character. |
|
545 |
(replace-regexp-in-string "\n[ \t]*\\'" "" s))))) |
|
546 |
|
|
547 |
(defun org-ascii--current-text-width (element info) |
|
548 |
"Return maximum text width for ELEMENT's contents. |
|
549 |
INFO is a plist used as a communication channel." |
|
550 |
(pcase (org-element-type element) |
|
551 |
;; Elements with an absolute width: `headline' and `inlinetask'. |
|
552 |
(`inlinetask (plist-get info :ascii-inlinetask-width)) |
|
553 |
(`headline |
|
554 |
(- (plist-get info :ascii-text-width) |
|
555 |
(let ((low-level-rank (org-export-low-level-p element info))) |
|
556 |
(if low-level-rank (* low-level-rank 2) |
|
557 |
(plist-get info :ascii-global-margin))))) |
|
558 |
;; Elements with a relative width: store maximum text width in |
|
559 |
;; TOTAL-WIDTH. |
|
560 |
(_ |
|
561 |
(let* ((genealogy (org-element-lineage element nil t)) |
|
562 |
;; Total width is determined by the presence, or not, of an |
|
563 |
;; inline task among ELEMENT parents. |
|
564 |
(total-width |
|
565 |
(if (cl-some (lambda (parent) |
|
566 |
(eq (org-element-type parent) 'inlinetask)) |
|
567 |
genealogy) |
|
568 |
(plist-get info :ascii-inlinetask-width) |
|
569 |
;; No inlinetask: Remove global margin from text width. |
|
570 |
(- (plist-get info :ascii-text-width) |
|
571 |
(plist-get info :ascii-global-margin) |
|
572 |
(let ((parent (org-export-get-parent-headline element))) |
|
573 |
;; Inner margin doesn't apply to text before first |
|
574 |
;; headline. |
|
575 |
(if (not parent) 0 |
|
576 |
(let ((low-level-rank |
|
577 |
(org-export-low-level-p parent info))) |
|
578 |
;; Inner margin doesn't apply to contents of |
|
579 |
;; low level headlines, since they've got their |
|
580 |
;; own indentation mechanism. |
|
581 |
(if low-level-rank (* low-level-rank 2) |
|
582 |
(plist-get info :ascii-inner-margin))))))))) |
|
583 |
(- total-width |
|
584 |
;; Each `quote-block' and `verse-block' above narrows text |
|
585 |
;; width by twice the standard margin size. |
|
586 |
(+ (* (cl-count-if (lambda (parent) |
|
587 |
(memq (org-element-type parent) |
|
588 |
'(quote-block verse-block))) |
|
589 |
genealogy) |
|
590 |
2 |
|
591 |
(plist-get info :ascii-quote-margin)) |
|
592 |
;; Apply list margin once per "top-level" plain-list |
|
593 |
;; containing current line |
|
594 |
(* (cl-count-if |
|
595 |
(lambda (e) |
|
596 |
(and (eq (org-element-type e) 'plain-list) |
|
597 |
(not (eq (org-element-type (org-export-get-parent e)) |
|
598 |
'item)))) |
|
599 |
genealogy) |
|
600 |
(plist-get info :ascii-list-margin)) |
|
601 |
;; Compute indentation offset due to current list. It is |
|
602 |
;; `org-ascii-quote-margin' per descriptive item in the |
|
603 |
;; genealogy, bullet's length otherwise. |
|
604 |
(let ((indentation 0)) |
|
605 |
(dolist (e genealogy) |
|
606 |
(cond |
|
607 |
((not (eq 'item (org-element-type e)))) |
|
608 |
((eq (org-element-property :type (org-export-get-parent e)) |
|
609 |
'descriptive) |
|
610 |
(cl-incf indentation org-ascii-quote-margin)) |
|
611 |
(t |
|
612 |
(cl-incf indentation |
|
613 |
(+ (string-width |
|
614 |
(or (org-ascii--checkbox e info) "")) |
|
615 |
(string-width |
|
616 |
(org-element-property :bullet e))))))) |
|
617 |
indentation))))))) |
|
618 |
|
|
619 |
(defun org-ascii--current-justification (element) |
|
620 |
"Return expected justification for ELEMENT's contents. |
|
621 |
Return value is a symbol among `left', `center', `right' and |
|
622 |
`full'." |
|
623 |
(let (justification) |
|
624 |
(while (and (not justification) |
|
625 |
(setq element (org-element-property :parent element))) |
|
626 |
(pcase (org-element-type element) |
|
627 |
(`center-block (setq justification 'center)) |
|
628 |
(`special-block |
|
629 |
(let ((name (org-element-property :type element))) |
|
630 |
(cond ((string= name "JUSTIFYRIGHT") (setq justification 'right)) |
|
631 |
((string= name "JUSTIFYLEFT") (setq justification 'left))))))) |
|
632 |
(or justification 'left))) |
|
633 |
|
|
634 |
(defun org-ascii--build-title |
|
635 |
(element info text-width &optional underline notags toc) |
|
636 |
"Format ELEMENT title and return it. |
|
637 |
|
|
638 |
ELEMENT is either an `headline' or `inlinetask' element. INFO is |
|
639 |
a plist used as a communication channel. TEXT-WIDTH is an |
|
640 |
integer representing the maximum length of a line. |
|
641 |
|
|
642 |
When optional argument UNDERLINE is non-nil, underline title, |
|
643 |
without the tags, according to `org-ascii-underline' |
|
644 |
specifications. |
|
645 |
|
|
646 |
If optional argument NOTAGS is non-nil, no tags will be added to |
|
647 |
the title. |
|
648 |
|
|
649 |
When optional argument TOC is non-nil, use optional title if |
|
650 |
possible. It doesn't apply to `inlinetask' elements." |
|
651 |
(let* ((headlinep (eq (org-element-type element) 'headline)) |
|
652 |
(numbers |
|
653 |
;; Numbering is specific to headlines. |
|
654 |
(and headlinep (org-export-numbered-headline-p element info) |
|
655 |
;; All tests passed: build numbering string. |
|
656 |
(concat |
|
657 |
(mapconcat |
|
658 |
'number-to-string |
|
659 |
(org-export-get-headline-number element info) ".") |
|
660 |
" "))) |
|
661 |
(text |
|
662 |
(org-trim |
|
663 |
(org-export-data |
|
664 |
(if (and toc headlinep) (org-export-get-alt-title element info) |
|
665 |
(org-element-property :title element)) |
|
666 |
info))) |
|
667 |
(todo |
|
668 |
(and (plist-get info :with-todo-keywords) |
|
669 |
(let ((todo (org-element-property :todo-keyword element))) |
|
670 |
(and todo (concat (org-export-data todo info) " "))))) |
|
671 |
(tags (and (not notags) |
|
672 |
(plist-get info :with-tags) |
|
673 |
(let ((tag-list (org-export-get-tags element info))) |
|
674 |
(and tag-list |
|
675 |
(format ":%s:" |
|
676 |
(mapconcat 'identity tag-list ":")))))) |
|
677 |
(priority |
|
678 |
(and (plist-get info :with-priority) |
|
679 |
(let ((char (org-element-property :priority element))) |
|
680 |
(and char (format "(#%c) " char))))) |
|
681 |
(first-part (concat numbers todo priority text))) |
|
682 |
(concat |
|
683 |
first-part |
|
684 |
;; Align tags, if any. |
|
685 |
(when tags |
|
686 |
(format |
|
687 |
(format " %%%ds" |
|
688 |
(max (- text-width (1+ (string-width first-part))) |
|
689 |
(string-width tags))) |
|
690 |
tags)) |
|
691 |
;; Maybe underline text, if ELEMENT type is `headline' and an |
|
692 |
;; underline character has been defined. |
|
693 |
(when (and underline headlinep) |
|
694 |
(let ((under-char |
|
695 |
(nth (1- (org-export-get-relative-level element info)) |
|
696 |
(cdr (assq (plist-get info :ascii-charset) |
|
697 |
(plist-get info :ascii-underline)))))) |
|
698 |
(and under-char |
|
699 |
(concat "\n" |
|
700 |
(make-string (/ (string-width first-part) |
|
701 |
(char-width under-char)) |
|
702 |
under-char)))))))) |
|
703 |
|
|
704 |
(defun org-ascii--has-caption-p (element _info) |
|
705 |
"Non-nil when ELEMENT has a caption affiliated keyword. |
|
706 |
INFO is a plist used as a communication channel. This function |
|
707 |
is meant to be used as a predicate for `org-export-get-ordinal'." |
|
708 |
(org-element-property :caption element)) |
|
709 |
|
|
710 |
(defun org-ascii--build-caption (element info) |
|
711 |
"Return caption string for ELEMENT, if applicable. |
|
712 |
|
|
713 |
INFO is a plist used as a communication channel. |
|
714 |
|
|
715 |
The caption string contains the sequence number of ELEMENT along |
|
716 |
with its real caption. Return nil when ELEMENT has no affiliated |
|
717 |
caption keyword." |
|
718 |
(let ((caption (org-export-get-caption element))) |
|
719 |
(when caption |
|
720 |
;; Get sequence number of current src-block among every |
|
721 |
;; src-block with a caption. |
|
722 |
(let ((reference |
|
723 |
(org-export-get-ordinal |
|
724 |
element info nil 'org-ascii--has-caption-p)) |
|
725 |
(title-fmt (org-ascii--translate |
|
726 |
(pcase (org-element-type element) |
|
727 |
(`table "Table %d:") |
|
728 |
(`src-block "Listing %d:")) |
|
729 |
info))) |
|
730 |
(org-ascii--fill-string |
|
731 |
(concat (format title-fmt reference) |
|
732 |
" " |
|
733 |
(org-export-data caption info)) |
|
734 |
(org-ascii--current-text-width element info) info))))) |
|
735 |
|
|
736 |
(defun org-ascii--build-toc (info &optional n keyword local) |
|
737 |
"Return a table of contents. |
|
738 |
|
|
739 |
INFO is a plist used as a communication channel. |
|
740 |
|
|
741 |
Optional argument N, when non-nil, is an integer specifying the |
|
742 |
depth of the table. |
|
743 |
|
|
744 |
Optional argument KEYWORD specifies the TOC keyword, if any, from |
|
745 |
which the table of contents generation has been initiated. |
|
746 |
|
|
747 |
When optional argument LOCAL is non-nil, build a table of |
|
748 |
contents according to the current headline." |
|
749 |
(concat |
|
750 |
(unless local |
|
751 |
(let ((title (org-ascii--translate "Table of Contents" info))) |
|
752 |
(concat title "\n" |
|
753 |
(make-string |
|
754 |
(string-width title) |
|
755 |
(if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_)) |
|
756 |
"\n\n"))) |
|
757 |
(let ((text-width |
|
758 |
(if keyword (org-ascii--current-text-width keyword info) |
|
759 |
(- (plist-get info :ascii-text-width) |
|
760 |
(plist-get info :ascii-global-margin))))) |
|
761 |
(mapconcat |
|
762 |
(lambda (headline) |
|
763 |
(let* ((level (org-export-get-relative-level headline info)) |
|
764 |
(indent (* (1- level) 3))) |
|
765 |
(concat |
|
766 |
(unless (zerop indent) (concat (make-string (1- indent) ?.) " ")) |
|
767 |
(org-ascii--build-title |
|
768 |
headline info (- text-width indent) nil |
|
769 |
(or (not (plist-get info :with-tags)) |
|
770 |
(eq (plist-get info :with-tags) 'not-in-toc)) |
|
771 |
'toc)))) |
|
772 |
(org-export-collect-headlines info n (and local keyword)) "\n")))) |
|
773 |
|
|
774 |
(defun org-ascii--list-listings (keyword info) |
|
775 |
"Return a list of listings. |
|
776 |
|
|
777 |
KEYWORD is the keyword that initiated the list of listings |
|
778 |
generation. INFO is a plist used as a communication channel." |
|
779 |
(let ((title (org-ascii--translate "List of Listings" info))) |
|
780 |
(concat |
|
781 |
title "\n" |
|
782 |
(make-string (string-width title) |
|
783 |
(if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_)) |
|
784 |
"\n\n" |
|
785 |
(let ((text-width |
|
786 |
(if keyword (org-ascii--current-text-width keyword info) |
|
787 |
(- (plist-get info :ascii-text-width) |
|
788 |
(plist-get info :ascii-global-margin)))) |
|
789 |
;; Use a counter instead of retrieving ordinal of each |
|
790 |
;; src-block. |
|
791 |
(count 0)) |
|
792 |
(mapconcat |
|
793 |
(lambda (src-block) |
|
794 |
;; Store initial text so its length can be computed. This is |
|
795 |
;; used to properly align caption right to it in case of |
|
796 |
;; filling (like contents of a description list item). |
|
797 |
(let* ((initial-text |
|
798 |
(format (org-ascii--translate "Listing %d:" info) |
|
799 |
(cl-incf count))) |
|
800 |
(initial-width (string-width initial-text))) |
|
801 |
(concat |
|
802 |
initial-text " " |
|
803 |
(org-trim |
|
804 |
(org-ascii--indent-string |
|
805 |
(org-ascii--fill-string |
|
806 |
;; Use short name in priority, if available. |
|
807 |
(let ((caption (or (org-export-get-caption src-block t) |
|
808 |
(org-export-get-caption src-block)))) |
|
809 |
(org-export-data caption info)) |
|
810 |
(- text-width initial-width) info) |
|
811 |
initial-width))))) |
|
812 |
(org-export-collect-listings info) "\n"))))) |
|
813 |
|
|
814 |
(defun org-ascii--list-tables (keyword info) |
|
815 |
"Return a list of tables. |
|
816 |
|
|
817 |
KEYWORD is the keyword that initiated the list of tables |
|
818 |
generation. INFO is a plist used as a communication channel." |
|
819 |
(let ((title (org-ascii--translate "List of Tables" info))) |
|
820 |
(concat |
|
821 |
title "\n" |
|
822 |
(make-string (string-width title) |
|
823 |
(if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_)) |
|
824 |
"\n\n" |
|
825 |
(let ((text-width |
|
826 |
(if keyword (org-ascii--current-text-width keyword info) |
|
827 |
(- (plist-get info :ascii-text-width) |
|
828 |
(plist-get info :ascii-global-margin)))) |
|
829 |
;; Use a counter instead of retrieving ordinal of each |
|
830 |
;; src-block. |
|
831 |
(count 0)) |
|
832 |
(mapconcat |
|
833 |
(lambda (table) |
|
834 |
;; Store initial text so its length can be computed. This is |
|
835 |
;; used to properly align caption right to it in case of |
|
836 |
;; filling (like contents of a description list item). |
|
837 |
(let* ((initial-text |
|
838 |
(format (org-ascii--translate "Table %d:" info) |
|
839 |
(cl-incf count))) |
|
840 |
(initial-width (string-width initial-text))) |
|
841 |
(concat |
|
842 |
initial-text " " |
|
843 |
(org-trim |
|
844 |
(org-ascii--indent-string |
|
845 |
(org-ascii--fill-string |
|
846 |
;; Use short name in priority, if available. |
|
847 |
(let ((caption (or (org-export-get-caption table t) |
|
848 |
(org-export-get-caption table)))) |
|
849 |
(org-export-data caption info)) |
|
850 |
(- text-width initial-width) info) |
|
851 |
initial-width))))) |
|
852 |
(org-export-collect-tables info) "\n"))))) |
|
853 |
|
|
854 |
(defun org-ascii--unique-links (element info) |
|
855 |
"Return a list of unique link references in ELEMENT. |
|
856 |
ELEMENT is either a headline element or a section element. INFO |
|
857 |
is a plist used as a communication channel." |
|
858 |
(let* (seen |
|
859 |
(unique-link-p |
|
860 |
;; Return LINK if it wasn't referenced so far, or nil. |
|
861 |
;; Update SEEN links along the way. |
|
862 |
(lambda (link) |
|
863 |
(let ((footprint |
|
864 |
;; Normalize description in footprints. |
|
865 |
(cons (org-element-property :raw-link link) |
|
866 |
(let ((contents (org-element-contents link))) |
|
867 |
(and contents |
|
868 |
(replace-regexp-in-string |
|
869 |
"[ \r\t\n]+" " " |
|
870 |
(org-trim |
|
871 |
(org-element-interpret-data contents)))))))) |
|
872 |
;; Ignore LINK if it hasn't been translated already. It |
|
873 |
;; can happen if it is located in an affiliated keyword |
|
874 |
;; that was ignored. |
|
875 |
(when (and (org-string-nw-p |
|
876 |
(gethash link (plist-get info :exported-data))) |
|
877 |
(not (member footprint seen))) |
|
878 |
(push footprint seen) link))))) |
|
879 |
(org-element-map (if (eq (org-element-type element) 'section) |
|
880 |
element |
|
881 |
;; In a headline, only retrieve links in title |
|
882 |
;; and relative section, not in children. |
|
883 |
(list (org-element-property :title element) |
|
884 |
(car (org-element-contents element)))) |
|
885 |
'link unique-link-p info nil 'headline t))) |
|
886 |
|
|
887 |
(defun org-ascii--describe-datum (datum info) |
|
888 |
"Describe DATUM object or element. |
|
889 |
If DATUM is a string, consider it to be a file name, per |
|
890 |
`org-export-resolve-id-link'. INFO is the communication channel, |
|
891 |
as a plist." |
|
892 |
(pcase (org-element-type datum) |
|
893 |
(`plain-text (format "See file %s" datum)) ;External file |
|
894 |
(`headline |
|
895 |
(format (org-ascii--translate "See section %s" info) |
|
896 |
(if (org-export-numbered-headline-p datum info) |
|
897 |
(mapconcat #'number-to-string |
|
898 |
(org-export-get-headline-number datum info) |
|
899 |
".") |
|
900 |
(org-export-data (org-element-property :title datum) info)))) |
|
901 |
(_ |
|
902 |
(let ((number (org-export-get-ordinal |
|
903 |
datum info nil #'org-ascii--has-caption-p)) |
|
904 |
;; If destination is a target, make sure we can name the |
|
905 |
;; container it refers to. |
|
906 |
(enumerable |
|
907 |
(org-element-lineage datum |
|
908 |
'(headline paragraph src-block table) t))) |
|
909 |
(pcase (org-element-type enumerable) |
|
910 |
(`headline |
|
911 |
(format (org-ascii--translate "See section %s" info) |
|
912 |
(if (org-export-numbered-headline-p enumerable info) |
|
913 |
(mapconcat #'number-to-string number ".") |
|
914 |
(org-export-data |
|
915 |
(org-element-property :title enumerable) info)))) |
|
916 |
((guard (not number)) |
|
917 |
(org-ascii--translate "Unknown reference" info)) |
|
918 |
(`paragraph |
|
919 |
(format (org-ascii--translate "See figure %s" info) number)) |
|
920 |
(`src-block |
|
921 |
(format (org-ascii--translate "See listing %s" info) number)) |
|
922 |
(`table |
|
923 |
(format (org-ascii--translate "See table %s" info) number)) |
|
924 |
(_ (org-ascii--translate "Unknown reference" info))))))) |
|
925 |
|
|
926 |
(defun org-ascii--describe-links (links width info) |
|
927 |
"Return a string describing a list of links. |
|
928 |
LINKS is a list of link type objects, as returned by |
|
929 |
`org-ascii--unique-links'. WIDTH is the text width allowed for |
|
930 |
the output string. INFO is a plist used as a communication |
|
931 |
channel." |
|
932 |
(mapconcat |
|
933 |
(lambda (link) |
|
934 |
(let* ((type (org-element-property :type link)) |
|
935 |
(description (org-element-contents link)) |
|
936 |
(anchor (org-export-data |
|
937 |
(or description (org-element-property :raw-link link)) |
|
938 |
info))) |
|
939 |
(cond |
|
940 |
((member type '("coderef" "radio")) nil) |
|
941 |
((member type '("custom-id" "fuzzy" "id")) |
|
942 |
;; Only links with a description need an entry. Other are |
|
943 |
;; already handled in `org-ascii-link'. |
|
944 |
(when description |
|
945 |
(let ((dest (if (equal type "fuzzy") |
|
946 |
(org-export-resolve-fuzzy-link link info) |
|
947 |
(org-export-resolve-id-link link info)))) |
|
948 |
(concat |
|
949 |
(org-ascii--fill-string |
|
950 |
(format "[%s] %s" anchor (org-ascii--describe-datum dest info)) |
|
951 |
width info) |
|
952 |
"\n\n")))) |
|
953 |
;; Do not add a link that cannot be resolved and doesn't have |
|
954 |
;; any description: destination is already visible in the |
|
955 |
;; paragraph. |
|
956 |
((not (org-element-contents link)) nil) |
|
957 |
;; Do not add a link already handled by custom export |
|
958 |
;; functions. |
|
959 |
((org-export-custom-protocol-maybe link anchor 'ascii) nil) |
|
960 |
(t |
|
961 |
(concat |
|
962 |
(org-ascii--fill-string |
|
963 |
(format "[%s] %s" anchor (org-element-property :raw-link link)) |
|
964 |
width info) |
|
965 |
"\n\n"))))) |
|
966 |
links "")) |
|
967 |
|
|
968 |
(defun org-ascii--checkbox (item info) |
|
969 |
"Return checkbox string for ITEM or nil. |
|
970 |
INFO is a plist used as a communication channel." |
|
971 |
(let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))) |
|
972 |
(pcase (org-element-property :checkbox item) |
|
973 |
(`on (if utf8p "☑ " "[X] ")) |
|
974 |
(`off (if utf8p "☐ " "[ ] ")) |
|
975 |
(`trans (if utf8p "☒ " "[-] "))))) |
|
976 |
|
|
977 |
|
|
978 |
|
|
979 |
;;; Template |
|
980 |
|
|
981 |
(defun org-ascii-template--document-title (info) |
|
982 |
"Return document title, as a string. |
|
983 |
INFO is a plist used as a communication channel." |
|
984 |
(let* ((text-width (plist-get info :ascii-text-width)) |
|
985 |
;; Links in the title will not be resolved later, so we make |
|
986 |
;; sure their path is located right after them. |
|
987 |
(info (org-combine-plists info '(:ascii-links-to-notes nil))) |
|
988 |
(with-title (plist-get info :with-title)) |
|
989 |
(title (org-export-data |
|
990 |
(when with-title (plist-get info :title)) info)) |
|
991 |
(subtitle (org-export-data |
|
992 |
(when with-title (plist-get info :subtitle)) info)) |
|
993 |
(author (and (plist-get info :with-author) |
|
994 |
(let ((auth (plist-get info :author))) |
|
995 |
(and auth (org-export-data auth info))))) |
|
996 |
(email (and (plist-get info :with-email) |
|
997 |
(org-export-data (plist-get info :email) info))) |
|
998 |
(date (and (plist-get info :with-date) |
|
999 |
(org-export-data (org-export-get-date info) info)))) |
|
1000 |
;; There are two types of title blocks depending on the presence |
|
1001 |
;; of a title to display. |
|
1002 |
(if (string= title "") |
|
1003 |
;; Title block without a title. DATE is positioned at the top |
|
1004 |
;; right of the document, AUTHOR to the top left and EMAIL |
|
1005 |
;; just below. |
|
1006 |
(cond |
|
1007 |
((and (org-string-nw-p date) (org-string-nw-p author)) |
|
1008 |
(concat |
|
1009 |
author |
|
1010 |
(make-string (- text-width (string-width date) (string-width author)) |
|
1011 |
?\s) |
|
1012 |
date |
|
1013 |
(when (org-string-nw-p email) (concat "\n" email)) |
|
1014 |
"\n\n\n")) |
|
1015 |
((and (org-string-nw-p date) (org-string-nw-p email)) |
|
1016 |
(concat |
|
1017 |
email |
|
1018 |
(make-string (- text-width (string-width date) (string-width email)) |
|
1019 |
?\s) |
|
1020 |
date "\n\n\n")) |
|
1021 |
((org-string-nw-p date) |
|
1022 |
(concat |
|
1023 |
(org-ascii--justify-lines date text-width 'right) |
|
1024 |
"\n\n\n")) |
|
1025 |
((and (org-string-nw-p author) (org-string-nw-p email)) |
|
1026 |
(concat author "\n" email "\n\n\n")) |
|
1027 |
((org-string-nw-p author) (concat author "\n\n\n")) |
|
1028 |
((org-string-nw-p email) (concat email "\n\n\n"))) |
|
1029 |
;; Title block with a title. Document's TITLE, along with the |
|
1030 |
;; AUTHOR and its EMAIL are both overlined and an underlined, |
|
1031 |
;; centered. Date is just below, also centered. |
|
1032 |
(let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)) |
|
1033 |
;; Format TITLE. It may be filled if it is too wide, |
|
1034 |
;; that is wider than the two thirds of the total width. |
|
1035 |
(title-len (min (apply #'max |
|
1036 |
(mapcar #'length |
|
1037 |
(org-split-string |
|
1038 |
(concat title "\n" subtitle) "\n"))) |
|
1039 |
(/ (* 2 text-width) 3))) |
|
1040 |
(formatted-title (org-ascii--fill-string title title-len info)) |
|
1041 |
(formatted-subtitle (when (org-string-nw-p subtitle) |
|
1042 |
(org-ascii--fill-string subtitle title-len info))) |
|
1043 |
(line |
|
1044 |
(make-string |
|
1045 |
(min (+ (max title-len |
|
1046 |
(string-width (or author "")) |
|
1047 |
(string-width (or email ""))) |
|
1048 |
2) |
|
1049 |
text-width) (if utf8p ?━ ?_)))) |
|
1050 |
(org-ascii--justify-lines |
|
1051 |
(concat line "\n" |
|
1052 |
(unless utf8p "\n") |
|
1053 |
(upcase formatted-title) |
|
1054 |
(and formatted-subtitle (concat "\n" formatted-subtitle)) |
|
1055 |
(cond |
|
1056 |
((and (org-string-nw-p author) (org-string-nw-p email)) |
|
1057 |
(concat "\n\n" author "\n" email)) |
|
1058 |
((org-string-nw-p author) (concat "\n\n" author)) |
|
1059 |
((org-string-nw-p email) (concat "\n\n" email))) |
|
1060 |
"\n" line |
|
1061 |
(when (org-string-nw-p date) (concat "\n\n\n" date)) |
|
1062 |
"\n\n\n") text-width 'center))))) |
|
1063 |
|
|
1064 |
(defun org-ascii-inner-template (contents info) |
|
1065 |
"Return complete document string after ASCII conversion. |
|
1066 |
CONTENTS is the transcoded contents string. INFO is a plist |
|
1067 |
holding export options." |
|
1068 |
(org-element-normalize-string |
|
1069 |
(let ((global-margin (plist-get info :ascii-global-margin))) |
|
1070 |
(org-ascii--indent-string |
|
1071 |
(concat |
|
1072 |
;; 1. Document's body. |
|
1073 |
contents |
|
1074 |
;; 2. Footnote definitions. |
|
1075 |
(let ((definitions (org-export-collect-footnote-definitions info)) |
|
1076 |
;; Insert full links right inside the footnote definition |
|
1077 |
;; as they have no chance to be inserted later. |
|
1078 |
(info (org-combine-plists info '(:ascii-links-to-notes nil)))) |
|
1079 |
(when definitions |
|
1080 |
(concat |
|
1081 |
"\n\n\n" |
|
1082 |
(let ((title (org-ascii--translate "Footnotes" info))) |
|
1083 |
(concat |
|
1084 |
title "\n" |
|
1085 |
(make-string |
|
1086 |
(string-width title) |
|
1087 |
(if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_)))) |
|
1088 |
"\n\n" |
|
1089 |
(let ((text-width (- (plist-get info :ascii-text-width) |
|
1090 |
global-margin))) |
|
1091 |
(mapconcat |
|
1092 |
(lambda (ref) |
|
1093 |
(let ((id (format "[%s] " (car ref)))) |
|
1094 |
;; Distinguish between inline definitions and |
|
1095 |
;; full-fledged definitions. |
|
1096 |
(org-trim |
|
1097 |
(let ((def (nth 2 ref))) |
|
1098 |
(if (org-element-map def org-element-all-elements |
|
1099 |
#'identity info 'first-match) |
|
1100 |
;; Full-fledged definition: footnote ID is |
|
1101 |
;; inserted inside the first parsed |
|
1102 |
;; paragraph (FIRST), if any, to be sure |
|
1103 |
;; filling will take it into consideration. |
|
1104 |
(let ((first (car (org-element-contents def)))) |
|
1105 |
(if (not (eq (org-element-type first) 'paragraph)) |
|
1106 |
(concat id "\n" (org-export-data def info)) |
|
1107 |
(push id (nthcdr 2 first)) |
|
1108 |
(org-export-data def info))) |
|
1109 |
;; Fill paragraph once footnote ID is inserted |
|
1110 |
;; in order to have a correct length for first |
|
1111 |
;; line. |
|
1112 |
(org-ascii--fill-string |
|
1113 |
(concat id (org-export-data def info)) |
|
1114 |
text-width info)))))) |
|
1115 |
definitions "\n\n")))))) |
|
1116 |
global-margin)))) |
|
1117 |
|
|
1118 |
(defun org-ascii-template (contents info) |
|
1119 |
"Return complete document string after ASCII conversion. |
|
1120 |
CONTENTS is the transcoded contents string. INFO is a plist |
|
1121 |
holding export options." |
|
1122 |
(let ((global-margin (plist-get info :ascii-global-margin))) |
|
1123 |
(concat |
|
1124 |
;; Build title block. |
|
1125 |
(org-ascii--indent-string |
|
1126 |
(concat (org-ascii-template--document-title info) |
|
1127 |
;; 2. Table of contents. |
|
1128 |
(let ((depth (plist-get info :with-toc))) |
|
1129 |
(when depth |
|
1130 |
(concat |
|
1131 |
(org-ascii--build-toc info (and (wholenump depth) depth)) |
|
1132 |
"\n\n\n")))) |
|
1133 |
global-margin) |
|
1134 |
;; Document's body. |
|
1135 |
contents |
|
1136 |
;; Creator. Justify it to the bottom right. |
|
1137 |
(and (plist-get info :with-creator) |
|
1138 |
(org-ascii--indent-string |
|
1139 |
(let ((text-width |
|
1140 |
(- (plist-get info :ascii-text-width) global-margin))) |
|
1141 |
(concat |
|
1142 |
"\n\n\n" |
|
1143 |
(org-ascii--fill-string |
|
1144 |
(plist-get info :creator) text-width info 'right))) |
|
1145 |
global-margin))))) |
|
1146 |
|
|
1147 |
(defun org-ascii--translate (s info) |
|
1148 |
"Translate string S according to specified language and charset. |
|
1149 |
INFO is a plist used as a communication channel." |
|
1150 |
(let ((charset (intern (format ":%s" (plist-get info :ascii-charset))))) |
|
1151 |
(org-export-translate s charset info))) |
|
1152 |
|
|
1153 |
|
|
1154 |
|
|
1155 |
;;; Transcode Functions |
|
1156 |
|
|
1157 |
;;;; Bold |
|
1158 |
|
|
1159 |
(defun org-ascii-bold (_bold contents _info) |
|
1160 |
"Transcode BOLD from Org to ASCII. |
|
1161 |
CONTENTS is the text with bold markup. INFO is a plist holding |
|
1162 |
contextual information." |
|
1163 |
(format "*%s*" contents)) |
|
1164 |
|
|
1165 |
|
|
1166 |
;;;; Center Block |
|
1167 |
|
|
1168 |
(defun org-ascii-center-block (_center-block contents _info) |
|
1169 |
"Transcode a CENTER-BLOCK element from Org to ASCII. |
|
1170 |
CONTENTS holds the contents of the block. INFO is a plist |
|
1171 |
holding contextual information." |
|
1172 |
;; Center has already been taken care of at a lower level, so |
|
1173 |
;; there's nothing left to do. |
|
1174 |
contents) |
|
1175 |
|
|
1176 |
|
|
1177 |
;;;; Clock |
|
1178 |
|
|
1179 |
(defun org-ascii-clock (clock _contents info) |
|
1180 |
"Transcode a CLOCK object from Org to ASCII. |
|
1181 |
CONTENTS is nil. INFO is a plist holding contextual |
|
1182 |
information." |
|
1183 |
(org-ascii--justify-element |
|
1184 |
(concat org-clock-string " " |
|
1185 |
(org-timestamp-translate (org-element-property :value clock)) |
|
1186 |
(let ((time (org-element-property :duration clock))) |
|
1187 |
(and time |
|
1188 |
(concat " => " |
|
1189 |
(apply 'format |
|
1190 |
"%2s:%02s" |
|
1191 |
(org-split-string time ":")))))) |
|
1192 |
clock info)) |
|
1193 |
|
|
1194 |
|
|
1195 |
;;;; Code |
|
1196 |
|
|
1197 |
(defun org-ascii-code (code _contents info) |
|
1198 |
"Return a CODE object from Org to ASCII. |
|
1199 |
CONTENTS is nil. INFO is a plist holding contextual |
|
1200 |
information." |
|
1201 |
(format (plist-get info :ascii-verbatim-format) |
|
1202 |
(org-element-property :value code))) |
|
1203 |
|
|
1204 |
|
|
1205 |
;;;; Drawer |
|
1206 |
|
|
1207 |
(defun org-ascii-drawer (drawer contents info) |
|
1208 |
"Transcode a DRAWER element from Org to ASCII. |
|
1209 |
CONTENTS holds the contents of the block. INFO is a plist |
|
1210 |
holding contextual information." |
|
1211 |
(let ((name (org-element-property :drawer-name drawer)) |
|
1212 |
(width (org-ascii--current-text-width drawer info))) |
|
1213 |
(funcall (plist-get info :ascii-format-drawer-function) |
|
1214 |
name contents width))) |
|
1215 |
|
|
1216 |
|
|
1217 |
;;;; Dynamic Block |
|
1218 |
|
|
1219 |
(defun org-ascii-dynamic-block (_dynamic-block contents _info) |
|
1220 |
"Transcode a DYNAMIC-BLOCK element from Org to ASCII. |
|
1221 |
CONTENTS holds the contents of the block. INFO is a plist |
|
1222 |
holding contextual information." |
|
1223 |
contents) |
|
1224 |
|
|
1225 |
|
|
1226 |
;;;; Entity |
|
1227 |
|
|
1228 |
(defun org-ascii-entity (entity _contents info) |
|
1229 |
"Transcode an ENTITY object from Org to ASCII. |
|
1230 |
CONTENTS are the definition itself. INFO is a plist holding |
|
1231 |
contextual information." |
|
1232 |
(org-element-property |
|
1233 |
(intern (concat ":" (symbol-name (plist-get info :ascii-charset)))) |
|
1234 |
entity)) |
|
1235 |
|
|
1236 |
|
|
1237 |
;;;; Example Block |
|
1238 |
|
|
1239 |
(defun org-ascii-example-block (example-block _contents info) |
|
1240 |
"Transcode a EXAMPLE-BLOCK element from Org to ASCII. |
|
1241 |
CONTENTS is nil. INFO is a plist holding contextual information." |
|
1242 |
(org-ascii--justify-element |
|
1243 |
(org-ascii--box-string |
|
1244 |
(org-export-format-code-default example-block info) info) |
|
1245 |
example-block info)) |
|
1246 |
|
|
1247 |
|
|
1248 |
;;;; Export Snippet |
|
1249 |
|
|
1250 |
(defun org-ascii-export-snippet (export-snippet _contents _info) |
|
1251 |
"Transcode a EXPORT-SNIPPET object from Org to ASCII. |
|
1252 |
CONTENTS is nil. INFO is a plist holding contextual information." |
|
1253 |
(when (eq (org-export-snippet-backend export-snippet) 'ascii) |
|
1254 |
(org-element-property :value export-snippet))) |
|
1255 |
|
|
1256 |
|
|
1257 |
;;;; Export Block |
|
1258 |
|
|
1259 |
(defun org-ascii-export-block (export-block _contents info) |
|
1260 |
"Transcode a EXPORT-BLOCK element from Org to ASCII. |
|
1261 |
CONTENTS is nil. INFO is a plist holding contextual information." |
|
1262 |
(when (string= (org-element-property :type export-block) "ASCII") |
|
1263 |
(org-ascii--justify-element |
|
1264 |
(org-element-property :value export-block) export-block info))) |
|
1265 |
|
|
1266 |
|
|
1267 |
;;;; Fixed Width |
|
1268 |
|
|
1269 |
(defun org-ascii-fixed-width (fixed-width _contents info) |
|
1270 |
"Transcode a FIXED-WIDTH element from Org to ASCII. |
|
1271 |
CONTENTS is nil. INFO is a plist holding contextual information." |
|
1272 |
(org-ascii--justify-element |
|
1273 |
(org-ascii--box-string |
|
1274 |
(org-remove-indentation |
|
1275 |
(org-element-property :value fixed-width)) info) |
|
1276 |
fixed-width info)) |
|
1277 |
|
|
1278 |
|
|
1279 |
;;;; Footnote Definition |
|
1280 |
|
|
1281 |
;; Footnote Definitions are ignored. They are compiled at the end of |
|
1282 |
;; the document, by `org-ascii-inner-template'. |
|
1283 |
|
|
1284 |
|
|
1285 |
;;;; Footnote Reference |
|
1286 |
|
|
1287 |
(defun org-ascii-footnote-reference (footnote-reference _contents info) |
|
1288 |
"Transcode a FOOTNOTE-REFERENCE element from Org to ASCII. |
|
1289 |
CONTENTS is nil. INFO is a plist holding contextual information." |
|
1290 |
(format "[%s]" (org-export-get-footnote-number footnote-reference info))) |
|
1291 |
|
|
1292 |
|
|
1293 |
;;;; Headline |
|
1294 |
|
|
1295 |
(defun org-ascii-headline (headline contents info) |
|
1296 |
"Transcode a HEADLINE element from Org to ASCII. |
|
1297 |
CONTENTS holds the contents of the headline. INFO is a plist |
|
1298 |
holding contextual information." |
|
1299 |
;; Don't export footnote section, which will be handled at the end |
|
1300 |
;; of the template. |
|
1301 |
(unless (org-element-property :footnote-section-p headline) |
|
1302 |
(let* ((low-level (org-export-low-level-p headline info)) |
|
1303 |
(width (org-ascii--current-text-width headline info)) |
|
1304 |
;; Export title early so that any link in it can be |
|
1305 |
;; exported and seen in `org-ascii--unique-links'. |
|
1306 |
(title (org-ascii--build-title headline info width (not low-level))) |
|
1307 |
;; Blank lines between headline and its contents. |
|
1308 |
;; `org-ascii-headline-spacing', when set, overwrites |
|
1309 |
;; original buffer's spacing. |
|
1310 |
(pre-blanks |
|
1311 |
(make-string (or (car (plist-get info :ascii-headline-spacing)) |
|
1312 |
(org-element-property :pre-blank headline) |
|
1313 |
0) |
|
1314 |
?\n)) |
|
1315 |
(links (and (plist-get info :ascii-links-to-notes) |
|
1316 |
(org-ascii--describe-links |
|
1317 |
(org-ascii--unique-links headline info) width info))) |
|
1318 |
;; Re-build contents, inserting section links at the right |
|
1319 |
;; place. The cost is low since build results are cached. |
|
1320 |
(body |
|
1321 |
(if (not (org-string-nw-p links)) contents |
|
1322 |
(let* ((contents (org-element-contents headline)) |
|
1323 |
(section (let ((first (car contents))) |
|
1324 |
(and (eq (org-element-type first) 'section) |
|
1325 |
first)))) |
|
1326 |
(concat (and section |
|
1327 |
(concat (org-element-normalize-string |
|
1328 |
(org-export-data section info)) |
|
1329 |
"\n\n")) |
|
1330 |
links |
|
1331 |
(mapconcat (lambda (e) (org-export-data e info)) |
|
1332 |
(if section (cdr contents) contents) |
|
1333 |
"")))))) |
|
1334 |
;; Deep subtree: export it as a list item. |
|
1335 |
(if low-level |
|
1336 |
(let* ((bullets (cdr (assq (plist-get info :ascii-charset) |
|
1337 |
(plist-get info :ascii-bullets)))) |
|
1338 |
(bullet |
|
1339 |
(format "%c " |
|
1340 |
(nth (mod (1- low-level) (length bullets)) bullets)))) |
|
1341 |
(concat bullet title "\n" pre-blanks |
|
1342 |
;; Contents, indented by length of bullet. |
|
1343 |
(org-ascii--indent-string body (length bullet)))) |
|
1344 |
;; Else: Standard headline. |
|
1345 |
(concat title "\n" pre-blanks body))))) |
|
1346 |
|
|
1347 |
|
|
1348 |
;;;; Horizontal Rule |
|
1349 |
|
|
1350 |
(defun org-ascii-horizontal-rule (horizontal-rule _contents info) |
|
1351 |
"Transcode an HORIZONTAL-RULE object from Org to ASCII. |
|
1352 |
CONTENTS is nil. INFO is a plist holding contextual |
|
1353 |
information." |
|
1354 |
(let ((text-width (org-ascii--current-text-width horizontal-rule info)) |
|
1355 |
(spec-width |
|
1356 |
(org-export-read-attribute :attr_ascii horizontal-rule :width))) |
|
1357 |
(org-ascii--justify-lines |
|
1358 |
(make-string (if (and spec-width (string-match "^[0-9]+$" spec-width)) |
|
1359 |
(string-to-number spec-width) |
|
1360 |
text-width) |
|
1361 |
(if (eq (plist-get info :ascii-charset) 'utf-8) ?― ?-)) |
|
1362 |
text-width 'center))) |
|
1363 |
|
|
1364 |
|
|
1365 |
;;;; Inline Src Block |
|
1366 |
|
|
1367 |
(defun org-ascii-inline-src-block (inline-src-block _contents info) |
|
1368 |
"Transcode an INLINE-SRC-BLOCK element from Org to ASCII. |
|
1369 |
CONTENTS holds the contents of the item. INFO is a plist holding |
|
1370 |
contextual information." |
|
1371 |
(format (plist-get info :ascii-verbatim-format) |
|
1372 |
(org-element-property :value inline-src-block))) |
|
1373 |
|
|
1374 |
|
|
1375 |
;;;; Inlinetask |
|
1376 |
|
|
1377 |
(defun org-ascii-format-inlinetask-default |
|
1378 |
(_todo _type _priority _name _tags contents width inlinetask info) |
|
1379 |
"Format an inline task element for ASCII export. |
|
1380 |
See `org-ascii-format-inlinetask-function' for a description |
|
1381 |
of the parameters." |
|
1382 |
(let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)) |
|
1383 |
(width (or width (plist-get info :ascii-inlinetask-width)))) |
|
1384 |
(org-ascii--indent-string |
|
1385 |
(concat |
|
1386 |
;; Top line, with an additional blank line if not in UTF-8. |
|
1387 |
(make-string width (if utf8p ?━ ?_)) "\n" |
|
1388 |
(unless utf8p (concat (make-string width ? ) "\n")) |
|
1389 |
;; Add title. Fill it if wider than inlinetask. |
|
1390 |
(let ((title (org-ascii--build-title inlinetask info width))) |
|
1391 |
(if (<= (string-width title) width) title |
|
1392 |
(org-ascii--fill-string title width info))) |
|
1393 |
"\n" |
|
1394 |
;; If CONTENTS is not empty, insert it along with |
|
1395 |
;; a separator. |
|
1396 |
(when (org-string-nw-p contents) |
|
1397 |
(concat (make-string width (if utf8p ?─ ?-)) "\n" contents)) |
|
1398 |
;; Bottom line. |
|
1399 |
(make-string width (if utf8p ?━ ?_))) |
|
1400 |
;; Flush the inlinetask to the right. |
|
1401 |
(- (plist-get info :ascii-text-width) (plist-get info :ascii-global-margin) |
|
1402 |
(if (not (org-export-get-parent-headline inlinetask)) 0 |
|
1403 |
(plist-get info :ascii-inner-margin)) |
|
1404 |
(org-ascii--current-text-width inlinetask info))))) |
|
1405 |
|
|
1406 |
(defun org-ascii-inlinetask (inlinetask contents info) |
|
1407 |
"Transcode an INLINETASK element from Org to ASCII. |
|
1408 |
CONTENTS holds the contents of the block. INFO is a plist |
|
1409 |
holding contextual information." |
|
1410 |
(let ((width (org-ascii--current-text-width inlinetask info))) |
|
1411 |
(funcall (plist-get info :ascii-format-inlinetask-function) |
|
1412 |
;; todo. |
|
1413 |
(and (plist-get info :with-todo-keywords) |
|
1414 |
(let ((todo (org-element-property |
|
1415 |
:todo-keyword inlinetask))) |
|
1416 |
(and todo (org-export-data todo info)))) |
|
1417 |
;; todo-type |
|
1418 |
(org-element-property :todo-type inlinetask) |
|
1419 |
;; priority |
|
1420 |
(and (plist-get info :with-priority) |
|
1421 |
(org-element-property :priority inlinetask)) |
|
1422 |
;; title |
|
1423 |
(org-export-data (org-element-property :title inlinetask) info) |
|
1424 |
;; tags |
|
1425 |
(and (plist-get info :with-tags) |
|
1426 |
(org-element-property :tags inlinetask)) |
|
1427 |
;; contents and width |
|
1428 |
contents width inlinetask info))) |
|
1429 |
|
|
1430 |
|
|
1431 |
;;;; Italic |
|
1432 |
|
|
1433 |
(defun org-ascii-italic (_italic contents _info) |
|
1434 |
"Transcode italic from Org to ASCII. |
|
1435 |
CONTENTS is the text with italic markup. INFO is a plist holding |
|
1436 |
contextual information." |
|
1437 |
(format "/%s/" contents)) |
|
1438 |
|
|
1439 |
|
|
1440 |
;;;; Item |
|
1441 |
|
|
1442 |
(defun org-ascii-item (item contents info) |
|
1443 |
"Transcode an ITEM element from Org to ASCII. |
|
1444 |
CONTENTS holds the contents of the item. INFO is a plist holding |
|
1445 |
contextual information." |
|
1446 |
(let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)) |
|
1447 |
(checkbox (org-ascii--checkbox item info)) |
|
1448 |
(list-type (org-element-property :type (org-export-get-parent item))) |
|
1449 |
(bullet |
|
1450 |
;; First parent of ITEM is always the plain-list. Get |
|
1451 |
;; `:type' property from it. |
|
1452 |
(pcase list-type |
|
1453 |
(`descriptive |
|
1454 |
(concat checkbox |
|
1455 |
(org-export-data (org-element-property :tag item) |
|
1456 |
info))) |
|
1457 |
(`ordered |
|
1458 |
;; Return correct number for ITEM, paying attention to |
|
1459 |
;; counters. |
|
1460 |
(let* ((struct (org-element-property :structure item)) |
|
1461 |
(bul (org-list-bullet-string |
|
1462 |
(org-element-property :bullet item))) |
|
1463 |
(num (number-to-string |
|
1464 |
(car (last (org-list-get-item-number |
|
1465 |
(org-element-property :begin item) |
|
1466 |
struct |
|
1467 |
(org-list-prevs-alist struct) |
|
1468 |
(org-list-parents-alist struct))))))) |
|
1469 |
(replace-regexp-in-string "[0-9]+" num bul))) |
|
1470 |
(_ (let ((bul (org-list-bullet-string |
|
1471 |
(org-element-property :bullet item)))) |
|
1472 |
;; Change bullets into more visible form if UTF-8 is active. |
|
1473 |
(if (not utf8p) bul |
|
1474 |
(replace-regexp-in-string |
|
1475 |
"-" "•" |
|
1476 |
(replace-regexp-in-string |
|
1477 |
"+" "⁃" |
|
1478 |
(replace-regexp-in-string "*" "‣" bul)))))))) |
|
1479 |
(indentation (if (eq list-type 'descriptive) org-ascii-quote-margin |
|
1480 |
(string-width bullet)))) |
|
1481 |
(concat |
|
1482 |
bullet |
|
1483 |
checkbox |
|
1484 |
;; Contents: Pay attention to indentation. Note: check-boxes are |
|
1485 |
;; already taken care of at the paragraph level so they don't |
|
1486 |
;; interfere with indentation. |
|
1487 |
(let ((contents (org-ascii--indent-string contents indentation))) |
|
1488 |
;; Determine if contents should follow the bullet or start |
|
1489 |
;; a new line. Do the former when the first contributing |
|
1490 |
;; element to contents is a paragraph. In descriptive lists |
|
1491 |
;; however, contents always start a new line. |
|
1492 |
(if (and (not (eq list-type 'descriptive)) |
|
1493 |
(org-string-nw-p contents) |
|
1494 |
(eq 'paragraph |
|
1495 |
(org-element-type |
|
1496 |
(cl-some (lambda (e) |
|
1497 |
(and (org-string-nw-p (org-export-data e info)) |
|
1498 |
e)) |
|
1499 |
(org-element-contents item))))) |
|
1500 |
(org-trim contents) |
|
1501 |
(concat "\n" contents)))))) |
|
1502 |
|
|
1503 |
|
|
1504 |
;;;; Keyword |
|
1505 |
|
|
1506 |
(defun org-ascii-keyword (keyword _contents info) |
|
1507 |
"Transcode a KEYWORD element from Org to ASCII. |
|
1508 |
CONTENTS is nil. INFO is a plist holding contextual |
|
1509 |
information." |
|
1510 |
(let ((key (org-element-property :key keyword)) |
|
1511 |
(value (org-element-property :value keyword))) |
|
1512 |
(cond |
|
1513 |
((string= key "ASCII") (org-ascii--justify-element value keyword info)) |
|
1514 |
((string= key "TOC") |
|
1515 |
(org-ascii--justify-element |
|
1516 |
(let ((case-fold-search t)) |
|
1517 |
(cond |
|
1518 |
((string-match-p "\\<headlines\\>" value) |
|
1519 |
(let ((depth (and (string-match "\\<[0-9]+\\>" value) |
|
1520 |
(string-to-number (match-string 0 value)))) |
|
1521 |
(localp (string-match-p "\\<local\\>" value))) |
|
1522 |
(org-ascii--build-toc info depth keyword localp))) |
|
1523 |
((string-match-p "\\<tables\\>" value) |
|
1524 |
(org-ascii--list-tables keyword info)) |
|
1525 |
((string-match-p "\\<listings\\>" value) |
|
1526 |
(org-ascii--list-listings keyword info)))) |
|
1527 |
keyword info))))) |
|
1528 |
|
|
1529 |
|
|
1530 |
;;;; Latex Environment |
|
1531 |
|
|
1532 |
(defun org-ascii-latex-environment (latex-environment _contents info) |
|
1533 |
"Transcode a LATEX-ENVIRONMENT element from Org to ASCII. |
|
1534 |
CONTENTS is nil. INFO is a plist holding contextual |
|
1535 |
information." |
|
1536 |
(when (plist-get info :with-latex) |
|
1537 |
(org-ascii--justify-element |
|
1538 |
(org-remove-indentation (org-element-property :value latex-environment)) |
|
1539 |
latex-environment info))) |
|
1540 |
|
|
1541 |
|
|
1542 |
;;;; Latex Fragment |
|
1543 |
|
|
1544 |
(defun org-ascii-latex-fragment (latex-fragment _contents info) |
|
1545 |
"Transcode a LATEX-FRAGMENT object from Org to ASCII. |
|
1546 |
CONTENTS is nil. INFO is a plist holding contextual |
|
1547 |
information." |
|
1548 |
(when (plist-get info :with-latex) |
|
1549 |
(org-element-property :value latex-fragment))) |
|
1550 |
|
|
1551 |
|
|
1552 |
;;;; Line Break |
|
1553 |
|
|
1554 |
(defun org-ascii-line-break (_line-break _contents _info) |
|
1555 |
"Transcode a LINE-BREAK object from Org to ASCII. |
|
1556 |
CONTENTS is nil. INFO is a plist holding contextual |
|
1557 |
information." hard-newline) |
|
1558 |
|
|
1559 |
|
|
1560 |
;;;; Link |
|
1561 |
|
|
1562 |
(defun org-ascii-link (link desc info) |
|
1563 |
"Transcode a LINK object from Org to ASCII. |
|
1564 |
|
|
1565 |
DESC is the description part of the link, or the empty string. |
|
1566 |
INFO is a plist holding contextual information." |
|
1567 |
(let ((type (org-element-property :type link))) |
|
1568 |
(cond |
|
1569 |
((org-export-custom-protocol-maybe link desc 'ascii)) |
|
1570 |
((string= type "coderef") |
|
1571 |
(let ((ref (org-element-property :path link))) |
|
1572 |
(format (org-export-get-coderef-format ref desc) |
|
1573 |
(org-export-resolve-coderef ref info)))) |
|
1574 |
;; Do not apply a special syntax on radio links. Though, use |
|
1575 |
;; transcoded target's contents as output. |
|
1576 |
((string= type "radio") desc) |
|
1577 |
((member type '("custom-id" "fuzzy" "id")) |
|
1578 |
(let ((destination (if (string= type "fuzzy") |
|
1579 |
(org-export-resolve-fuzzy-link link info) |
|
1580 |
(org-export-resolve-id-link link info)))) |
|
1581 |
(pcase (org-element-type destination) |
|
1582 |
((guard desc) |
|
1583 |
(if (plist-get info :ascii-links-to-notes) |
|
1584 |
(format "[%s]" desc) |
|
1585 |
(concat desc |
|
1586 |
(format " (%s)" |
|
1587 |
(org-ascii--describe-datum destination info))))) |
|
1588 |
;; External file. |
|
1589 |
(`plain-text destination) |
|
1590 |
(`headline |
|
1591 |
(if (org-export-numbered-headline-p destination info) |
|
1592 |
(mapconcat #'number-to-string |
|
1593 |
(org-export-get-headline-number destination info) |
|
1594 |
".") |
|
1595 |
(org-export-data (org-element-property :title destination) info))) |
|
1596 |
;; Handle enumerable elements and targets within them. |
|
1597 |
((and (let number (org-export-get-ordinal |
|
1598 |
destination info nil #'org-ascii--has-caption-p)) |
|
1599 |
(guard number)) |
|
1600 |
(if (atom number) (number-to-string number) |
|
1601 |
(mapconcat #'number-to-string number "."))) |
|
1602 |
;; Don't know what to do. Signal it. |
|
1603 |
(_ "???")))) |
|
1604 |
(t |
|
1605 |
(let ((raw-link (org-element-property :raw-link link))) |
|
1606 |
(if (not (org-string-nw-p desc)) (format "[%s]" raw-link) |
|
1607 |
(concat (format "[%s]" desc) |
|
1608 |
(and (not (plist-get info :ascii-links-to-notes)) |
|
1609 |
(format " (%s)" raw-link))))))))) |
|
1610 |
|
|
1611 |
|
|
1612 |
;;;; Node Properties |
|
1613 |
|
|
1614 |
(defun org-ascii-node-property (node-property _contents _info) |
|
1615 |
"Transcode a NODE-PROPERTY element from Org to ASCII. |
|
1616 |
CONTENTS is nil. INFO is a plist holding contextual |
|
1617 |
information." |
|
1618 |
(format "%s:%s" |
|
1619 |
(org-element-property :key node-property) |
|
1620 |
(let ((value (org-element-property :value node-property))) |
|
1621 |
(if value (concat " " value) "")))) |
|
1622 |
|
|
1623 |
|
|
1624 |
;;;; Paragraph |
|
1625 |
|
|
1626 |
(defun org-ascii-paragraph (paragraph contents info) |
|
1627 |
"Transcode a PARAGRAPH element from Org to ASCII. |
|
1628 |
CONTENTS is the contents of the paragraph, as a string. INFO is |
|
1629 |
the plist used as a communication channel." |
|
1630 |
(org-ascii--justify-element |
|
1631 |
(let ((indented-line-width (plist-get info :ascii-indented-line-width))) |
|
1632 |
(if (not (wholenump indented-line-width)) contents |
|
1633 |
(concat |
|
1634 |
;; Do not indent first paragraph in a section. |
|
1635 |
(unless (and (not (org-export-get-previous-element paragraph info)) |
|
1636 |
(eq (org-element-type (org-export-get-parent paragraph)) |
|
1637 |
'section)) |
|
1638 |
(make-string indented-line-width ?\s)) |
|
1639 |
(replace-regexp-in-string "\\`[ \t]+" "" contents)))) |
|
1640 |
paragraph info)) |
|
1641 |
|
|
1642 |
|
|
1643 |
;;;; Plain List |
|
1644 |
|
|
1645 |
(defun org-ascii-plain-list (plain-list contents info) |
|
1646 |
"Transcode a PLAIN-LIST element from Org to ASCII. |
|
1647 |
CONTENTS is the contents of the list. INFO is a plist holding |
|
1648 |
contextual information." |
|
1649 |
(let ((margin (plist-get info :ascii-list-margin))) |
|
1650 |
(if (or (< margin 1) |
|
1651 |
(eq (org-element-type (org-export-get-parent plain-list)) 'item)) |
|
1652 |
contents |
|
1653 |
(org-ascii--indent-string contents margin)))) |
|
1654 |
|
|
1655 |
|
|
1656 |
;;;; Plain Text |
|
1657 |
|
|
1658 |
(defun org-ascii-plain-text (text info) |
|
1659 |
"Transcode a TEXT string from Org to ASCII. |
|
1660 |
INFO is a plist used as a communication channel." |
|
1661 |
(let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))) |
|
1662 |
(when (and utf8p (plist-get info :with-smart-quotes)) |
|
1663 |
(setq text (org-export-activate-smart-quotes text :utf-8 info))) |
|
1664 |
(if (not (plist-get info :with-special-strings)) text |
|
1665 |
(setq text (replace-regexp-in-string "\\\\-" "" text)) |
|
1666 |
(if (not utf8p) text |
|
1667 |
;; Usual replacements in utf-8 with proper option set. |
|
1668 |
(replace-regexp-in-string |
|
1669 |
"\\.\\.\\." "…" |
|
1670 |
(replace-regexp-in-string |
|
1671 |
"--" "–" |
|
1672 |
(replace-regexp-in-string "---" "—" text))))))) |
|
1673 |
|
|
1674 |
|
|
1675 |
;;;; Planning |
|
1676 |
|
|
1677 |
(defun org-ascii-planning (planning _contents info) |
|
1678 |
"Transcode a PLANNING element from Org to ASCII. |
|
1679 |
CONTENTS is nil. INFO is a plist used as a communication |
|
1680 |
channel." |
|
1681 |
(org-ascii--justify-element |
|
1682 |
(mapconcat |
|
1683 |
#'identity |
|
1684 |
(delq nil |
|
1685 |
(list (let ((closed (org-element-property :closed planning))) |
|
1686 |
(when closed |
|
1687 |
(concat org-closed-string " " |
|
1688 |
(org-timestamp-translate closed)))) |
|
1689 |
(let ((deadline (org-element-property :deadline planning))) |
|
1690 |
(when deadline |
|
1691 |
(concat org-deadline-string " " |
|
1692 |
(org-timestamp-translate deadline)))) |
|
1693 |
(let ((scheduled (org-element-property :scheduled planning))) |
|
1694 |
(when scheduled |
|
1695 |
(concat org-scheduled-string " " |
|
1696 |
(org-timestamp-translate scheduled)))))) |
|
1697 |
" ") |
|
1698 |
planning info)) |
|
1699 |
|
|
1700 |
|
|
1701 |
;;;; Property Drawer |
|
1702 |
|
|
1703 |
(defun org-ascii-property-drawer (property-drawer contents info) |
|
1704 |
"Transcode a PROPERTY-DRAWER element from Org to ASCII. |
|
1705 |
CONTENTS holds the contents of the drawer. INFO is a plist |
|
1706 |
holding contextual information." |
|
1707 |
(and (org-string-nw-p contents) |
|
1708 |
(org-ascii--justify-element contents property-drawer info))) |
|
1709 |
|
|
1710 |
|
|
1711 |
;;;; Quote Block |
|
1712 |
|
|
1713 |
(defun org-ascii-quote-block (_quote-block contents info) |
|
1714 |
"Transcode a QUOTE-BLOCK element from Org to ASCII. |
|
1715 |
CONTENTS holds the contents of the block. INFO is a plist |
|
1716 |
holding contextual information." |
|
1717 |
(org-ascii--indent-string contents (plist-get info :ascii-quote-margin))) |
|
1718 |
|
|
1719 |
|
|
1720 |
;;;; Radio Target |
|
1721 |
|
|
1722 |
(defun org-ascii-radio-target (_radio-target contents _info) |
|
1723 |
"Transcode a RADIO-TARGET object from Org to ASCII. |
|
1724 |
CONTENTS is the contents of the target. INFO is a plist holding |
|
1725 |
contextual information." |
|
1726 |
contents) |
|
1727 |
|
|
1728 |
|
|
1729 |
;;;; Section |
|
1730 |
|
|
1731 |
(defun org-ascii-section (section contents info) |
|
1732 |
"Transcode a SECTION element from Org to ASCII. |
|
1733 |
CONTENTS is the contents of the section. INFO is a plist holding |
|
1734 |
contextual information." |
|
1735 |
(let ((links |
|
1736 |
(and (plist-get info :ascii-links-to-notes) |
|
1737 |
;; Take care of links in first section of the document. |
|
1738 |
(not (org-element-lineage section '(headline))) |
|
1739 |
(org-ascii--describe-links |
|
1740 |
(org-ascii--unique-links section info) |
|
1741 |
(org-ascii--current-text-width section info) |
|
1742 |
info)))) |
|
1743 |
(org-ascii--indent-string |
|
1744 |
(if (not (org-string-nw-p links)) contents |
|
1745 |
(concat (org-element-normalize-string contents) "\n\n" links)) |
|
1746 |
;; Do not apply inner margin if parent headline is low level. |
|
1747 |
(let ((headline (org-export-get-parent-headline section))) |
|
1748 |
(if (or (not headline) (org-export-low-level-p headline info)) 0 |
|
1749 |
(plist-get info :ascii-inner-margin)))))) |
|
1750 |
|
|
1751 |
|
|
1752 |
;;;; Special Block |
|
1753 |
|
|
1754 |
(defun org-ascii-special-block (_special-block contents _info) |
|
1755 |
"Transcode a SPECIAL-BLOCK element from Org to ASCII. |
|
1756 |
CONTENTS holds the contents of the block. INFO is a plist |
|
1757 |
holding contextual information." |
|
1758 |
;; "JUSTIFYLEFT" and "JUSTIFYRIGHT" have already been taken care of |
|
1759 |
;; at a lower level. There is no other special block type to |
|
1760 |
;; handle. |
|
1761 |
contents) |
|
1762 |
|
|
1763 |
|
|
1764 |
;;;; Src Block |
|
1765 |
|
|
1766 |
(defun org-ascii-src-block (src-block _contents info) |
|
1767 |
"Transcode a SRC-BLOCK element from Org to ASCII. |
|
1768 |
CONTENTS holds the contents of the item. INFO is a plist holding |
|
1769 |
contextual information." |
|
1770 |
(let ((caption (org-ascii--build-caption src-block info)) |
|
1771 |
(caption-above-p (plist-get info :ascii-caption-above)) |
|
1772 |
(code (org-export-format-code-default src-block info))) |
|
1773 |
(if (equal code "") "" |
|
1774 |
(org-ascii--justify-element |
|
1775 |
(concat |
|
1776 |
(and caption caption-above-p (concat caption "\n")) |
|
1777 |
(org-ascii--box-string code info) |
|
1778 |
(and caption (not caption-above-p) (concat "\n" caption))) |
|
1779 |
src-block info)))) |
|
1780 |
|
|
1781 |
|
|
1782 |
;;;; Statistics Cookie |
|
1783 |
|
|
1784 |
(defun org-ascii-statistics-cookie (statistics-cookie _contents _info) |
|
1785 |
"Transcode a STATISTICS-COOKIE object from Org to ASCII. |
|
1786 |
CONTENTS is nil. INFO is a plist holding contextual information." |
|
1787 |
(org-element-property :value statistics-cookie)) |
|
1788 |
|
|
1789 |
|
|
1790 |
;;;; Subscript |
|
1791 |
|
|
1792 |
(defun org-ascii-subscript (subscript contents _info) |
|
1793 |
"Transcode a SUBSCRIPT object from Org to ASCII. |
|
1794 |
CONTENTS is the contents of the object. INFO is a plist holding |
|
1795 |
contextual information." |
|
1796 |
(if (org-element-property :use-brackets-p subscript) |
|
1797 |
(format "_{%s}" contents) |
|
1798 |
(format "_%s" contents))) |
|
1799 |
|
|
1800 |
|
|
1801 |
;;;; Superscript |
|
1802 |
|
|
1803 |
(defun org-ascii-superscript (superscript contents _info) |
|
1804 |
"Transcode a SUPERSCRIPT object from Org to ASCII. |
|
1805 |
CONTENTS is the contents of the object. INFO is a plist holding |
|
1806 |
contextual information." |
|
1807 |
(if (org-element-property :use-brackets-p superscript) |
|
1808 |
(format "^{%s}" contents) |
|
1809 |
(format "^%s" contents))) |
|
1810 |
|
|
1811 |
|
|
1812 |
;;;; Strike-through |
|
1813 |
|
|
1814 |
(defun org-ascii-strike-through (_strike-through contents _info) |
|
1815 |
"Transcode STRIKE-THROUGH from Org to ASCII. |
|
1816 |
CONTENTS is text with strike-through markup. INFO is a plist |
|
1817 |
holding contextual information." |
|
1818 |
(format "+%s+" contents)) |
|
1819 |
|
|
1820 |
|
|
1821 |
;;;; Table |
|
1822 |
|
|
1823 |
(defun org-ascii-table (table contents info) |
|
1824 |
"Transcode a TABLE element from Org to ASCII. |
|
1825 |
CONTENTS is the contents of the table. INFO is a plist holding |
|
1826 |
contextual information." |
|
1827 |
(let ((caption (org-ascii--build-caption table info)) |
|
1828 |
(caption-above-p (plist-get info :ascii-caption-above))) |
|
1829 |
(org-ascii--justify-element |
|
1830 |
(concat |
|
1831 |
;; Possibly add a caption string above. |
|
1832 |
(and caption caption-above-p (concat caption "\n")) |
|
1833 |
;; Insert table. Note: "table.el" tables are left unmodified. |
|
1834 |
(cond ((eq (org-element-property :type table) 'org) contents) |
|
1835 |
((and (plist-get info :ascii-table-use-ascii-art) |
|
1836 |
(eq (plist-get info :ascii-charset) 'utf-8) |
|
1837 |
(require 'ascii-art-to-unicode nil t)) |
|
1838 |
(with-temp-buffer |
|
1839 |
(insert (org-remove-indentation |
|
1840 |
(org-element-property :value table))) |
|
1841 |
(goto-char (point-min)) |
|
1842 |
(aa2u) |
|
1843 |
(goto-char (point-max)) |
|
1844 |
(skip-chars-backward " \r\t\n") |
|
1845 |
(buffer-substring (point-min) (point)))) |
|
1846 |
(t (org-remove-indentation (org-element-property :value table)))) |
|
1847 |
;; Possible add a caption string below. |
|
1848 |
(and (not caption-above-p) caption)) |
|
1849 |
table info))) |
|
1850 |
|
|
1851 |
|
|
1852 |
;;;; Table Cell |
|
1853 |
|
|
1854 |
(defun org-ascii--table-cell-width (table-cell info) |
|
1855 |
"Return width of TABLE-CELL. |
|
1856 |
|
|
1857 |
INFO is a plist used as a communication channel. |
|
1858 |
|
|
1859 |
Width of a cell is determined either by a width cookie in the |
|
1860 |
same column as the cell, or by the maximum cell's length in that |
|
1861 |
column. |
|
1862 |
|
|
1863 |
When `org-ascii-table-widen-columns' is non-nil, width cookies |
|
1864 |
are ignored." |
|
1865 |
(let* ((row (org-export-get-parent table-cell)) |
|
1866 |
(table (org-export-get-parent row)) |
|
1867 |
(col (let ((cells (org-element-contents row))) |
|
1868 |
(- (length cells) (length (memq table-cell cells))))) |
|
1869 |
(cache |
|
1870 |
(or (plist-get info :ascii-table-cell-width-cache) |
|
1871 |
(plist-get (setq info |
|
1872 |
(plist-put info :ascii-table-cell-width-cache |
|
1873 |
(make-hash-table :test 'equal))) |
|
1874 |
:ascii-table-cell-width-cache))) |
|
1875 |
(key (cons table col)) |
|
1876 |
(widenp (plist-get info :ascii-table-widen-columns))) |
|
1877 |
(or (gethash key cache) |
|
1878 |
(puthash |
|
1879 |
key |
|
1880 |
(let ((cookie-width (org-export-table-cell-width table-cell info))) |
|
1881 |
(or (and (not widenp) cookie-width) |
|
1882 |
(let ((contents-width |
|
1883 |
(let ((max-width 0)) |
|
1884 |
(org-element-map table 'table-row |
|
1885 |
(lambda (row) |
|
1886 |
(setq max-width |
|
1887 |
(max (string-width |
|
1888 |
(org-export-data |
|
1889 |
(org-element-contents |
|
1890 |
(elt (org-element-contents row) col)) |
|
1891 |
info)) |
|
1892 |
max-width))) |
|
1893 |
info) |
|
1894 |
max-width))) |
|
1895 |
(cond ((not cookie-width) contents-width) |
|
1896 |
(widenp (max cookie-width contents-width)) |
|
1897 |
(t cookie-width))))) |
|
1898 |
cache)))) |
|
1899 |
|
|
1900 |
(defun org-ascii-table-cell (table-cell contents info) |
|
1901 |
"Transcode a TABLE-CELL object from Org to ASCII. |
|
1902 |
CONTENTS is the cell contents. INFO is a plist used as |
|
1903 |
a communication channel." |
|
1904 |
;; Determine column width. When `org-ascii-table-widen-columns' |
|
1905 |
;; is nil and some width cookie has set it, use that value. |
|
1906 |
;; Otherwise, compute the maximum width among transcoded data of |
|
1907 |
;; each cell in the column. |
|
1908 |
(let ((width (org-ascii--table-cell-width table-cell info))) |
|
1909 |
;; When contents are too large, truncate them. |
|
1910 |
(unless (or (plist-get info :ascii-table-widen-columns) |
|
1911 |
(<= (string-width (or contents "")) width)) |
|
1912 |
(setq contents (concat (substring contents 0 (- width 2)) "=>"))) |
|
1913 |
;; Align contents correctly within the cell. |
|
1914 |
(let* ((indent-tabs-mode nil) |
|
1915 |
(data |
|
1916 |
(when contents |
|
1917 |
(org-ascii--justify-lines |
|
1918 |
contents width |
|
1919 |
(org-export-table-cell-alignment table-cell info))))) |
|
1920 |
(setq contents |
|
1921 |
(concat data |
|
1922 |
(make-string (- width (string-width (or data ""))) ?\s)))) |
|
1923 |
;; Return cell. |
|
1924 |
(concat (format " %s " contents) |
|
1925 |
(when (memq 'right (org-export-table-cell-borders table-cell info)) |
|
1926 |
(if (eq (plist-get info :ascii-charset) 'utf-8) "│" "|"))))) |
|
1927 |
|
|
1928 |
|
|
1929 |
;;;; Table Row |
|
1930 |
|
|
1931 |
(defun org-ascii-table-row (table-row contents info) |
|
1932 |
"Transcode a TABLE-ROW element from Org to ASCII. |
|
1933 |
CONTENTS is the row contents. INFO is a plist used as |
|
1934 |
a communication channel." |
|
1935 |
(when (eq (org-element-property :type table-row) 'standard) |
|
1936 |
(let ((build-hline |
|
1937 |
(function |
|
1938 |
(lambda (lcorner horiz vert rcorner) |
|
1939 |
(concat |
|
1940 |
(apply |
|
1941 |
'concat |
|
1942 |
(org-element-map table-row 'table-cell |
|
1943 |
(lambda (cell) |
|
1944 |
(let ((width (org-ascii--table-cell-width cell info)) |
|
1945 |
(borders (org-export-table-cell-borders cell info))) |
|
1946 |
(concat |
|
1947 |
;; In order to know if CELL starts the row, do |
|
1948 |
;; not compare it with the first cell in the |
|
1949 |
;; row as there might be a special column. |
|
1950 |
;; Instead, compare it with first exportable |
|
1951 |
;; cell, obtained with `org-element-map'. |
|
1952 |
(when (and (memq 'left borders) |
|
1953 |
(eq (org-element-map table-row 'table-cell |
|
1954 |
'identity info t) |
|
1955 |
cell)) |
|
1956 |
lcorner) |
|
1957 |
(make-string (+ 2 width) (string-to-char horiz)) |
|
1958 |
(cond |
|
1959 |
((not (memq 'right borders)) nil) |
|
1960 |
((eq (car (last (org-element-contents table-row))) cell) |
|
1961 |
rcorner) |
|
1962 |
(t vert))))) |
|
1963 |
info)) "\n")))) |
|
1964 |
(utf8p (eq (plist-get info :ascii-charset) 'utf-8)) |
|
1965 |
(borders (org-export-table-cell-borders |
|
1966 |
(org-element-map table-row 'table-cell 'identity info t) |
|
1967 |
info))) |
|
1968 |
(concat (cond |
|
1969 |
((and (memq 'top borders) (or utf8p (memq 'above borders))) |
|
1970 |
(if utf8p (funcall build-hline "┍" "━" "┯" "┑") |
|
1971 |
(funcall build-hline "+" "-" "+" "+"))) |
|
1972 |
((memq 'above borders) |
|
1973 |
(if utf8p (funcall build-hline "├" "─" "┼" "┤") |
|
1974 |
(funcall build-hline "+" "-" "+" "+")))) |
|
1975 |
(when (memq 'left borders) (if utf8p "│" "|")) |
|
1976 |
contents "\n" |
|
1977 |
(when (and (memq 'bottom borders) (or utf8p (memq 'below borders))) |
|
1978 |
(if utf8p (funcall build-hline "┕" "━" "┷" "┙") |
|
1979 |
(funcall build-hline "+" "-" "+" "+"))))))) |
|
1980 |
|
|
1981 |
|
|
1982 |
;;;; Timestamp |
|
1983 |
|
|
1984 |
(defun org-ascii-timestamp (timestamp _contents info) |
|
1985 |
"Transcode a TIMESTAMP object from Org to ASCII. |
|
1986 |
CONTENTS is nil. INFO is a plist holding contextual information." |
|
1987 |
(org-ascii-plain-text (org-timestamp-translate timestamp) info)) |
|
1988 |
|
|
1989 |
|
|
1990 |
;;;; Underline |
|
1991 |
|
|
1992 |
(defun org-ascii-underline (_underline contents _info) |
|
1993 |
"Transcode UNDERLINE from Org to ASCII. |
|
1994 |
CONTENTS is the text with underline markup. INFO is a plist |
|
1995 |
holding contextual information." |
|
1996 |
(format "_%s_" contents)) |
|
1997 |
|
|
1998 |
|
|
1999 |
;;;; Verbatim |
|
2000 |
|
|
2001 |
(defun org-ascii-verbatim (verbatim _contents info) |
|
2002 |
"Return a VERBATIM object from Org to ASCII. |
|
2003 |
CONTENTS is nil. INFO is a plist holding contextual information." |
|
2004 |
(format (plist-get info :ascii-verbatim-format) |
|
2005 |
(org-element-property :value verbatim))) |
|
2006 |
|
|
2007 |
|
|
2008 |
;;;; Verse Block |
|
2009 |
|
|
2010 |
(defun org-ascii-verse-block (verse-block contents info) |
|
2011 |
"Transcode a VERSE-BLOCK element from Org to ASCII. |
|
2012 |
CONTENTS is verse block contents. INFO is a plist holding |
|
2013 |
contextual information." |
|
2014 |
(org-ascii--indent-string |
|
2015 |
(org-ascii--justify-element contents verse-block info) |
|
2016 |
(plist-get info :ascii-quote-margin))) |
|
2017 |
|
|
2018 |
|
|
2019 |
|
|
2020 |
;;; Filters |
|
2021 |
|
|
2022 |
(defun org-ascii-filter-headline-blank-lines (headline _backend info) |
|
2023 |
"Filter controlling number of blank lines after a headline. |
|
2024 |
|
|
2025 |
HEADLINE is a string representing a transcoded headline. BACKEND |
|
2026 |
is symbol specifying back-end used for export. INFO is plist |
|
2027 |
containing the communication channel. |
|
2028 |
|
|
2029 |
This function only applies to `ascii' back-end. See |
|
2030 |
`org-ascii-headline-spacing' for information." |
|
2031 |
(let ((headline-spacing (plist-get info :ascii-headline-spacing))) |
|
2032 |
(if (not headline-spacing) headline |
|
2033 |
(let ((blanks (make-string (1+ (cdr headline-spacing)) ?\n))) |
|
2034 |
(replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline))))) |
|
2035 |
|
|
2036 |
(defun org-ascii-filter-paragraph-spacing (tree _backend info) |
|
2037 |
"Filter controlling number of blank lines between paragraphs. |
|
2038 |
|
|
2039 |
TREE is the parse tree. BACKEND is the symbol specifying |
|
2040 |
back-end used for export. INFO is a plist used as |
|
2041 |
a communication channel. |
|
2042 |
|
|
2043 |
See `org-ascii-paragraph-spacing' for information." |
|
2044 |
(let ((paragraph-spacing (plist-get info :ascii-paragraph-spacing))) |
|
2045 |
(when (wholenump paragraph-spacing) |
|
2046 |
(org-element-map tree 'paragraph |
|
2047 |
(lambda (p) |
|
2048 |
(when (eq (org-element-type (org-export-get-next-element p info)) |
|
2049 |
'paragraph) |
|
2050 |
(org-element-put-property p :post-blank paragraph-spacing)))))) |
|
2051 |
tree) |
|
2052 |
|
|
2053 |
(defun org-ascii-filter-comment-spacing (tree _backend info) |
|
2054 |
"Filter removing blank lines between comments. |
|
2055 |
TREE is the parse tree. BACKEND is the symbol specifying |
|
2056 |
back-end used for export. INFO is a plist used as |
|
2057 |
a communication channel." |
|
2058 |
(org-element-map tree '(comment comment-block) |
|
2059 |
(lambda (c) |
|
2060 |
(when (memq (org-element-type (org-export-get-next-element c info)) |
|
2061 |
'(comment comment-block)) |
|
2062 |
(org-element-put-property c :post-blank 0)))) |
|
2063 |
tree) |
|
2064 |
|
|
2065 |
|
|
2066 |
|
|
2067 |
;;; End-user functions |
|
2068 |
|
|
2069 |
;;;###autoload |
|
2070 |
(defun org-ascii-export-as-ascii |
|
2071 |
(&optional async subtreep visible-only body-only ext-plist) |
|
2072 |
"Export current buffer to a text buffer. |
|
2073 |
|
|
2074 |
If narrowing is active in the current buffer, only export its |
|
2075 |
narrowed part. |
|
2076 |
|
|
2077 |
If a region is active, export that region. |
|
2078 |
|
|
2079 |
A non-nil optional argument ASYNC means the process should happen |
|
2080 |
asynchronously. The resulting buffer should be accessible |
|
2081 |
through the `org-export-stack' interface. |
|
2082 |
|
|
2083 |
When optional argument SUBTREEP is non-nil, export the sub-tree |
|
2084 |
at point, extracting information from the headline properties |
|
2085 |
first. |
|
2086 |
|
|
2087 |
When optional argument VISIBLE-ONLY is non-nil, don't export |
|
2088 |
contents of hidden elements. |
|
2089 |
|
|
2090 |
When optional argument BODY-ONLY is non-nil, strip title and |
|
2091 |
table of contents from output. |
|
2092 |
|
|
2093 |
EXT-PLIST, when provided, is a property list with external |
|
2094 |
parameters overriding Org default settings, but still inferior to |
|
2095 |
file-local settings. |
|
2096 |
|
|
2097 |
Export is done in a buffer named \"*Org ASCII Export*\", which |
|
2098 |
will be displayed when `org-export-show-temporary-export-buffer' |
|
2099 |
is non-nil." |
|
2100 |
(interactive) |
|
2101 |
(org-export-to-buffer 'ascii "*Org ASCII Export*" |
|
2102 |
async subtreep visible-only body-only ext-plist (lambda () (text-mode)))) |
|
2103 |
|
|
2104 |
;;;###autoload |
|
2105 |
(defun org-ascii-export-to-ascii |
|
2106 |
(&optional async subtreep visible-only body-only ext-plist) |
|
2107 |
"Export current buffer to a text file. |
|
2108 |
|
|
2109 |
If narrowing is active in the current buffer, only export its |
|
2110 |
narrowed part. |
|
2111 |
|
|
2112 |
If a region is active, export that region. |
|
2113 |
|
|
2114 |
A non-nil optional argument ASYNC means the process should happen |
|
2115 |
asynchronously. The resulting file should be accessible through |
|
2116 |
the `org-export-stack' interface. |
|
2117 |
|
|
2118 |
When optional argument SUBTREEP is non-nil, export the sub-tree |
|
2119 |
at point, extracting information from the headline properties |
|
2120 |
first. |
|
2121 |
|
|
2122 |
When optional argument VISIBLE-ONLY is non-nil, don't export |
|
2123 |
contents of hidden elements. |
|
2124 |
|
|
2125 |
When optional argument BODY-ONLY is non-nil, strip title and |
|
2126 |
table of contents from output. |
|
2127 |
|
|
2128 |
EXT-PLIST, when provided, is a property list with external |
|
2129 |
parameters overriding Org default settings, but still inferior to |
|
2130 |
file-local settings. |
|
2131 |
|
|
2132 |
Return output file's name." |
|
2133 |
(interactive) |
|
2134 |
(let ((file (org-export-output-file-name ".txt" subtreep))) |
|
2135 |
(org-export-to-file 'ascii file |
|
2136 |
async subtreep visible-only body-only ext-plist))) |
|
2137 |
|
|
2138 |
;;;###autoload |
|
2139 |
(defun org-ascii-publish-to-ascii (plist filename pub-dir) |
|
2140 |
"Publish an Org file to ASCII. |
|
2141 |
|
|
2142 |
FILENAME is the filename of the Org file to be published. PLIST |
|
2143 |
is the property list for the given project. PUB-DIR is the |
|
2144 |
publishing directory. |
|
2145 |
|
|
2146 |
Return output file name." |
|
2147 |
(org-publish-org-to |
|
2148 |
'ascii filename ".txt" `(:ascii-charset ascii ,@plist) pub-dir)) |
|
2149 |
|
|
2150 |
;;;###autoload |
|
2151 |
(defun org-ascii-publish-to-latin1 (plist filename pub-dir) |
|
2152 |
"Publish an Org file to Latin-1. |
|
2153 |
|
|
2154 |
FILENAME is the filename of the Org file to be published. PLIST |
|
2155 |
is the property list for the given project. PUB-DIR is the |
|
2156 |
publishing directory. |
|
2157 |
|
|
2158 |
Return output file name." |
|
2159 |
(org-publish-org-to |
|
2160 |
'ascii filename ".txt" `(:ascii-charset latin1 ,@plist) pub-dir)) |
|
2161 |
|
|
2162 |
;;;###autoload |
|
2163 |
(defun org-ascii-publish-to-utf8 (plist filename pub-dir) |
|
2164 |
"Publish an org file to UTF-8. |
|
2165 |
|
|
2166 |
FILENAME is the filename of the Org file to be published. PLIST |
|
2167 |
is the property list for the given project. PUB-DIR is the |
|
2168 |
publishing directory. |
|
2169 |
|
|
2170 |
Return output file name." |
|
2171 |
(org-publish-org-to |
|
2172 |
'ascii filename ".txt" `(:ascii-charset utf-8 ,@plist) pub-dir)) |
|
2173 |
|
|
2174 |
|
|
2175 |
(provide 'ox-ascii) |
|
2176 |
|
|
2177 |
;; Local variables: |
|
2178 |
;; generated-autoload-file: "org-loaddefs.el" |
|
2179 |
;; coding: utf-8 |
|
2180 |
;; End: |
|
2181 |
|
|
2182 |
;;; ox-ascii.el ends here |