1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221 |
- ;;; ox-ascii.el --- ASCII Back-End for Org Export Engine -*- lexical-binding: t; -*-
- ;; Copyright (C) 2012-2022 Free Software Foundation, Inc.
- ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com>
- ;; Maintainer: Nicolas Goaziou <mail@nicolasgoaziou.fr>
- ;; Keywords: outlines, hypermedia, calendar, wp
- ;; This file is part of GNU Emacs.
- ;; GNU Emacs is free software: you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation, either version 3 of the License, or
- ;; (at your option) any later version.
- ;; GNU Emacs is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;; You should have received a copy of the GNU General Public License
- ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
- ;;; Commentary:
- ;;
- ;; This library implements an ASCII back-end for Org generic exporter.
- ;; See Org manual for more information.
- ;;; Code:
- (require 'org-macs)
- (org-assert-version)
- (require 'ox)
- (require 'ox-publish)
- (require 'cl-lib)
- ;;; Function Declarations
- (declare-function aa2u "ext:ascii-art-to-unicode" ())
- (declare-function org-at-heading-p "org" (&optional _))
- (declare-function org-back-to-heading "org" (&optional invisible-ok))
- (declare-function org-next-visible-heading "org" (arg))
- ;;; Define Back-End
- ;;
- ;; The following setting won't allow modifying preferred charset
- ;; through a buffer keyword or an option item, but, since the property
- ;; will appear in communication channel nonetheless, it allows
- ;; overriding `org-ascii-charset' variable on the fly by the ext-plist
- ;; mechanism.
- ;;
- ;; We also install a filter for headlines and sections, in order to
- ;; control blank lines separating them in output string.
- (org-export-define-backend 'ascii
- '((bold . org-ascii-bold)
- (center-block . org-ascii-center-block)
- (clock . org-ascii-clock)
- (code . org-ascii-code)
- (drawer . org-ascii-drawer)
- (dynamic-block . org-ascii-dynamic-block)
- (entity . org-ascii-entity)
- (example-block . org-ascii-example-block)
- (export-block . org-ascii-export-block)
- (export-snippet . org-ascii-export-snippet)
- (fixed-width . org-ascii-fixed-width)
- (footnote-reference . org-ascii-footnote-reference)
- (headline . org-ascii-headline)
- (horizontal-rule . org-ascii-horizontal-rule)
- (inline-src-block . org-ascii-inline-src-block)
- (inlinetask . org-ascii-inlinetask)
- (inner-template . org-ascii-inner-template)
- (italic . org-ascii-italic)
- (item . org-ascii-item)
- (keyword . org-ascii-keyword)
- (latex-environment . org-ascii-latex-environment)
- (latex-fragment . org-ascii-latex-fragment)
- (line-break . org-ascii-line-break)
- (link . org-ascii-link)
- (node-property . org-ascii-node-property)
- (paragraph . org-ascii-paragraph)
- (plain-list . org-ascii-plain-list)
- (plain-text . org-ascii-plain-text)
- (planning . org-ascii-planning)
- (property-drawer . org-ascii-property-drawer)
- (quote-block . org-ascii-quote-block)
- (radio-target . org-ascii-radio-target)
- (section . org-ascii-section)
- (special-block . org-ascii-special-block)
- (src-block . org-ascii-src-block)
- (statistics-cookie . org-ascii-statistics-cookie)
- (strike-through . org-ascii-strike-through)
- (subscript . org-ascii-subscript)
- (superscript . org-ascii-superscript)
- (table . org-ascii-table)
- (table-cell . org-ascii-table-cell)
- (table-row . org-ascii-table-row)
- (target . org-ascii-target)
- (template . org-ascii-template)
- (timestamp . org-ascii-timestamp)
- (underline . org-ascii-underline)
- (verbatim . org-ascii-verbatim)
- (verse-block . org-ascii-verse-block))
- :menu-entry
- '(?t "Export to Plain Text"
- ((?A "As ASCII buffer"
- (lambda (a s v b)
- (org-ascii-export-as-ascii a s v b '(:ascii-charset ascii))))
- (?a "As ASCII file"
- (lambda (a s v b)
- (org-ascii-export-to-ascii a s v b '(:ascii-charset ascii))))
- (?L "As Latin1 buffer"
- (lambda (a s v b)
- (org-ascii-export-as-ascii a s v b '(:ascii-charset latin1))))
- (?l "As Latin1 file"
- (lambda (a s v b)
- (org-ascii-export-to-ascii a s v b '(:ascii-charset latin1))))
- (?U "As UTF-8 buffer"
- (lambda (a s v b)
- (org-ascii-export-as-ascii a s v b '(:ascii-charset utf-8))))
- (?u "As UTF-8 file"
- (lambda (a s v b)
- (org-ascii-export-to-ascii a s v b '(:ascii-charset utf-8))))))
- :filters-alist '((:filter-headline . org-ascii-filter-headline-blank-lines)
- (:filter-parse-tree org-ascii-filter-paragraph-spacing
- org-ascii-filter-comment-spacing)
- (:filter-section . org-ascii-filter-headline-blank-lines))
- :options-alist
- '((:subtitle "SUBTITLE" nil nil parse)
- (:ascii-bullets nil nil org-ascii-bullets)
- (:ascii-caption-above nil nil org-ascii-caption-above)
- (:ascii-charset nil nil org-ascii-charset)
- (:ascii-global-margin nil nil org-ascii-global-margin)
- (:ascii-format-drawer-function nil nil org-ascii-format-drawer-function)
- (:ascii-format-inlinetask-function
- nil nil org-ascii-format-inlinetask-function)
- (:ascii-headline-spacing nil nil org-ascii-headline-spacing)
- (:ascii-indented-line-width nil nil org-ascii-indented-line-width)
- (:ascii-inlinetask-width nil nil org-ascii-inlinetask-width)
- (:ascii-inner-margin nil nil org-ascii-inner-margin)
- (:ascii-links-to-notes nil nil org-ascii-links-to-notes)
- (:ascii-list-margin nil nil org-ascii-list-margin)
- (:ascii-paragraph-spacing nil nil org-ascii-paragraph-spacing)
- (:ascii-quote-margin nil nil org-ascii-quote-margin)
- (:ascii-table-keep-all-vertical-lines
- nil nil org-ascii-table-keep-all-vertical-lines)
- (:ascii-table-use-ascii-art nil nil org-ascii-table-use-ascii-art)
- (:ascii-table-widen-columns nil nil org-ascii-table-widen-columns)
- (:ascii-text-width nil nil org-ascii-text-width)
- (:ascii-underline nil nil org-ascii-underline)
- (:ascii-verbatim-format nil nil org-ascii-verbatim-format)))
- ;;; User Configurable Variables
- (defgroup org-export-ascii nil
- "Options for exporting Org mode files to ASCII."
- :tag "Org Export ASCII"
- :group 'org-export)
- (defcustom org-ascii-text-width 72
- "Maximum width of exported text.
- This number includes margin size, as set in
- `org-ascii-global-margin'."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'integer)
- (defcustom org-ascii-global-margin 0
- "Width of the left margin, in number of characters."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'integer)
- (defcustom org-ascii-inner-margin 2
- "Width of the inner margin, in number of characters.
- Inner margin is applied between each headline."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'integer)
- (defcustom org-ascii-quote-margin 6
- "Width of margin used for quoting text, in characters.
- This margin is applied on both sides of the text. It is also
- applied on the left side of contents in descriptive lists."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'integer)
- (defcustom org-ascii-list-margin 0
- "Width of margin used for plain lists, in characters.
- This margin applies to top level list only, not to its
- sub-lists."
- :group 'org-export-ascii
- :version "26.1"
- :package-version '(Org . "8.3")
- :type 'integer)
- (defcustom org-ascii-inlinetask-width 30
- "Width of inline tasks, in number of characters.
- This number ignores any margin."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'integer)
- (defcustom org-ascii-headline-spacing '(1 . 2)
- "Number of blank lines inserted around headlines.
- This variable can be set to a cons cell. In that case, its car
- represents the number of blank lines present before headline
- contents whereas its cdr reflects the number of blank lines after
- contents.
- A nil value replicates the number of blank lines found in the
- original Org buffer at the same place."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type '(choice
- (const :tag "Replicate original spacing" nil)
- (cons :tag "Set a uniform spacing"
- (integer :tag "Number of blank lines before contents")
- (integer :tag "Number of blank lines after contents"))))
- (defcustom org-ascii-indented-line-width 'auto
- "Additional indentation width for the first line in a paragraph.
- If the value is an integer, indent the first line of each
- paragraph by this width, unless it is located at the beginning of
- a section, in which case indentation is removed from that line.
- If it is the symbol `auto' preserve indentation from original
- document."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type '(choice
- (integer :tag "Number of white spaces characters")
- (const :tag "Preserve original width" auto)))
- (defcustom org-ascii-paragraph-spacing 'auto
- "Number of white lines between paragraphs.
- If the value is an integer, add this number of blank lines
- between contiguous paragraphs. If is it the symbol `auto', keep
- the same number of blank lines as in the original document."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type '(choice
- (integer :tag "Number of blank lines")
- (const :tag "Preserve original spacing" auto)))
- (defcustom org-ascii-charset 'ascii
- "The charset allowed to represent various elements and objects.
- Possible values are:
- `ascii' Only use plain ASCII characters
- `latin1' Include Latin-1 characters
- `utf-8' Use all UTF-8 characters"
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type '(choice
- (const :tag "ASCII" ascii)
- (const :tag "Latin-1" latin1)
- (const :tag "UTF-8" utf-8)))
- (defcustom org-ascii-underline '((ascii ?= ?~ ?-)
- (latin1 ?= ?~ ?-)
- (utf-8 ?═ ?─ ?╌ ?┄ ?┈))
- "Characters for underlining headings in ASCII export.
- Alist whose key is a symbol among `ascii', `latin1' and `utf-8'
- and whose value is a list of characters.
- For each supported charset, this variable associates a sequence
- of underline characters. In a sequence, the characters will be
- used in order for headlines level 1, 2, ... If no character is
- available for a given level, the headline won't be underlined."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type '(list
- (cons :tag "Underline characters sequence"
- (const :tag "ASCII charset" ascii)
- (repeat character))
- (cons :tag "Underline characters sequence"
- (const :tag "Latin-1 charset" latin1)
- (repeat character))
- (cons :tag "Underline characters sequence"
- (const :tag "UTF-8 charset" utf-8)
- (repeat character))))
- (defcustom org-ascii-bullets '((ascii ?* ?+ ?-)
- (latin1 ?§ ?¶)
- (utf-8 ?◊))
- "Bullet characters for headlines converted to lists in ASCII export.
- Alist whose key is a symbol among `ascii', `latin1' and `utf-8'
- and whose value is a list of characters.
- The first character is used for the first level considered as low
- level, and so on. If there are more levels than characters given
- here, the list will be repeated.
- Note that this variable doesn't affect plain lists
- representation."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type '(list
- (cons :tag "Bullet characters for low level headlines"
- (const :tag "ASCII charset" ascii)
- (repeat character))
- (cons :tag "Bullet characters for low level headlines"
- (const :tag "Latin-1 charset" latin1)
- (repeat character))
- (cons :tag "Bullet characters for low level headlines"
- (const :tag "UTF-8 charset" utf-8)
- (repeat character))))
- (defcustom org-ascii-links-to-notes t
- "Non-nil means convert links to notes before the next headline.
- When nil, the link will be exported in place. If the line
- becomes long in this way, it will be wrapped."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'boolean)
- (defcustom org-ascii-table-keep-all-vertical-lines nil
- "Non-nil means keep all vertical lines in ASCII tables.
- When nil, vertical lines will be removed except for those needed
- for column grouping."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'boolean)
- (defcustom org-ascii-table-widen-columns t
- "Non-nil means widen narrowed columns for export.
- When nil, narrowed columns will look in ASCII export just like in
- Org mode, i.e. with \"=>\" as ellipsis."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'boolean)
- (defcustom org-ascii-table-use-ascii-art nil
- "Non-nil means \"table.el\" tables are turned into ASCII art.
- It only makes sense when export charset is `utf-8'. It is nil by
- default since it requires \"ascii-art-to-unicode.el\" package,
- available through, e.g., GNU ELPA."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'boolean)
- (defcustom org-ascii-caption-above nil
- "When non-nil, place caption string before the element.
- Otherwise, place it right after it."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'boolean)
- (defcustom org-ascii-verbatim-format "`%s'"
- "Format string used for verbatim text and inline code."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'string)
- (defcustom org-ascii-format-drawer-function
- (lambda (_name contents _width) contents)
- "Function called to format a drawer in ASCII.
- The function must accept three parameters:
- NAME the drawer name, like \"LOGBOOK\"
- CONTENTS the contents of the drawer.
- WIDTH the text width within the drawer.
- The function should return either the string to be exported or
- nil to ignore the drawer.
- The default value simply returns the value of CONTENTS."
- :group 'org-export-ascii
- :version "24.4"
- :package-version '(Org . "8.0")
- :type 'function)
- (defcustom org-ascii-format-inlinetask-function
- 'org-ascii-format-inlinetask-default
- "Function called to format an inlinetask in ASCII.
- The function must accept nine parameters:
- TODO the todo keyword, as a string
- TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
- PRIORITY the inlinetask priority, as a string
- NAME the inlinetask name, as a string.
- TAGS the inlinetask tags, as a list of strings.
- CONTENTS the contents of the inlinetask, as a string.
- WIDTH the width of the inlinetask, as a number.
- INLINETASK the inlinetask itself.
- INFO the info channel.
- The function should return either the string to be exported or
- nil to ignore the inline task."
- :group 'org-export-ascii
- :version "26.1"
- :package-version '(Org . "8.3")
- :type 'function)
- ;;; Internal Functions
- ;; Internal functions fall into three categories.
- ;; The first one is about text formatting. The core functions are
- ;; `org-ascii--current-text-width' and
- ;; `org-ascii--current-justification', which determine, respectively,
- ;; the current text width allowed to a given element and its expected
- ;; justification. Once this information is known,
- ;; `org-ascii--fill-string', `org-ascii--justify-lines',
- ;; `org-ascii--justify-element' `org-ascii--box-string' and
- ;; `org-ascii--indent-string' can operate on a given output string.
- ;; In particular, justification happens at the regular (i.e.,
- ;; non-greater) element level, which means that when the exporting
- ;; process reaches a container (e.g., a center block) content are
- ;; already justified.
- ;; The second category contains functions handling elements listings,
- ;; triggered by "#+TOC:" keyword. As such, `org-ascii--build-toc'
- ;; returns a complete table of contents, `org-ascii--list-listings'
- ;; returns a list of referenceable src-block elements, and
- ;; `org-ascii--list-tables' does the same for table elements.
- ;; The third category includes general helper functions.
- ;; `org-ascii--build-title' creates the title for a given headline
- ;; or inlinetask element. `org-ascii--build-caption' returns the
- ;; caption string associated to a table or a src-block.
- ;; `org-ascii--describe-links' creates notes about links for
- ;; insertion at the end of a section. It uses
- ;; `org-ascii--unique-links' to get the list of links to describe.
- ;; Eventually, `org-ascii--translate' translates a string according
- ;; to language and charset specification.
- (defun org-ascii--fill-string (s text-width info &optional justify)
- "Fill a string with specified text-width and return it.
- S is the string being filled. TEXT-WIDTH is an integer
- specifying maximum length of a line. INFO is the plist used as
- a communication channel.
- Optional argument JUSTIFY can specify any type of justification
- among `left', `center', `right' or `full'. A nil value is
- equivalent to `left'. For a justification that doesn't also fill
- string, see `org-ascii--justify-lines' and
- `org-ascii--justify-block'.
- Return nil if S isn't a string."
- (when (stringp s)
- (let ((double-space-p sentence-end-double-space))
- (with-temp-buffer
- (let ((fill-column text-width)
- (use-hard-newlines t)
- (sentence-end-double-space double-space-p))
- (insert (if (plist-get info :preserve-breaks)
- (replace-regexp-in-string "\n" hard-newline s)
- s))
- (fill-region (point-min) (point-max) justify))
- (buffer-string)))))
- (defun org-ascii--justify-lines (s text-width how)
- "Justify all lines in string S.
- TEXT-WIDTH is an integer specifying maximum length of a line.
- HOW determines the type of justification: it can be `left',
- `right', `full' or `center'."
- (with-temp-buffer
- (insert s)
- (goto-char (point-min))
- (let ((fill-column text-width)
- ;; Ensure that `indent-tabs-mode' is nil so that indentation
- ;; will always be achieved using spaces rather than tabs.
- (indent-tabs-mode nil)
- ;; Disable `adaptive-fill-mode' so it doesn't prevent
- ;; filling lines matching `adaptive-fill-regexp'.
- (adaptive-fill-mode nil))
- (while (< (point) (point-max))
- (justify-current-line how)
- (forward-line)))
- (buffer-string)))
- (defun org-ascii--justify-element (contents element info)
- "Justify CONTENTS of ELEMENT.
- INFO is a plist used as a communication channel. Justification
- is done according to the type of element. More accurately,
- paragraphs are filled and other elements are justified as blocks,
- that is according to the widest non blank line in CONTENTS."
- (if (not (org-string-nw-p contents)) contents
- (let ((text-width (org-ascii--current-text-width element info))
- (how (org-ascii--current-justification element)))
- (cond
- ((eq (org-element-type element) 'paragraph)
- ;; Paragraphs are treated specially as they need to be filled.
- (org-ascii--fill-string contents text-width info how))
- ((eq how 'left) contents)
- (t (with-temp-buffer
- (insert contents)
- (goto-char (point-min))
- (catch 'exit
- (let ((max-width 0))
- ;; Compute maximum width. Bail out if it is greater
- ;; than page width, since no justification is
- ;; possible.
- (save-excursion
- (while (not (eobp))
- (unless (looking-at-p "[ \t]*$")
- (end-of-line)
- (let ((column (current-column)))
- (cond
- ((>= column text-width) (throw 'exit contents))
- ((> column max-width) (setq max-width column)))))
- (forward-line)))
- ;; Justify every line according to TEXT-WIDTH and
- ;; MAX-WIDTH.
- (let ((offset (/ (- text-width max-width)
- (if (eq how 'right) 1 2))))
- (if (zerop offset) (throw 'exit contents)
- (while (not (eobp))
- (unless (looking-at-p "[ \t]*$")
- (indent-to-column offset))
- (forward-line)))))
- (buffer-string))))))))
- (defun org-ascii--indent-string (s width)
- "Indent string S by WIDTH white spaces.
- Empty lines are not indented."
- (when (stringp s)
- (replace-regexp-in-string
- "\\(^\\)[ \t]*\\S-" (make-string width ?\s) s nil nil 1)))
- (defun org-ascii--box-string (s info)
- "Return string S with a partial box to its left.
- INFO is a plist used as a communication channel."
- (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
- (format (if utf8p "┌────\n%s\n└────" ",----\n%s\n`----")
- (replace-regexp-in-string
- "^" (if utf8p "│ " "| ")
- ;; Remove last newline character.
- (replace-regexp-in-string "\n[ \t]*\\'" "" s)))))
- (defun org-ascii--current-text-width (element info)
- "Return maximum text width for ELEMENT's contents.
- INFO is a plist used as a communication channel."
- (pcase (org-element-type element)
- ;; Elements with an absolute width: `headline' and `inlinetask'.
- (`inlinetask (plist-get info :ascii-inlinetask-width))
- (`headline
- (- (plist-get info :ascii-text-width)
- (let ((low-level-rank (org-export-low-level-p element info)))
- (if low-level-rank (* low-level-rank 2)
- (plist-get info :ascii-global-margin)))))
- ;; Elements with a relative width: store maximum text width in
- ;; TOTAL-WIDTH.
- (_
- (let* ((genealogy (org-element-lineage element nil t))
- ;; Total width is determined by the presence, or not, of an
- ;; inline task among ELEMENT parents.
- (total-width
- (if (cl-some (lambda (parent)
- (eq (org-element-type parent) 'inlinetask))
- genealogy)
- (plist-get info :ascii-inlinetask-width)
- ;; No inlinetask: Remove global margin from text width.
- (- (plist-get info :ascii-text-width)
- (plist-get info :ascii-global-margin)
- (let ((parent (org-export-get-parent-headline element)))
- ;; Inner margin doesn't apply to text before first
- ;; headline.
- (if (not parent) 0
- (let ((low-level-rank
- (org-export-low-level-p parent info)))
- ;; Inner margin doesn't apply to contents of
- ;; low level headlines, since they've got their
- ;; own indentation mechanism.
- (if low-level-rank (* low-level-rank 2)
- (plist-get info :ascii-inner-margin)))))))))
- (- total-width
- ;; Each `quote-block' and `verse-block' above narrows text
- ;; width by twice the standard margin size.
- (+ (* (cl-count-if (lambda (parent)
- (memq (org-element-type parent)
- '(quote-block verse-block)))
- genealogy)
- 2
- (plist-get info :ascii-quote-margin))
- ;; Apply list margin once per "top-level" plain-list
- ;; containing current line
- (* (cl-count-if
- (lambda (e)
- (and (eq (org-element-type e) 'plain-list)
- (not (eq (org-element-type (org-export-get-parent e))
- 'item))))
- genealogy)
- (plist-get info :ascii-list-margin))
- ;; Compute indentation offset due to current list. It is
- ;; `org-ascii-quote-margin' per descriptive item in the
- ;; genealogy, bullet's length otherwise.
- (let ((indentation 0))
- (dolist (e genealogy)
- (cond
- ((not (eq 'item (org-element-type e))))
- ((eq (org-element-property :type (org-export-get-parent e))
- 'descriptive)
- (cl-incf indentation org-ascii-quote-margin))
- (t
- (cl-incf indentation
- (+ (string-width
- (or (org-ascii--checkbox e info) ""))
- (string-width
- (org-element-property :bullet e)))))))
- indentation)))))))
- (defun org-ascii--current-justification (element)
- "Return expected justification for ELEMENT's contents.
- Return value is a symbol among `left', `center', `right' and
- `full'."
- (let (justification)
- (while (and (not justification)
- (setq element (org-element-property :parent element)))
- (pcase (org-element-type element)
- (`center-block (setq justification 'center))
- (`special-block
- (let ((name (org-element-property :type element)))
- (cond ((string= name "JUSTIFYRIGHT") (setq justification 'right))
- ((string= name "JUSTIFYLEFT") (setq justification 'left)))))))
- (or justification 'left)))
- (defun org-ascii--build-title
- (element info text-width &optional underline notags toc)
- "Format ELEMENT title and return it.
- ELEMENT is either an `headline' or `inlinetask' element. INFO is
- a plist used as a communication channel. TEXT-WIDTH is an
- integer representing the maximum length of a line.
- When optional argument UNDERLINE is non-nil, underline title,
- without the tags, according to `org-ascii-underline'
- specifications.
- If optional argument NOTAGS is non-nil, no tags will be added to
- the title.
- When optional argument TOC is non-nil, use optional title if
- possible. It doesn't apply to `inlinetask' elements."
- (let* ((headlinep (eq (org-element-type element) 'headline))
- (numbers
- ;; Numbering is specific to headlines.
- (and headlinep
- (org-export-numbered-headline-p element info)
- (let ((numbering (org-export-get-headline-number element info)))
- (if toc (format "%d. " (org-last numbering))
- (concat (mapconcat #'number-to-string numbering ".")
- " ")))))
- (text
- (org-trim
- (org-export-data
- (if (and toc headlinep) (org-export-get-alt-title element info)
- (org-element-property :title element))
- info)))
- (todo
- (and (plist-get info :with-todo-keywords)
- (let ((todo (org-element-property :todo-keyword element)))
- (and todo (concat (org-export-data todo info) " ")))))
- (tags (and (not notags)
- (plist-get info :with-tags)
- (let ((tag-list (org-export-get-tags element info)))
- (and tag-list
- (org-make-tag-string tag-list)))))
- (priority
- (and (plist-get info :with-priority)
- (let ((char (org-element-property :priority element)))
- (and char (format "(#%c) " char)))))
- (first-part (concat numbers todo priority text)))
- (concat
- first-part
- ;; Align tags, if any.
- (when tags
- (format
- (format " %%%ds"
- (max (- text-width (1+ (string-width first-part)))
- (string-width tags)))
- tags))
- ;; Maybe underline text, if ELEMENT type is `headline' and an
- ;; underline character has been defined.
- (when (and underline headlinep)
- (let ((under-char
- (nth (1- (org-export-get-relative-level element info))
- (cdr (assq (plist-get info :ascii-charset)
- (plist-get info :ascii-underline))))))
- (and under-char
- (concat "\n"
- (make-string (/ (string-width first-part)
- (char-width under-char))
- under-char))))))))
- (defun org-ascii--has-caption-p (element _info)
- "Non-nil when ELEMENT has a caption affiliated keyword.
- INFO is a plist used as a communication channel. This function
- is meant to be used as a predicate for `org-export-get-ordinal'."
- (org-element-property :caption element))
- (defun org-ascii--build-caption (element info)
- "Return caption string for ELEMENT, if applicable.
- INFO is a plist used as a communication channel.
- The caption string contains the sequence number of ELEMENT along
- with its real caption. Return nil when ELEMENT has no affiliated
- caption keyword."
- (let ((caption (org-export-get-caption element)))
- (when caption
- ;; Get sequence number of current src-block among every
- ;; src-block with a caption.
- (let ((reference
- (org-export-get-ordinal
- element info nil 'org-ascii--has-caption-p))
- (title-fmt (org-ascii--translate
- (pcase (org-element-type element)
- (`table "Table %d:")
- (`src-block "Listing %d:"))
- info)))
- (org-ascii--fill-string
- (concat (format title-fmt reference)
- " "
- (org-export-data caption info))
- (org-ascii--current-text-width element info) info)))))
- (defun org-ascii--build-toc (info &optional n keyword scope)
- "Return a table of contents.
- INFO is a plist used as a communication channel.
- Optional argument N, when non-nil, is an integer specifying the
- depth of the table.
- Optional argument KEYWORD specifies the TOC keyword, if any, from
- which the table of contents generation has been initiated.
- When optional argument SCOPE is non-nil, build a table of
- contents according to the specified scope."
- (concat
- (unless scope
- (let ((title (org-ascii--translate "Table of Contents" info)))
- (concat title "\n"
- (make-string
- (string-width title)
- (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
- "\n\n")))
- (let ((text-width
- (if keyword (org-ascii--current-text-width keyword info)
- (- (plist-get info :ascii-text-width)
- (plist-get info :ascii-global-margin)))))
- (mapconcat
- (lambda (headline)
- (let* ((level (org-export-get-relative-level headline info))
- (indent (* (1- level) 3)))
- (concat
- (unless (zerop indent) (concat (make-string (1- indent) ?.) " "))
- (org-ascii--build-title
- headline info (- text-width indent) nil
- (or (not (plist-get info :with-tags))
- (eq (plist-get info :with-tags) 'not-in-toc))
- 'toc))))
- (org-export-collect-headlines info n scope) "\n"))))
- (defun org-ascii--list-listings (keyword info)
- "Return a list of listings.
- KEYWORD is the keyword that initiated the list of listings
- generation. INFO is a plist used as a communication channel."
- (let ((title (org-ascii--translate "List of Listings" info)))
- (concat
- title "\n"
- (make-string (string-width title)
- (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
- "\n\n"
- (let ((text-width
- (if keyword (org-ascii--current-text-width keyword info)
- (- (plist-get info :ascii-text-width)
- (plist-get info :ascii-global-margin))))
- ;; Use a counter instead of retrieving ordinal of each
- ;; src-block.
- (count 0))
- (mapconcat
- (lambda (src-block)
- ;; Store initial text so its length can be computed. This is
- ;; used to properly align caption right to it in case of
- ;; filling (like contents of a description list item).
- (let* ((initial-text
- (format (org-ascii--translate "Listing %d:" info)
- (cl-incf count)))
- (initial-width (string-width initial-text)))
- (concat
- initial-text " "
- (org-trim
- (org-ascii--indent-string
- (org-ascii--fill-string
- ;; Use short name in priority, if available.
- (let ((caption (or (org-export-get-caption src-block t)
- (org-export-get-caption src-block))))
- (org-export-data caption info))
- (- text-width initial-width) info)
- initial-width)))))
- (org-export-collect-listings info) "\n")))))
- (defun org-ascii--list-tables (keyword info)
- "Return a list of tables.
- KEYWORD is the keyword that initiated the list of tables
- generation. INFO is a plist used as a communication channel."
- (let ((title (org-ascii--translate "List of Tables" info)))
- (concat
- title "\n"
- (make-string (string-width title)
- (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
- "\n\n"
- (let ((text-width
- (if keyword (org-ascii--current-text-width keyword info)
- (- (plist-get info :ascii-text-width)
- (plist-get info :ascii-global-margin))))
- ;; Use a counter instead of retrieving ordinal of each
- ;; src-block.
- (count 0))
- (mapconcat
- (lambda (table)
- ;; Store initial text so its length can be computed. This is
- ;; used to properly align caption right to it in case of
- ;; filling (like contents of a description list item).
- (let* ((initial-text
- (format (org-ascii--translate "Table %d:" info)
- (cl-incf count)))
- (initial-width (string-width initial-text)))
- (concat
- initial-text " "
- (org-trim
- (org-ascii--indent-string
- (org-ascii--fill-string
- ;; Use short name in priority, if available.
- (let ((caption (or (org-export-get-caption table t)
- (org-export-get-caption table))))
- (org-export-data caption info))
- (- text-width initial-width) info)
- initial-width)))))
- (org-export-collect-tables info) "\n")))))
- (defun org-ascii--unique-links (element info)
- "Return a list of unique link references in ELEMENT.
- ELEMENT is either a headline element or a section element. INFO
- is a plist used as a communication channel."
- (let* (seen
- (unique-link-p
- ;; Return LINK if it wasn't referenced so far, or nil.
- ;; Update SEEN links along the way.
- (lambda (link)
- (let ((footprint
- ;; Normalize description in footprints.
- (cons (org-element-property :raw-link link)
- (let ((contents (org-element-contents link)))
- (and contents
- (replace-regexp-in-string
- "[ \r\t\n]+" " "
- (org-trim
- (org-element-interpret-data contents))))))))
- ;; Ignore LINK if it hasn't been translated already. It
- ;; can happen if it is located in an affiliated keyword
- ;; that was ignored.
- (when (and (org-string-nw-p
- (gethash link (plist-get info :exported-data)))
- (not (member footprint seen)))
- (push footprint seen) link)))))
- (org-element-map (if (eq (org-element-type element) 'section)
- element
- ;; In a headline, only retrieve links in title
- ;; and relative section, not in children.
- (list (org-element-property :title element)
- (car (org-element-contents element))))
- 'link unique-link-p info nil 'headline t)))
- (defun org-ascii--describe-datum (datum info)
- "Describe DATUM object or element.
- If DATUM is a string, consider it to be a file name, per
- `org-export-resolve-id-link'. INFO is the communication channel,
- as a plist."
- (pcase (org-element-type datum)
- (`plain-text (format "See file %s" datum)) ;External file
- (`headline
- (format (org-ascii--translate "See section %s" info)
- (if (org-export-numbered-headline-p datum info)
- (mapconcat #'number-to-string
- (org-export-get-headline-number datum info)
- ".")
- (org-export-data (org-element-property :title datum) info))))
- (_
- (let ((number (org-export-get-ordinal
- datum info nil #'org-ascii--has-caption-p))
- ;; If destination is a target, make sure we can name the
- ;; container it refers to.
- (enumerable
- (org-element-lineage datum
- '(headline paragraph src-block table) t)))
- (pcase (org-element-type enumerable)
- (`headline
- (format (org-ascii--translate "See section %s" info)
- (if (org-export-numbered-headline-p enumerable info)
- (mapconcat #'number-to-string number ".")
- (org-export-data
- (org-element-property :title enumerable) info))))
- ((guard (not number))
- (org-ascii--translate "Unknown reference" info))
- (`paragraph
- (format (org-ascii--translate "See figure %s" info) number))
- (`src-block
- (format (org-ascii--translate "See listing %s" info) number))
- (`table
- (format (org-ascii--translate "See table %s" info) number))
- (_ (org-ascii--translate "Unknown reference" info)))))))
- (defun org-ascii--describe-links (links width info)
- "Return a string describing a list of links.
- LINKS is a list of link type objects, as returned by
- `org-ascii--unique-links'. WIDTH is the text width allowed for
- the output string. INFO is a plist used as a communication
- channel."
- (mapconcat
- (lambda (link)
- (let* ((type (org-element-property :type link))
- (description (org-element-contents link))
- (anchor (org-export-data
- (or description (org-element-property :raw-link link))
- info)))
- (cond
- ((member type '("coderef" "radio")) nil)
- ((member type '("custom-id" "fuzzy" "id"))
- ;; Only links with a description need an entry. Other are
- ;; already handled in `org-ascii-link'.
- (when description
- (let ((dest (if (equal type "fuzzy")
- (org-export-resolve-fuzzy-link link info)
- ;; Ignore broken links. On broken link,
- ;; `org-export-resolve-id-link' will throw an
- ;; error and we will return nil.
- (condition-case nil
- (org-export-resolve-id-link link info)
- (org-link-broken nil)))))
- (when dest
- (concat
- (org-ascii--fill-string
- (format "[%s] %s" anchor (org-ascii--describe-datum dest info))
- width info)
- "\n\n")))))
- ;; Do not add a link that cannot be resolved and doesn't have
- ;; any description: destination is already visible in the
- ;; paragraph.
- ((not (org-element-contents link)) nil)
- ;; Do not add a link already handled by custom export
- ;; functions.
- ((org-export-custom-protocol-maybe link anchor 'ascii info) nil)
- (t
- (concat
- (org-ascii--fill-string
- (format "[%s] <%s>" anchor (org-element-property :raw-link link))
- width info)
- "\n\n")))))
- links ""))
- (defun org-ascii--checkbox (item info)
- "Return checkbox string for ITEM or nil.
- INFO is a plist used as a communication channel."
- (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
- (pcase (org-element-property :checkbox item)
- (`on (if utf8p "☑ " "[X] "))
- (`off (if utf8p "☐ " "[ ] "))
- (`trans (if utf8p "☒ " "[-] ")))))
- ;;; Template
- (defun org-ascii-template--document-title (info)
- "Return document title, as a string.
- INFO is a plist used as a communication channel."
- (let* ((text-width (plist-get info :ascii-text-width))
- ;; Links in the title will not be resolved later, so we make
- ;; sure their path is located right after them.
- (info (org-combine-plists info '(:ascii-links-to-notes nil)))
- (with-title (plist-get info :with-title))
- (title (org-export-data
- (when with-title (plist-get info :title)) info))
- (subtitle (org-export-data
- (when with-title (plist-get info :subtitle)) info))
- (author (and (plist-get info :with-author)
- (let ((auth (plist-get info :author)))
- (and auth (org-export-data auth info)))))
- (email (and (plist-get info :with-email)
- (org-export-data (plist-get info :email) info)))
- (date (and (plist-get info :with-date)
- (org-export-data (org-export-get-date info) info))))
- ;; There are two types of title blocks depending on the presence
- ;; of a title to display.
- (if (string= title "")
- ;; Title block without a title. DATE is positioned at the top
- ;; right of the document, AUTHOR to the top left and EMAIL
- ;; just below.
- (cond
- ((and (org-string-nw-p date) (org-string-nw-p author))
- (concat
- author
- (make-string (- text-width (string-width date) (string-width author))
- ?\s)
- date
- (when (org-string-nw-p email) (concat "\n" email))
- "\n\n\n"))
- ((and (org-string-nw-p date) (org-string-nw-p email))
- (concat
- email
- (make-string (- text-width (string-width date) (string-width email))
- ?\s)
- date "\n\n\n"))
- ((org-string-nw-p date)
- (concat
- (org-ascii--justify-lines date text-width 'right)
- "\n\n\n"))
- ((and (org-string-nw-p author) (org-string-nw-p email))
- (concat author "\n" email "\n\n\n"))
- ((org-string-nw-p author) (concat author "\n\n\n"))
- ((org-string-nw-p email) (concat email "\n\n\n")))
- ;; Title block with a title. Document's TITLE, along with the
- ;; AUTHOR and its EMAIL are both overlined and an underlined,
- ;; centered. Date is just below, also centered.
- (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))
- ;; Format TITLE. It may be filled if it is too wide,
- ;; that is wider than the two thirds of the total width.
- (title-len (min (apply #'max
- (mapcar #'string-width
- (org-split-string
- (concat title "\n" subtitle) "\n")))
- (/ (* 2 text-width) 3)))
- (formatted-title (org-ascii--fill-string title title-len info))
- (formatted-subtitle (when (org-string-nw-p subtitle)
- (org-ascii--fill-string subtitle title-len info)))
- (line
- (make-string
- (min (+ (max title-len
- (string-width (or author ""))
- (string-width (or email "")))
- 2)
- text-width) (if utf8p ?━ ?_))))
- (org-ascii--justify-lines
- (concat line "\n"
- (unless utf8p "\n")
- (upcase formatted-title)
- (and formatted-subtitle (concat "\n" formatted-subtitle))
- (cond
- ((and (org-string-nw-p author) (org-string-nw-p email))
- (concat "\n\n" author "\n" email))
- ((org-string-nw-p author) (concat "\n\n" author))
- ((org-string-nw-p email) (concat "\n\n" email)))
- "\n" line
- (when (org-string-nw-p date) (concat "\n\n\n" date))
- "\n\n\n") text-width 'center)))))
- (defun org-ascii-inner-template (contents info)
- "Return complete document string after ASCII conversion.
- CONTENTS is the transcoded contents string. INFO is a plist
- holding export options."
- (org-element-normalize-string
- (let ((global-margin (plist-get info :ascii-global-margin)))
- (org-ascii--indent-string
- (concat
- ;; 1. Document's body.
- contents
- ;; 2. Footnote definitions.
- (let ((definitions (org-export-collect-footnote-definitions info))
- ;; Insert full links right inside the footnote definition
- ;; as they have no chance to be inserted later.
- (info (org-combine-plists info '(:ascii-links-to-notes nil))))
- (when definitions
- (concat
- "\n\n\n"
- (let ((title (org-ascii--translate "Footnotes" info)))
- (concat
- title "\n"
- (make-string
- (string-width title)
- (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))))
- "\n\n"
- (let ((text-width (- (plist-get info :ascii-text-width)
- global-margin)))
- (mapconcat
- (lambda (ref)
- (let ((id (format "[%s] " (car ref))))
- ;; Distinguish between inline definitions and
- ;; full-fledged definitions.
- (org-trim
- (let ((def (nth 2 ref)))
- (if (org-element-map def org-element-all-elements
- #'identity info 'first-match)
- ;; Full-fledged definition: footnote ID is
- ;; inserted inside the first parsed
- ;; paragraph (FIRST), if any, to be sure
- ;; filling will take it into consideration.
- (let ((first (car (org-element-contents def))))
- (if (not (eq (org-element-type first) 'paragraph))
- (concat id "\n" (org-export-data def info))
- (push id (nthcdr 2 first))
- (org-export-data def info)))
- ;; Fill paragraph once footnote ID is inserted
- ;; in order to have a correct length for first
- ;; line.
- (org-ascii--fill-string
- (concat id (org-export-data def info))
- text-width info))))))
- definitions "\n\n"))))))
- global-margin))))
- (defun org-ascii-template (contents info)
- "Return complete document string after ASCII conversion.
- CONTENTS is the transcoded contents string. INFO is a plist
- holding export options."
- (let ((global-margin (plist-get info :ascii-global-margin)))
- (concat
- ;; Build title block.
- (org-ascii--indent-string
- (concat (org-ascii-template--document-title info)
- ;; 2. Table of contents.
- (let ((depth (plist-get info :with-toc)))
- (when depth
- (concat
- (org-ascii--build-toc info (and (wholenump depth) depth))
- "\n\n\n"))))
- global-margin)
- ;; Document's body.
- contents
- ;; Creator. Justify it to the bottom right.
- (and (plist-get info :with-creator)
- (org-ascii--indent-string
- (let ((text-width
- (- (plist-get info :ascii-text-width) global-margin)))
- (concat
- "\n\n\n"
- (org-ascii--fill-string
- (plist-get info :creator) text-width info 'right)))
- global-margin)))))
- (defun org-ascii--translate (s info)
- "Translate string S according to specified language and charset.
- INFO is a plist used as a communication channel."
- (let ((charset (intern (format ":%s" (plist-get info :ascii-charset)))))
- (org-export-translate s charset info)))
- ;;; Transcode Functions
- ;;;; Bold
- (defun org-ascii-bold (_bold contents _info)
- "Transcode BOLD from Org to ASCII.
- CONTENTS is the text with bold markup. INFO is a plist holding
- contextual information."
- (format "*%s*" contents))
- ;;;; Center Block
- (defun org-ascii-center-block (_center-block contents _info)
- "Transcode a CENTER-BLOCK element from Org to ASCII.
- CONTENTS holds the contents of the block. INFO is a plist
- holding contextual information."
- ;; Center has already been taken care of at a lower level, so
- ;; there's nothing left to do.
- contents)
- ;;;; Clock
- (defun org-ascii-clock (clock _contents info)
- "Transcode a CLOCK object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual
- information."
- (org-ascii--justify-element
- (concat org-clock-string " "
- (org-timestamp-translate (org-element-property :value clock))
- (let ((time (org-element-property :duration clock)))
- (and time
- (concat " => "
- (apply 'format
- "%2s:%02s"
- (org-split-string time ":"))))))
- clock info))
- ;;;; Code
- (defun org-ascii-code (code _contents info)
- "Return a CODE object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual
- information."
- (format (plist-get info :ascii-verbatim-format)
- (org-element-property :value code)))
- ;;;; Drawer
- (defun org-ascii-drawer (drawer contents info)
- "Transcode a DRAWER element from Org to ASCII.
- CONTENTS holds the contents of the block. INFO is a plist
- holding contextual information."
- (let ((name (org-element-property :drawer-name drawer))
- (width (org-ascii--current-text-width drawer info)))
- (funcall (plist-get info :ascii-format-drawer-function)
- name contents width)))
- ;;;; Dynamic Block
- (defun org-ascii-dynamic-block (_dynamic-block contents _info)
- "Transcode a DYNAMIC-BLOCK element from Org to ASCII.
- CONTENTS holds the contents of the block. INFO is a plist
- holding contextual information."
- contents)
- ;;;; Entity
- (defun org-ascii-entity (entity _contents info)
- "Transcode an ENTITY object from Org to ASCII.
- CONTENTS are the definition itself. INFO is a plist holding
- contextual information."
- (org-element-property
- (intern (concat ":" (symbol-name (plist-get info :ascii-charset))))
- entity))
- ;;;; Example Block
- (defun org-ascii-example-block (example-block _contents info)
- "Transcode a EXAMPLE-BLOCK element from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual information."
- (org-ascii--justify-element
- (org-ascii--box-string
- (org-export-format-code-default example-block info) info)
- example-block info))
- ;;;; Export Snippet
- (defun org-ascii-export-snippet (export-snippet _contents _info)
- "Transcode a EXPORT-SNIPPET object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual information."
- (when (eq (org-export-snippet-backend export-snippet) 'ascii)
- (org-element-property :value export-snippet)))
- ;;;; Export Block
- (defun org-ascii-export-block (export-block _contents info)
- "Transcode a EXPORT-BLOCK element from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual information."
- (when (string= (org-element-property :type export-block) "ASCII")
- (org-ascii--justify-element
- (org-element-property :value export-block) export-block info)))
- ;;;; Fixed Width
- (defun org-ascii-fixed-width (fixed-width _contents info)
- "Transcode a FIXED-WIDTH element from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual information."
- (org-ascii--justify-element
- (org-ascii--box-string
- (org-remove-indentation
- (org-element-property :value fixed-width))
- info)
- fixed-width info))
- ;;;; Footnote Definition
- ;; Footnote Definitions are ignored. They are compiled at the end of
- ;; the document, by `org-ascii-inner-template'.
- ;;;; Footnote Reference
- (defun org-ascii-footnote-reference (footnote-reference _contents info)
- "Transcode a FOOTNOTE-REFERENCE element from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual information."
- (format "[%s]" (org-export-get-footnote-number footnote-reference info)))
- ;;;; Headline
- (defun org-ascii-headline (headline contents info)
- "Transcode a HEADLINE element from Org to ASCII.
- CONTENTS holds the contents of the headline. INFO is a plist
- holding contextual information."
- ;; Don't export footnote section, which will be handled at the end
- ;; of the template.
- (unless (org-element-property :footnote-section-p headline)
- (let* ((low-level (org-export-low-level-p headline info))
- (width (org-ascii--current-text-width headline info))
- ;; Export title early so that any link in it can be
- ;; exported and seen in `org-ascii--unique-links'.
- (title (org-ascii--build-title headline info width (not low-level)))
- ;; Blank lines between headline and its contents.
- ;; `org-ascii-headline-spacing', when set, overwrites
- ;; original buffer's spacing.
- (pre-blanks
- (make-string (or (car (plist-get info :ascii-headline-spacing))
- (org-element-property :pre-blank headline)
- 0)
- ?\n))
- (links (and (plist-get info :ascii-links-to-notes)
- (org-ascii--describe-links
- (org-ascii--unique-links headline info) width info)))
- ;; Re-build contents, inserting section links at the right
- ;; place. The cost is low since build results are cached.
- (body
- (if (not (org-string-nw-p links)) contents
- (let* ((contents (org-element-contents headline))
- (section (let ((first (car contents)))
- (and (eq (org-element-type first) 'section)
- first))))
- (concat (and section
- (concat (org-element-normalize-string
- (org-export-data section info))
- "\n\n"))
- links
- (mapconcat (lambda (e) (org-export-data e info))
- (if section (cdr contents) contents)
- ""))))))
- ;; Deep subtree: export it as a list item.
- (if low-level
- (let* ((bullets (cdr (assq (plist-get info :ascii-charset)
- (plist-get info :ascii-bullets))))
- (bullet
- (format "%c "
- (nth (mod (1- low-level) (length bullets)) bullets))))
- (concat bullet title "\n" pre-blanks
- ;; Contents, indented by length of bullet.
- (org-ascii--indent-string body (length bullet))))
- ;; Else: Standard headline.
- (concat title "\n" pre-blanks body)))))
- ;;;; Horizontal Rule
- (defun org-ascii-horizontal-rule (horizontal-rule _contents info)
- "Transcode an HORIZONTAL-RULE object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual
- information."
- (let ((text-width (org-ascii--current-text-width horizontal-rule info))
- (spec-width
- (org-export-read-attribute :attr_ascii horizontal-rule :width)))
- (org-ascii--justify-lines
- (make-string (if (and spec-width (string-match "^[0-9]+$" spec-width))
- (string-to-number spec-width)
- text-width)
- (if (eq (plist-get info :ascii-charset) 'utf-8) ?― ?-))
- text-width 'center)))
- ;;;; Inline Src Block
- (defun org-ascii-inline-src-block (inline-src-block _contents info)
- "Transcode an INLINE-SRC-BLOCK element from Org to ASCII.
- CONTENTS holds the contents of the item. INFO is a plist holding
- contextual information."
- (format (plist-get info :ascii-verbatim-format)
- (org-element-property :value inline-src-block)))
- ;;;; Inlinetask
- (defun org-ascii-format-inlinetask-default
- (_todo _type _priority _name _tags contents width inlinetask info)
- "Format an inline task element for ASCII export.
- See `org-ascii-format-inlinetask-function' for a description
- of the parameters."
- (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))
- (width (or width (plist-get info :ascii-inlinetask-width))))
- (org-ascii--indent-string
- (concat
- ;; Top line, with an additional blank line if not in UTF-8.
- (make-string width (if utf8p ?━ ?_)) "\n"
- (unless utf8p (concat (make-string width ? ) "\n"))
- ;; Add title. Fill it if wider than inlinetask.
- (let ((title (org-ascii--build-title inlinetask info width)))
- (if (<= (string-width title) width) title
- (org-ascii--fill-string title width info)))
- "\n"
- ;; If CONTENTS is not empty, insert it along with
- ;; a separator.
- (when (org-string-nw-p contents)
- (concat (make-string width (if utf8p ?─ ?-)) "\n" contents))
- ;; Bottom line.
- (make-string width (if utf8p ?━ ?_)))
- ;; Flush the inlinetask to the right.
- (- (plist-get info :ascii-text-width) (plist-get info :ascii-global-margin)
- (if (not (org-export-get-parent-headline inlinetask)) 0
- (plist-get info :ascii-inner-margin))
- (org-ascii--current-text-width inlinetask info)))))
- (defun org-ascii-inlinetask (inlinetask contents info)
- "Transcode an INLINETASK element from Org to ASCII.
- CONTENTS holds the contents of the block. INFO is a plist
- holding contextual information."
- (let ((width (org-ascii--current-text-width inlinetask info)))
- (funcall (plist-get info :ascii-format-inlinetask-function)
- ;; todo.
- (and (plist-get info :with-todo-keywords)
- (let ((todo (org-element-property
- :todo-keyword inlinetask)))
- (and todo (org-export-data todo info))))
- ;; todo-type
- (org-element-property :todo-type inlinetask)
- ;; priority
- (and (plist-get info :with-priority)
- (org-element-property :priority inlinetask))
- ;; title
- (org-export-data (org-element-property :title inlinetask) info)
- ;; tags
- (and (plist-get info :with-tags)
- (org-element-property :tags inlinetask))
- ;; contents and width
- contents width inlinetask info)))
- ;;;; Italic
- (defun org-ascii-italic (_italic contents _info)
- "Transcode italic from Org to ASCII.
- CONTENTS is the text with italic markup. INFO is a plist holding
- contextual information."
- (format "/%s/" contents))
- ;;;; Item
- (defun org-ascii-item (item contents info)
- "Transcode an ITEM element from Org to ASCII.
- CONTENTS holds the contents of the item. INFO is a plist holding
- contextual information."
- (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))
- (checkbox (org-ascii--checkbox item info))
- (list-type (org-element-property :type (org-export-get-parent item)))
- (bullet
- ;; First parent of ITEM is always the plain-list. Get
- ;; `:type' property from it.
- (pcase list-type
- (`descriptive
- (concat checkbox
- (org-export-data (org-element-property :tag item)
- info)))
- (`ordered
- ;; Return correct number for ITEM, paying attention to
- ;; counters.
- (let* ((struct (org-element-property :structure item))
- (bul (org-list-bullet-string
- (org-element-property :bullet item)))
- (num (number-to-string
- (car (last (org-list-get-item-number
- (org-element-property :begin item)
- struct
- (org-list-prevs-alist struct)
- (org-list-parents-alist struct)))))))
- (replace-regexp-in-string "[0-9]+" num bul)))
- (_ (let ((bul (org-list-bullet-string
- (org-element-property :bullet item))))
- ;; Change bullets into more visible form if UTF-8 is active.
- (if (not utf8p) bul
- (replace-regexp-in-string
- "-" "•"
- (replace-regexp-in-string
- "\\+" "⁃"
- (replace-regexp-in-string "\\*" "‣" bul))))))))
- (indentation (if (eq list-type 'descriptive) org-ascii-quote-margin
- (string-width bullet))))
- (concat
- bullet
- checkbox
- ;; Contents: Pay attention to indentation. Note: check-boxes are
- ;; already taken care of at the paragraph level so they don't
- ;; interfere with indentation.
- (let ((contents (org-ascii--indent-string contents indentation)))
- ;; Determine if contents should follow the bullet or start
- ;; a new line. Do the former when the first contributing
- ;; element to contents is a paragraph. In descriptive lists
- ;; however, contents always start a new line.
- (if (and (not (eq list-type 'descriptive))
- (org-string-nw-p contents)
- (eq 'paragraph
- (org-element-type
- (cl-some (lambda (e)
- (and (org-string-nw-p (org-export-data e info))
- e))
- (org-element-contents item)))))
- (org-trim contents)
- (concat "\n" contents))))))
- ;;;; Keyword
- (defun org-ascii-keyword (keyword _contents info)
- "Transcode a KEYWORD element from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual
- information."
- (let ((key (org-element-property :key keyword))
- (value (org-element-property :value keyword)))
- (cond
- ((string= key "ASCII") (org-ascii--justify-element value keyword info))
- ((string= key "TOC")
- (org-ascii--justify-element
- (let ((case-fold-search t))
- (cond
- ((string-match-p "\\<headlines\\>" value)
- (let ((depth (and (string-match "\\<[0-9]+\\>" value)
- (string-to-number (match-string 0 value))))
- (scope
- (cond
- ((string-match ":target +\\(\".+?\"\\|\\S-+\\)" value) ;link
- (org-export-resolve-link
- (org-strip-quotes (match-string 1 value)) info))
- ((string-match-p "\\<local\\>" value) keyword)))) ;local
- (org-ascii--build-toc info depth keyword scope)))
- ((string-match-p "\\<tables\\>" value)
- (org-ascii--list-tables keyword info))
- ((string-match-p "\\<listings\\>" value)
- (org-ascii--list-listings keyword info))))
- keyword info)))))
- ;;;; Latex Environment
- (defun org-ascii-latex-environment (latex-environment _contents info)
- "Transcode a LATEX-ENVIRONMENT element from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual
- information."
- (when (plist-get info :with-latex)
- (org-ascii--justify-element
- (org-remove-indentation (org-element-property :value latex-environment))
- latex-environment info)))
- ;;;; Latex Fragment
- (defun org-ascii-latex-fragment (latex-fragment _contents info)
- "Transcode a LATEX-FRAGMENT object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual
- information."
- (when (plist-get info :with-latex)
- (org-element-property :value latex-fragment)))
- ;;;; Line Break
- (defun org-ascii-line-break (_line-break _contents _info)
- "Transcode a LINE-BREAK object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual
- information." hard-newline)
- ;;;; Link
- (defun org-ascii-link (link desc info)
- "Transcode a LINK object from Org to ASCII.
- DESC is the description part of the link, or the empty string.
- INFO is a plist holding contextual information."
- (let ((type (org-element-property :type link)))
- (cond
- ((org-export-custom-protocol-maybe link desc 'ascii info))
- ((string= type "coderef")
- (let ((ref (org-element-property :path link)))
- (format (org-export-get-coderef-format ref desc)
- (org-export-resolve-coderef ref info))))
- ;; Do not apply a special syntax on radio links. Though, use
- ;; transcoded target's contents as output.
- ((string= type "radio") desc)
- ((member type '("custom-id" "fuzzy" "id"))
- (let ((destination (if (string= type "fuzzy")
- (org-export-resolve-fuzzy-link link info)
- (org-export-resolve-id-link link info))))
- (pcase (org-element-type destination)
- ((guard desc)
- (if (plist-get info :ascii-links-to-notes)
- (format "[%s]" desc)
- (concat desc
- (format " (%s)"
- (org-ascii--describe-datum destination info)))))
- ;; External file.
- (`plain-text destination)
- (`headline
- (if (org-export-numbered-headline-p destination info)
- (mapconcat #'number-to-string
- (org-export-get-headline-number destination info)
- ".")
- (org-export-data (org-element-property :title destination) info)))
- ;; Handle enumerable elements and targets within them.
- ((and (let number (org-export-get-ordinal
- destination info nil #'org-ascii--has-caption-p))
- (guard number))
- (if (atom number) (number-to-string number)
- (mapconcat #'number-to-string number ".")))
- ;; Don't know what to do. Signal it.
- (_ "???"))))
- (t
- (let ((path (org-element-property :raw-link link)))
- (if (not (org-string-nw-p desc)) (format "<%s>" path)
- (concat (format "[%s]" desc)
- (and (not (plist-get info :ascii-links-to-notes))
- (format " (<%s>)" path)))))))))
- ;;;; Node Properties
- (defun org-ascii-node-property (node-property _contents _info)
- "Transcode a NODE-PROPERTY element from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual
- information."
- (format "%s:%s"
- (org-element-property :key node-property)
- (let ((value (org-element-property :value node-property)))
- (if value (concat " " value) ""))))
- ;;;; Paragraph
- (defun org-ascii-paragraph (paragraph contents info)
- "Transcode a PARAGRAPH element from Org to ASCII.
- CONTENTS is the contents of the paragraph, as a string. INFO is
- the plist used as a communication channel."
- (org-ascii--justify-element
- (let ((indented-line-width (plist-get info :ascii-indented-line-width)))
- (if (not (wholenump indented-line-width)) contents
- (concat
- ;; Do not indent first paragraph in a section.
- (unless (and (not (org-export-get-previous-element paragraph info))
- (eq (org-element-type (org-export-get-parent paragraph))
- 'section))
- (make-string indented-line-width ?\s))
- (replace-regexp-in-string "\\`[ \t]+" "" contents))))
- paragraph info))
- ;;;; Plain List
- (defun org-ascii-plain-list (plain-list contents info)
- "Transcode a PLAIN-LIST element from Org to ASCII.
- CONTENTS is the contents of the list. INFO is a plist holding
- contextual information."
- (let ((margin (plist-get info :ascii-list-margin)))
- (if (or (< margin 1)
- (eq (org-element-type (org-export-get-parent plain-list)) 'item))
- contents
- (org-ascii--indent-string contents margin))))
- ;;;; Plain Text
- (defun org-ascii-plain-text (text info)
- "Transcode a TEXT string from Org to ASCII.
- INFO is a plist used as a communication channel."
- (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
- (when (and utf8p (plist-get info :with-smart-quotes))
- (setq text (org-export-activate-smart-quotes text :utf-8 info)))
- (if (not (plist-get info :with-special-strings)) text
- (setq text (replace-regexp-in-string "\\\\-" "" text))
- (if (not utf8p) text
- ;; Usual replacements in utf-8 with proper option set.
- (replace-regexp-in-string
- "\\.\\.\\." "…"
- (replace-regexp-in-string
- "--" "–"
- (replace-regexp-in-string "---" "—" text)))))))
- ;;;; Planning
- (defun org-ascii-planning (planning _contents info)
- "Transcode a PLANNING element from Org to ASCII.
- CONTENTS is nil. INFO is a plist used as a communication
- channel."
- (org-ascii--justify-element
- (mapconcat
- #'identity
- (delq nil
- (list (let ((closed (org-element-property :closed planning)))
- (when closed
- (concat org-closed-string " "
- (org-timestamp-translate closed))))
- (let ((deadline (org-element-property :deadline planning)))
- (when deadline
- (concat org-deadline-string " "
- (org-timestamp-translate deadline))))
- (let ((scheduled (org-element-property :scheduled planning)))
- (when scheduled
- (concat org-scheduled-string " "
- (org-timestamp-translate scheduled))))))
- " ")
- planning info))
- ;;;; Property Drawer
- (defun org-ascii-property-drawer (property-drawer contents info)
- "Transcode a PROPERTY-DRAWER element from Org to ASCII.
- CONTENTS holds the contents of the drawer. INFO is a plist
- holding contextual information."
- (and (org-string-nw-p contents)
- (org-ascii--justify-element contents property-drawer info)))
- ;;;; Quote Block
- (defun org-ascii-quote-block (_quote-block contents info)
- "Transcode a QUOTE-BLOCK element from Org to ASCII.
- CONTENTS holds the contents of the block. INFO is a plist
- holding contextual information."
- (org-ascii--indent-string contents (plist-get info :ascii-quote-margin)))
- ;;;; Radio Target
- (defun org-ascii-radio-target (_radio-target contents _info)
- "Transcode a RADIO-TARGET object from Org to ASCII.
- CONTENTS is the contents of the target. INFO is a plist holding
- contextual information."
- contents)
- ;;;; Section
- (defun org-ascii-section (section contents info)
- "Transcode a SECTION element from Org to ASCII.
- CONTENTS is the contents of the section. INFO is a plist holding
- contextual information."
- (let ((links
- (and (plist-get info :ascii-links-to-notes)
- ;; Take care of links in first section of the document.
- (not (org-element-lineage section '(headline)))
- (org-ascii--describe-links
- (org-ascii--unique-links section info)
- (org-ascii--current-text-width section info)
- info))))
- (org-ascii--indent-string
- (if (not (org-string-nw-p links)) contents
- (concat (org-element-normalize-string contents) "\n\n" links))
- ;; Do not apply inner margin if parent headline is low level.
- (let ((headline (org-export-get-parent-headline section)))
- (if (or (not headline) (org-export-low-level-p headline info)) 0
- (plist-get info :ascii-inner-margin))))))
- ;;;; Special Block
- (defun org-ascii-special-block (_special-block contents _info)
- "Transcode a SPECIAL-BLOCK element from Org to ASCII.
- CONTENTS holds the contents of the block. INFO is a plist
- holding contextual information."
- ;; "JUSTIFYLEFT" and "JUSTIFYRIGHT" have already been taken care of
- ;; at a lower level. There is no other special block type to
- ;; handle.
- contents)
- ;;;; Src Block
- (defun org-ascii-src-block (src-block _contents info)
- "Transcode a SRC-BLOCK element from Org to ASCII.
- CONTENTS holds the contents of the item. INFO is a plist holding
- contextual information."
- (let ((caption (org-ascii--build-caption src-block info))
- (caption-above-p (plist-get info :ascii-caption-above))
- (code (org-export-format-code-default src-block info)))
- (if (equal code "") ""
- (org-ascii--justify-element
- (concat
- (and caption caption-above-p (concat caption "\n"))
- (org-ascii--box-string code info)
- (and caption (not caption-above-p) (concat "\n" caption)))
- src-block info))))
- ;;;; Statistics Cookie
- (defun org-ascii-statistics-cookie (statistics-cookie _contents _info)
- "Transcode a STATISTICS-COOKIE object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual information."
- (org-element-property :value statistics-cookie))
- ;;;; Subscript
- (defun org-ascii-subscript (subscript contents _info)
- "Transcode a SUBSCRIPT object from Org to ASCII.
- CONTENTS is the contents of the object. INFO is a plist holding
- contextual information."
- (if (org-element-property :use-brackets-p subscript)
- (format "_{%s}" contents)
- (format "_%s" contents)))
- ;;;; Superscript
- (defun org-ascii-superscript (superscript contents _info)
- "Transcode a SUPERSCRIPT object from Org to ASCII.
- CONTENTS is the contents of the object. INFO is a plist holding
- contextual information."
- (if (org-element-property :use-brackets-p superscript)
- (format "^{%s}" contents)
- (format "^%s" contents)))
- ;;;; Strike-through
- (defun org-ascii-strike-through (_strike-through contents _info)
- "Transcode STRIKE-THROUGH from Org to ASCII.
- CONTENTS is text with strike-through markup. INFO is a plist
- holding contextual information."
- (format "+%s+" contents))
- ;;;; Table
- (defun org-ascii-table (table contents info)
- "Transcode a TABLE element from Org to ASCII.
- CONTENTS is the contents of the table. INFO is a plist holding
- contextual information."
- (let ((caption (org-ascii--build-caption table info))
- (caption-above-p (plist-get info :ascii-caption-above)))
- (org-ascii--justify-element
- (concat
- ;; Possibly add a caption string above.
- (and caption caption-above-p (concat caption "\n"))
- ;; Insert table. Note: "table.el" tables are left unmodified.
- (cond ((eq (org-element-property :type table) 'org) contents)
- ((and (plist-get info :ascii-table-use-ascii-art)
- (eq (plist-get info :ascii-charset) 'utf-8)
- (require 'ascii-art-to-unicode nil t))
- (with-temp-buffer
- (insert (org-remove-indentation
- (org-element-property :value table)))
- (goto-char (point-min))
- (aa2u)
- (goto-char (point-max))
- (skip-chars-backward " \r\t\n")
- (buffer-substring (point-min) (point))))
- (t (org-remove-indentation (org-element-property :value table))))
- ;; Possible add a caption string below.
- (and (not caption-above-p) caption))
- table info)))
- ;;;; Table Cell
- (defun org-ascii--table-cell-width (table-cell info)
- "Return width of TABLE-CELL.
- INFO is a plist used as a communication channel.
- Width of a cell is determined either by a width cookie in the
- same column as the cell, or by the maximum cell's length in that
- column.
- When `org-ascii-table-widen-columns' is non-nil, width cookies
- are ignored."
- (let* ((row (org-export-get-parent table-cell))
- (table (org-export-get-parent row))
- (col (let ((cells (org-element-contents row)))
- (- (length cells) (length (memq table-cell cells)))))
- (cache
- (or (plist-get info :ascii-table-cell-width-cache)
- (plist-get (setq info
- (plist-put info :ascii-table-cell-width-cache
- (make-hash-table :test 'equal)))
- :ascii-table-cell-width-cache)))
- (key (cons table col))
- (widenp (plist-get info :ascii-table-widen-columns)))
- (or (gethash key cache)
- (puthash
- key
- (let ((cookie-width (org-export-table-cell-width table-cell info)))
- (or (and (not widenp) cookie-width)
- (let ((contents-width
- (let ((max-width 0))
- (org-element-map table 'table-row
- (lambda (row)
- (setq max-width
- (max (string-width
- (org-export-data
- (org-element-contents
- (elt (org-element-contents row) col))
- info))
- max-width)))
- info)
- max-width)))
- (cond ((not cookie-width) contents-width)
- (widenp (max cookie-width contents-width))
- (t cookie-width)))))
- cache))))
- (defun org-ascii-table-cell (table-cell contents info)
- "Transcode a TABLE-CELL object from Org to ASCII.
- CONTENTS is the cell contents. INFO is a plist used as
- a communication channel."
- ;; Determine column width. When `org-ascii-table-widen-columns'
- ;; is nil and some width cookie has set it, use that value.
- ;; Otherwise, compute the maximum width among transcoded data of
- ;; each cell in the column.
- (let ((width (org-ascii--table-cell-width table-cell info)))
- ;; When contents are too large, truncate them.
- (unless (or (plist-get info :ascii-table-widen-columns)
- (<= (string-width (or contents "")) width))
- (setq contents (concat (substring contents 0 (- width 2)) "=>")))
- ;; Align contents correctly within the cell.
- (let* ((indent-tabs-mode nil)
- (data
- (when contents
- (org-ascii--justify-lines
- contents width
- (org-export-table-cell-alignment table-cell info)))))
- (setq contents
- (concat data
- ;; FIXME: If CONTENTS was transformed by filters,
- ;; the whole width calculation can be wrong.
- ;; At least, make sure that we do not throw error
- ;; when CONTENTS is larger than width.
- (make-string (max 0 (- width (string-width (or data "")))) ?\s))))
- ;; Return cell.
- (concat (format " %s " contents)
- (when (memq 'right (org-export-table-cell-borders table-cell info))
- (if (eq (plist-get info :ascii-charset) 'utf-8) "│" "|")))))
- ;;;; Table Row
- (defun org-ascii-table-row (table-row contents info)
- "Transcode a TABLE-ROW element from Org to ASCII.
- CONTENTS is the row contents. INFO is a plist used as
- a communication channel."
- (when (eq (org-element-property :type table-row) 'standard)
- (let ((build-hline
- (lambda (lcorner horiz vert rcorner)
- (concat
- (apply
- 'concat
- (org-element-map table-row 'table-cell
- (lambda (cell)
- (let ((width (org-ascii--table-cell-width cell info))
- (borders (org-export-table-cell-borders cell info)))
- (concat
- ;; In order to know if CELL starts the row, do
- ;; not compare it with the first cell in the
- ;; row as there might be a special column.
- ;; Instead, compare it with first exportable
- ;; cell, obtained with `org-element-map'.
- (when (and (memq 'left borders)
- (eq (org-element-map table-row 'table-cell
- 'identity info t)
- cell))
- lcorner)
- (make-string (+ 2 width) (string-to-char horiz))
- (cond
- ((not (memq 'right borders)) nil)
- ((eq (car (last (org-element-contents table-row))) cell)
- rcorner)
- (t vert)))))
- info)) "\n")))
- (utf8p (eq (plist-get info :ascii-charset) 'utf-8))
- (borders (org-export-table-cell-borders
- (org-element-map table-row 'table-cell 'identity info t)
- info)))
- (concat (cond
- ((and (memq 'top borders) (or utf8p (memq 'above borders)))
- (if utf8p (funcall build-hline "┍" "━" "┯" "┑")
- (funcall build-hline "+" "-" "+" "+")))
- ((memq 'above borders)
- (if utf8p (funcall build-hline "├" "─" "┼" "┤")
- (funcall build-hline "+" "-" "+" "+"))))
- (when (memq 'left borders) (if utf8p "│" "|"))
- contents "\n"
- (when (and (memq 'bottom borders) (or utf8p (memq 'below borders)))
- (if utf8p (funcall build-hline "┕" "━" "┷" "┙")
- (funcall build-hline "+" "-" "+" "+")))))))
- ;;;; Timestamp
- (defun org-ascii-timestamp (timestamp _contents info)
- "Transcode a TIMESTAMP object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual information."
- (org-ascii-plain-text (org-timestamp-translate timestamp) info))
- ;;;; Underline
- (defun org-ascii-underline (_underline contents _info)
- "Transcode UNDERLINE from Org to ASCII.
- CONTENTS is the text with underline markup. INFO is a plist
- holding contextual information."
- (format "_%s_" contents))
- ;;;; Verbatim
- (defun org-ascii-verbatim (verbatim _contents info)
- "Return a VERBATIM object from Org to ASCII.
- CONTENTS is nil. INFO is a plist holding contextual information."
- (format (plist-get info :ascii-verbatim-format)
- (org-element-property :value verbatim)))
- ;;;; Verse Block
- (defun org-ascii-verse-block (verse-block contents info)
- "Transcode a VERSE-BLOCK element from Org to ASCII.
- CONTENTS is verse block contents. INFO is a plist holding
- contextual information."
- (org-ascii--indent-string
- (org-ascii--justify-element contents verse-block info)
- (plist-get info :ascii-quote-margin)))
- ;;; Filters
- (defun org-ascii-filter-headline-blank-lines (headline _backend info)
- "Filter controlling number of blank lines after a headline.
- HEADLINE is a string representing a transcoded headline. BACKEND
- is symbol specifying back-end used for export. INFO is plist
- containing the communication channel.
- This function only applies to `ascii' back-end. See
- `org-ascii-headline-spacing' for information."
- (let ((headline-spacing (plist-get info :ascii-headline-spacing)))
- (if (not headline-spacing) headline
- (let ((blanks (make-string (1+ (cdr headline-spacing)) ?\n)))
- (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline)))))
- (defun org-ascii-filter-paragraph-spacing (tree _backend info)
- "Filter controlling number of blank lines between paragraphs.
- TREE is the parse tree. BACKEND is the symbol specifying
- back-end used for export. INFO is a plist used as
- a communication channel.
- See `org-ascii-paragraph-spacing' for information."
- (let ((paragraph-spacing (plist-get info :ascii-paragraph-spacing)))
- (when (wholenump paragraph-spacing)
- (org-element-map tree 'paragraph
- (lambda (p)
- (when (eq (org-element-type (org-export-get-next-element p info))
- 'paragraph)
- (org-element-put-property p :post-blank paragraph-spacing))))))
- tree)
- (defun org-ascii-filter-comment-spacing (tree _backend info)
- "Filter removing blank lines between comments.
- TREE is the parse tree. BACKEND is the symbol specifying
- back-end used for export. INFO is a plist used as
- a communication channel."
- (org-element-map tree '(comment comment-block)
- (lambda (c)
- (when (memq (org-element-type (org-export-get-next-element c info))
- '(comment comment-block))
- (org-element-put-property c :post-blank 0))))
- tree)
- ;;; End-user functions
- ;;;###autoload
- (defun org-ascii-convert-region-to-ascii ()
- "Assume region has Org syntax, and convert it to plain ASCII."
- (interactive)
- (let ((org-ascii-charset 'ascii))
- (org-export-replace-region-by 'ascii)))
- ;;;###autoload
- (defun org-ascii-convert-region-to-utf8 ()
- "Assume region has Org syntax, and convert it to UTF-8."
- (interactive)
- (let ((org-ascii-charset 'utf-8))
- (org-export-replace-region-by 'ascii)))
- ;;;###autoload
- (defun org-ascii-export-as-ascii
- (&optional async subtreep visible-only body-only ext-plist)
- "Export current buffer to a text buffer.
- If narrowing is active in the current buffer, only export its
- narrowed part.
- If a region is active, export that region.
- A non-nil optional argument ASYNC means the process should happen
- asynchronously. The resulting buffer should be accessible
- through the `org-export-stack' interface.
- When optional argument SUBTREEP is non-nil, export the sub-tree
- at point, extracting information from the headline properties
- first.
- When optional argument VISIBLE-ONLY is non-nil, don't export
- contents of hidden elements.
- When optional argument BODY-ONLY is non-nil, strip title and
- table of contents from output.
- EXT-PLIST, when provided, is a property list with external
- parameters overriding Org default settings, but still inferior to
- file-local settings.
- Export is done in a buffer named \"*Org ASCII Export*\", which
- will be displayed when `org-export-show-temporary-export-buffer'
- is non-nil."
- (interactive)
- (org-export-to-buffer 'ascii "*Org ASCII Export*"
- async subtreep visible-only body-only ext-plist (lambda () (text-mode))))
- ;;;###autoload
- (defun org-ascii-export-to-ascii
- (&optional async subtreep visible-only body-only ext-plist)
- "Export current buffer to a text file.
- If narrowing is active in the current buffer, only export its
- narrowed part.
- If a region is active, export that region.
- A non-nil optional argument ASYNC means the process should happen
- asynchronously. The resulting file should be accessible through
- the `org-export-stack' interface.
- When optional argument SUBTREEP is non-nil, export the sub-tree
- at point, extracting information from the headline properties
- first.
- When optional argument VISIBLE-ONLY is non-nil, don't export
- contents of hidden elements.
- When optional argument BODY-ONLY is non-nil, strip title and
- table of contents from output.
- EXT-PLIST, when provided, is a property list with external
- parameters overriding Org default settings, but still inferior to
- file-local settings.
- Return output file's name."
- (interactive)
- (let ((file (org-export-output-file-name ".txt" subtreep)))
- (org-export-to-file 'ascii file
- async subtreep visible-only body-only ext-plist)))
- ;;;###autoload
- (defun org-ascii-publish-to-ascii (plist filename pub-dir)
- "Publish an Org file to ASCII.
- FILENAME is the filename of the Org file to be published. PLIST
- is the property list for the given project. PUB-DIR is the
- publishing directory.
- Return output file name."
- (org-publish-org-to
- 'ascii filename ".txt" `(:ascii-charset ascii ,@plist) pub-dir))
- ;;;###autoload
- (defun org-ascii-publish-to-latin1 (plist filename pub-dir)
- "Publish an Org file to Latin-1.
- FILENAME is the filename of the Org file to be published. PLIST
- is the property list for the given project. PUB-DIR is the
- publishing directory.
- Return output file name."
- (org-publish-org-to
- 'ascii filename ".txt" `(:ascii-charset latin1 ,@plist) pub-dir))
- ;;;###autoload
- (defun org-ascii-publish-to-utf8 (plist filename pub-dir)
- "Publish an org file to UTF-8.
- FILENAME is the filename of the Org file to be published. PLIST
- is the property list for the given project. PUB-DIR is the
- publishing directory.
- Return output file name."
- (org-publish-org-to
- 'ascii filename ".txt" `(:ascii-charset utf-8 ,@plist) pub-dir))
- (provide 'ox-ascii)
- ;; Local variables:
- ;; generated-autoload-file: "org-loaddefs.el"
- ;; coding: utf-8
- ;; End:
- ;;; ox-ascii.el ends here
|