diff options
Diffstat (limited to 'lisp/ox-ascii.el')
-rw-r--r-- | lisp/ox-ascii.el | 754 |
1 files changed, 460 insertions, 294 deletions
diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el index cd2a9af..5cc70bd 100644 --- a/lisp/ox-ascii.el +++ b/lisp/ox-ascii.el @@ -1,6 +1,6 @@ ;;; ox-ascii.el --- ASCII Back-End for Org Export Engine -;; Copyright (C) 2012-2014 Free Software Foundation, Inc. +;; Copyright (C) 2012-2015 Free Software Foundation, Inc. ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com> ;; Keywords: outlines, hypermedia, calendar, wp @@ -49,8 +49,6 @@ (center-block . org-ascii-center-block) (clock . org-ascii-clock) (code . org-ascii-code) - (comment . (lambda (&rest args) "")) - (comment-block . (lambda (&rest args) "")) (drawer . org-ascii-drawer) (dynamic-block . org-ascii-dynamic-block) (entity . org-ascii-entity) @@ -71,12 +69,13 @@ (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) - (quote-section . org-ascii-quote-section) (radio-target . org-ascii-radio-target) (section . org-ascii-section) (special-block . org-ascii-special-block) @@ -119,7 +118,30 @@ (:filter-parse-tree org-ascii-filter-paragraph-spacing org-ascii-filter-comment-spacing) (:filter-section . org-ascii-filter-headline-blank-lines)) - :options-alist '((:ascii-charset nil nil org-ascii-charset))) + :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))) @@ -162,6 +184,15 @@ This margin is applied on both sides of the text." :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 "25.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." @@ -185,7 +216,7 @@ original Org buffer at the same place." :package-version '(Org . "8.0") :type '(choice (const :tag "Replicate original spacing" nil) - (cons :tag "Set an uniform spacing" + (cons :tag "Set a uniform spacing" (integer :tag "Number of blank lines before contents") (integer :tag "Number of blank lines after contents")))) @@ -384,14 +415,18 @@ nil to ignore the inline task." ;; Internal functions fall into three categories. -;; The first one is about text formatting. The core function is -;; `org-ascii--current-text-width', which determines the current -;; text width allowed to a given element. In other words, it helps -;; keeping each line width within maximum text width defined in -;; `org-ascii-text-width'. Once this information is known, -;; `org-ascii--fill-string', `org-ascii--justify-string', -;; `org-ascii--box-string' and `org-ascii--indent-string' can -;; operate on a given output string. +;; 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' @@ -420,7 +455,8 @@ 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-string'. +string, see `org-ascii--justify-lines' and +`org-ascii--justify-block'. Return nil if S isn't a string." (when (stringp s) @@ -435,8 +471,8 @@ Return nil if S isn't a string." (fill-region (point-min) (point-max) justify)) (buffer-string))))) -(defun org-ascii--justify-string (s text-width how) - "Justify string S. +(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'." @@ -452,6 +488,48 @@ HOW determines the type of justification: it can be `left', (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 (org-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 (org-looking-at-p "[ \t]*$") + (org-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." @@ -474,24 +552,25 @@ INFO is a plist used as a communication channel." INFO is a plist used as a communication channel." (case (org-element-type element) ;; Elements with an absolute width: `headline' and `inlinetask'. - (inlinetask org-ascii-inlinetask-width) + (inlinetask (plist-get info :ascii-inlinetask-width)) (headline - (- org-ascii-text-width + (- (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) org-ascii-global-margin)))) + (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. (otherwise - (let* ((genealogy (cons element (org-export-get-genealogy element))) + (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 (loop for parent in genealogy thereis (eq (org-element-type parent) 'inlinetask)) - org-ascii-inlinetask-width + (plist-get info :ascii-inlinetask-width) ;; No inlinetask: Remove global margin from text width. - (- org-ascii-text-width - org-ascii-global-margin + (- (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. @@ -502,41 +581,66 @@ INFO is a plist used as a communication channel." ;; low level headlines, since they've got their ;; own indentation mechanism. (if low-level-rank (* low-level-rank 2) - org-ascii-inner-margin)))))))) + (plist-get info :ascii-inner-margin))))))))) (- total-width - ;; Each `quote-block', `quote-section' and `verse-block' above - ;; narrows text width by twice the standard margin size. + ;; Each `quote-block' and `verse-block' above narrows text + ;; width by twice the standard margin size. (+ (* (loop for parent in genealogy when (memq (org-element-type parent) - '(quote-block quote-section verse-block)) + '(quote-block verse-block)) count parent) - 2 org-ascii-quote-margin) + 2 (plist-get info :ascii-quote-margin)) + ;; Apply list margin once per "top-level" plain-list + ;; containing current line + (* (let ((count 0)) + (dolist (e genealogy count) + (and (eq (org-element-type e) 'plain-list) + (not (eq (org-element-type (org-export-get-parent e)) + 'item)) + (incf count)))) + (plist-get info :ascii-list-margin)) ;; Text width within a plain-list is restricted by ;; indentation of current item. If that's the case, ;; compute it with the help of `:structure' property from ;; parent item, if any. - (let ((parent-item + (let ((item (if (eq (org-element-type element) 'item) element (loop for parent in genealogy when (eq (org-element-type parent) 'item) return parent)))) - (if (not parent-item) 0 + (if (not item) 0 ;; Compute indentation offset of the current item, ;; that is the sum of the difference between its ;; indentation and the indentation of the top item in ;; the list and current item bullet's length. Also ;; remove checkbox length, and tag length (for ;; description lists) or bullet length. - (let ((struct (org-element-property :structure parent-item)) - (beg-item (org-element-property :begin parent-item))) + (let ((struct (org-element-property :structure item)) + (beg-item (org-element-property :begin item))) (+ (- (org-list-get-ind beg-item struct) (org-list-get-ind (org-list-get-top-point struct) struct)) - (string-width (or (org-ascii--checkbox parent-item info) + (string-width (or (org-ascii--checkbox item info) "")) (string-width - (or (org-list-get-tag beg-item struct) - (org-list-get-bullet beg-item struct))))))))))))) + (let ((tag (org-element-property :tag item))) + (if tag (org-export-data tag info) + (org-element-property :bullet item)))))))))))))) + +(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))) + (case (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) @@ -601,7 +705,7 @@ possible. It doesn't apply to `inlinetask' elements." (let ((under-char (nth (1- (org-export-get-relative-level element info)) (cdr (assq (plist-get info :ascii-charset) - org-ascii-underline))))) + (plist-get info :ascii-underline)))))) (and under-char (concat "\n" (make-string (/ (string-width first-part) @@ -640,7 +744,7 @@ caption keyword." (org-export-data caption info)) (org-ascii--current-text-width element info) info))))) -(defun org-ascii--build-toc (info &optional n keyword) +(defun org-ascii--build-toc (info &optional n keyword local) "Return a table of contents. INFO is a plist used as a communication channel. @@ -649,28 +753,34 @@ 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." - (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) - (- org-ascii-text-width org-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) "\n"))))) +which the table of contents generation has been initiated. + +When optional argument LOCAL is non-nil, build a table of +contents according to the current headline." + (concat + (unless local + (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 (and local keyword)) "\n")))) (defun org-ascii--list-listings (keyword info) "Return a list of listings. @@ -685,7 +795,8 @@ generation. INFO is a plist used as a communication channel." "\n\n" (let ((text-width (if keyword (org-ascii--current-text-width keyword info) - (- org-ascii-text-width org-ascii-global-margin))) + (- (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)) @@ -724,7 +835,8 @@ generation. INFO is a plist used as a communication channel." "\n\n" (let ((text-width (if keyword (org-ascii--current-text-width keyword info) - (- org-ascii-text-width org-ascii-global-margin))) + (- (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)) @@ -812,13 +924,22 @@ channel." (if (not dest) (org-ascii--translate "Unknown reference" info) (format (org-ascii--translate "See section %s" info) - (mapconcat 'number-to-string - (org-export-get-headline-number dest info) ".")))) + (if (org-export-numbered-headline-p dest info) + (mapconcat #'number-to-string + (org-export-get-headline-number dest info) ".") + (org-export-data (org-element-property :title 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. + ((let ((protocol (nth 2 (assoc type org-link-protocols))) + (path (org-element-property :path link))) + (and (functionp protocol) + (funcall protocol (org-link-unescape path) anchor 'ascii))) + nil) (t (concat (org-ascii--fill-string @@ -843,11 +964,15 @@ INFO is a plist used as a communication channel." (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 org-ascii-text-width) + (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. - (org-ascii-links-to-notes nil) - (title (org-export-data (plist-get info :title) info)) + (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))))) @@ -878,7 +1003,7 @@ INFO is a plist used as a communication channel." date "\n\n\n")) ((org-string-nw-p date) (concat - (org-ascii--justify-string date text-width 'right) + (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")) @@ -890,8 +1015,14 @@ INFO is a plist used as a communication channel." (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 (length title) (/ (* 2 text-width) 3))) + (title-len (min (apply #'max + (mapcar #'length + (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 @@ -899,17 +1030,16 @@ INFO is a plist used as a communication channel." (string-width (or email ""))) 2) text-width) (if utf8p ?━ ?_)))) - (org-ascii--justify-string + (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 (if utf8p "\n\n\n" "\n\n") author "\n" email)) - ((org-string-nw-p author) - (concat (if utf8p "\n\n\n" "\n\n") author)) - ((org-string-nw-p email) - (concat (if utf8p "\n\n\n" "\n\n") 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))))) @@ -919,81 +1049,82 @@ INFO is a plist used as a communication channel." CONTENTS is the transcoded contents string. INFO is a plist holding export options." (org-element-normalize-string - (org-ascii--indent-string - (concat - ;; 1. Document's body. - contents - ;; 2. Footnote definitions. - (let ((definitions (org-export-collect-footnote-definitions - (plist-get info :parse-tree) info)) - ;; Insert full links right inside the footnote definition - ;; as they have no chance to be inserted later. - (org-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 (- org-ascii-text-width org-ascii-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 (eq (org-element-type def) 'org-data) - ;; 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")))))) - org-ascii-global-margin))) + (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 (eq (org-element-type def) 'org-data) + ;; 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." - (concat - ;; 1. 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")))) - org-ascii-global-margin) - ;; 3. Document's body. - contents - ;; 4. Creator. Ignore `comment' value as there are no comments in - ;; ASCII. Justify it to the bottom right. - (org-ascii--indent-string - (let ((creator-info (plist-get info :with-creator)) - (text-width (- org-ascii-text-width org-ascii-global-margin))) - (unless (or (not creator-info) (eq creator-info 'comment)) - (concat - "\n\n\n" - (org-ascii--fill-string - (plist-get info :creator) text-width info 'right)))) - org-ascii-global-margin))) + (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. @@ -1020,8 +1151,9 @@ contextual information." "Transcode a CENTER-BLOCK element from Org to ASCII. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." - (org-ascii--justify-string - contents (org-ascii--current-text-width center-block info) 'center)) + ;; Center has already been taken care of at a lower level, so + ;; there's nothing left to do. + contents) ;;;; Clock @@ -1030,16 +1162,16 @@ holding contextual information." "Transcode a CLOCK object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." - (concat org-clock-string " " - (org-translate-time - (org-element-property :raw-value - (org-element-property :value clock))) - (let ((time (org-element-property :duration clock))) - (and time - (concat " => " - (apply 'format - "%2s:%02s" - (org-split-string time ":"))))))) + (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 @@ -1048,7 +1180,8 @@ information." "Return a CODE object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." - (format org-ascii-verbatim-format (org-element-property :value code))) + (format (plist-get info :ascii-verbatim-format) + (org-element-property :value code))) ;;;; Drawer @@ -1059,7 +1192,8 @@ 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 org-ascii-format-drawer-function name contents width))) + (funcall (plist-get info :ascii-format-drawer-function) + name contents width))) ;;;; Dynamic Block @@ -1087,8 +1221,10 @@ contextual information." (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--box-string - (org-export-format-code-default example-block info) info)) + (org-ascii--justify-element + (org-ascii--box-string + (org-export-format-code-default example-block info) info) + example-block info)) ;;;; Export Snippet @@ -1106,7 +1242,8 @@ CONTENTS is nil. INFO is a plist holding contextual information." "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-remove-indentation (org-element-property :value export-block)))) + (org-ascii--justify-element + (org-element-property :value export-block) export-block info))) ;;;; Fixed Width @@ -1114,9 +1251,11 @@ CONTENTS is nil. INFO is a plist holding contextual information." (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--box-string - (org-remove-indentation - (org-element-property :value fixed-width)) info)) + (org-ascii--justify-element + (org-ascii--box-string + (org-remove-indentation + (org-element-property :value fixed-width)) info) + fixed-width info)) ;;;; Footnote Definition @@ -1149,8 +1288,9 @@ holding contextual information." ;; original buffer's spacing. (pre-blanks (make-string - (if org-ascii-headline-spacing (car org-ascii-headline-spacing) - (org-element-property :pre-blank headline)) ?\n)) + (or (car (plist-get info :ascii-headline-spacing)) + (org-element-property :pre-blank headline)) + ?\n)) ;; Even if HEADLINE has no section, there might be some ;; links in its title that we shouldn't forget to describe. (links @@ -1164,7 +1304,7 @@ holding contextual information." (concat ;; Bullet. (let ((bullets (cdr (assq (plist-get info :ascii-charset) - org-ascii-bullets)))) + (plist-get info :ascii-bullets))))) (char-to-string (nth (mod (1- low-level-rank) (length bullets)) bullets))) " " @@ -1192,7 +1332,7 @@ 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-string + (org-ascii--justify-lines (make-string (if (and spec-width (string-match "^[0-9]+$" spec-width)) (string-to-number spec-width) text-width) @@ -1206,7 +1346,7 @@ information." "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 org-ascii-verbatim-format + (format (plist-get info :ascii-verbatim-format) (org-element-property :value inline-src-block))) @@ -1218,7 +1358,7 @@ contextual information." 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 org-ascii-inlinetask-width))) + (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. @@ -1236,9 +1376,9 @@ of the parameters." ;; Bottom line. (make-string width (if utf8p ?━ ?_))) ;; Flush the inlinetask to the right. - (- org-ascii-text-width org-ascii-global-margin + (- (plist-get info :ascii-text-width) (plist-get info :ascii-global-margin) (if (not (org-export-get-parent-headline inlinetask)) 0 - org-ascii-inner-margin) + (plist-get info :ascii-inner-margin)) (org-ascii--current-text-width inlinetask info))))) (defun org-ascii-inlinetask (inlinetask contents info) @@ -1246,7 +1386,7 @@ of the parameters." CONTENTS holds the contents of the block. INFO is a plist holding contextual information." (let ((width (org-ascii--current-text-width inlinetask info))) - (funcall org-ascii-format-inlinetask-function + (funcall (plist-get info :ascii-format-inlinetask-function) ;; todo. (and (plist-get info :with-todo-keywords) (let ((todo (org-element-property @@ -1334,20 +1474,21 @@ information." (let ((key (org-element-property :key keyword)) (value (org-element-property :value keyword))) (cond - ((string= key "ASCII") value) + ((string= key "ASCII") (org-ascii--justify-element value keyword info)) ((string= key "TOC") - (let ((value (downcase value))) - (cond - ((string-match "\\<headlines\\>" value) - (let ((depth (or (and (string-match "[0-9]+" value) - (string-to-number (match-string 0 value))) - (plist-get info :with-toc)))) - (org-ascii--build-toc - info (and (wholenump depth) depth) keyword))) - ((string= "tables" value) - (org-ascii--list-tables keyword info)) - ((string= "listings" value) - (org-ascii--list-listings keyword info)))))))) + (org-ascii--justify-element + (let ((case-fold-search t)) + (cond + ((org-string-match-p "\\<headlines\\>" value) + (let ((depth (and (string-match "\\<[0-9]+\\>" value) + (string-to-number (match-string 0 value)))) + (localp (org-string-match-p "\\<local\\>" value))) + (org-ascii--build-toc info depth keyword localp))) + ((org-string-match-p "\\<tables\\>" value) + (org-ascii--list-tables keyword info)) + ((org-string-match-p "\\<listings\\>" value) + (org-ascii--list-listings keyword info)))) + keyword info))))) ;;;; Latex Environment @@ -1357,7 +1498,9 @@ information." CONTENTS is nil. INFO is a plist holding contextual information." (when (plist-get info :with-latex) - (org-remove-indentation (org-element-property :value latex-environment)))) + (org-ascii--justify-element + (org-remove-indentation (org-element-property :value latex-environment)) + latex-environment info))) ;;;; Latex Fragment @@ -1385,9 +1528,9 @@ CONTENTS is nil. INFO is a plist holding contextual DESC is the description part of the link, or the empty string. INFO is a plist holding contextual information." - (let ((raw-link (org-element-property :raw-link link)) - (type (org-element-property :type link))) + (let ((type (org-element-property :type link))) (cond + ((org-export-custom-protocol-maybe link desc 'ascii)) ((string= type "coderef") (let ((ref (org-element-property :path link))) (format (org-export-get-coderef-format ref desc) @@ -1404,14 +1547,33 @@ INFO is a plist holding contextual information." (let ((number (org-export-get-ordinal destination info nil 'org-ascii--has-caption-p))) - (when number - (if (atom number) (number-to-string number) - (mapconcat 'number-to-string number ".")))))))) + (if number + (if (atom number) (number-to-string number) + (mapconcat #'number-to-string number ".")) + ;; Unnumbered headline. + (when (eq 'headline (org-element-type destination)) + (format "[%s]" + (org-export-data + (org-element-property :title destination) + info))))))))) (t - (if (not (org-string-nw-p desc)) (format "[%s]" raw-link) - (concat - (format "[%s]" desc) - (unless org-ascii-links-to-notes (format " (%s)" raw-link)))))))) + (let ((raw-link (org-element-property :raw-link link))) + (if (not (org-string-nw-p desc)) (format "[%s]" raw-link) + (concat (format "[%s]" desc) + (and (not (plist-get info :ascii-links-to-notes)) + (format " (%s)" raw-link))))))))) + + +;;;; 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 @@ -1420,16 +1582,17 @@ INFO is a plist holding contextual information." "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--fill-string - (if (not (wholenump org-ascii-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 org-ascii-indented-line-width ?\s)) - (replace-regexp-in-string "\\`[ \t]+" "" contents))) - (org-ascii--current-text-width paragraph info) info)) + (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 @@ -1438,7 +1601,11 @@ the plist used as a communication channel." "Transcode a PLAIN-LIST element from Org to ASCII. CONTENTS is the contents of the list. INFO is a plist holding contextual information." - contents) + (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 @@ -1466,25 +1633,34 @@ INFO is a plist used as a communication channel." "Transcode a PLANNING element from Org to ASCII. CONTENTS is nil. INFO is a plist used as a communication channel." - (mapconcat - 'identity - (delq nil - (list (let ((closed (org-element-property :closed planning))) - (when closed - (concat org-closed-string " " - (org-translate-time - (org-element-property :raw-value closed))))) - (let ((deadline (org-element-property :deadline planning))) - (when deadline - (concat org-deadline-string " " - (org-translate-time - (org-element-property :raw-value deadline))))) - (let ((scheduled (org-element-property :scheduled planning))) - (when scheduled - (concat org-scheduled-string " " - (org-translate-time - (org-element-property :raw-value scheduled))))))) - " ")) + (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 @@ -1493,26 +1669,7 @@ channel." "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 org-ascii-quote-margin)) - - -;;;; Quote Section - -(defun org-ascii-quote-section (quote-section contents info) - "Transcode a QUOTE-SECTION element from Org to ASCII. -CONTENTS is nil. INFO is a plist holding contextual information." - (let ((width (org-ascii--current-text-width quote-section info)) - (value - (org-export-data - (org-remove-indentation (org-element-property :value quote-section)) - info))) - (org-ascii--indent-string - value - (+ org-ascii-quote-margin - ;; Don't apply inner margin if parent headline is low level. - (let ((headline (org-export-get-parent-headline quote-section))) - (if (org-export-low-level-p headline info) 0 - org-ascii-inner-margin)))))) + (org-ascii--indent-string contents (plist-get info :ascii-quote-margin))) ;;;; Radio Target @@ -1533,7 +1690,7 @@ contextual information." (org-ascii--indent-string (concat contents - (when org-ascii-links-to-notes + (when (plist-get info :ascii-links-to-notes) ;; Add list of links at the end of SECTION. (let ((links (org-ascii--describe-links (org-ascii--unique-links section info) @@ -1543,7 +1700,7 @@ contextual information." ;; 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 - org-ascii-inner-margin)))) + (plist-get info :ascii-inner-margin))))) ;;;; Special Block @@ -1552,6 +1709,9 @@ contextual information." "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 "JUSTFYRIGHT" have already been taken care of + ;; at a lower level. There is no other special block type to + ;; handle. contents) @@ -1562,13 +1722,15 @@ holding contextual information." 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 "") "" - (concat - (when (and caption org-ascii-caption-above) (concat caption "\n")) - (org-ascii--box-string code info) - (when (and caption (not org-ascii-caption-above)) - (concat "\n" caption)))))) + (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 @@ -1616,26 +1778,29 @@ holding contextual information." "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))) - (concat - ;; Possibly add a caption string above. - (when (and caption org-ascii-caption-above) (concat caption "\n")) - ;; Insert table. Note: "table.el" tables are left unmodified. - (cond ((eq (org-element-property :type table) 'org) contents) - ((and org-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 org-ascii-caption-above) caption)))) + (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 @@ -1661,12 +1826,13 @@ are ignored." (plist-put info :ascii-table-cell-width-cache (make-hash-table :test 'equal))) :ascii-table-cell-width-cache))) - (key (cons table col))) + (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 org-ascii-table-widen-columns) cookie-width) + (or (and (not widenp) cookie-width) (let ((contents-width (let ((max-width 0)) (org-element-map table 'table-row @@ -1681,8 +1847,7 @@ are ignored." info) max-width))) (cond ((not cookie-width) contents-width) - (org-ascii-table-widen-columns - (max cookie-width contents-width)) + (widenp (max cookie-width contents-width)) (t cookie-width))))) cache)))) @@ -1696,14 +1861,14 @@ a communication channel." ;; each cell in the column. (let ((width (org-ascii--table-cell-width table-cell info))) ;; When contents are too large, truncate them. - (unless (or org-ascii-table-widen-columns + (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-string + (org-ascii--justify-lines contents width (org-export-table-cell-alignment table-cell info))))) (setq contents @@ -1790,7 +1955,7 @@ holding contextual information." (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 org-ascii-verbatim-format + (format (plist-get info :ascii-verbatim-format) (org-element-property :value verbatim))) @@ -1802,8 +1967,8 @@ CONTENTS is verse block contents. INFO is a plist holding contextual information." (let ((verse-width (org-ascii--current-text-width verse-block info))) (org-ascii--indent-string - (org-ascii--justify-string contents verse-width 'left) - org-ascii-quote-margin))) + (org-ascii--justify-element contents verse-block info) + (plist-get info :ascii-quote-margin)))) @@ -1818,9 +1983,10 @@ plist containing the communication channel. This function only applies to `ascii' back-end. See `org-ascii-headline-spacing' for information." - (if (not org-ascii-headline-spacing) headline - (let ((blanks (make-string (1+ (cdr org-ascii-headline-spacing)) ?\n))) - (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline)))) + (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 back-end info) "Filter controlling number of blank lines between paragraphs. @@ -1830,13 +1996,13 @@ back-end used for export. INFO is a plist used as a communication channel. See `org-ascii-paragraph-spacing' for information." - (when (wholenump org-ascii-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 org-ascii-paragraph-spacing))))) + (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) @@ -1965,7 +2131,7 @@ Return output file name." ;; Local variables: ;; generated-autoload-file: "org-loaddefs.el" -;; coding: utf-8-emacs +;; coding: utf-8 ;; End: ;;; ox-ascii.el ends here |