;;; ox-ascii.el --- ASCII Back-End for Org Export Engine
-;; Copyright (C) 2012-2013 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
: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"))))
(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 number. If it is the symbol `auto' preserve
-indentation from original document."
+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")
:package-version '(Org . "8.0")
:type 'string)
-(defcustom org-ascii-format-drawer-function nil
+(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:
The function should return either the string to be exported or
nil to ignore the drawer.
-For example, the variable could be set to the following function
-in order to mimic default behaviour:
-
-\(defun org-ascii-format-drawer-default (name contents width)
- \"Format a drawer element for ASCII export.\"
- contents)"
+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 nil
+(defcustom org-ascii-format-inlinetask-function
+ 'org-ascii-format-inlinetask-default
"Function called to format an inlinetask in ASCII.
-The function must accept six 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.
+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.
-
-For example, the variable could be set to the following function
-in order to mimic default behaviour:
-
-\(defun org-ascii-format-inlinetask-default
- \(todo type priority name tags contents\)
- \"Format an inline task element for ASCII export.\"
- \(let* \(\(utf8p \(eq \(plist-get info :ascii-charset\) 'utf-8\)\)
- \(width org-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 \(<= \(length 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-width\)
- \(plist-get info :ascii-margin\)
- \(plist-get info :ascii-inner-margin\)
- \(org-ascii--current-text-width inlinetask info\)\)"
+nil to ignore the inline task."
:group 'org-export-ascii
:version "24.4"
- :package-version '(Org . "8.0")
+ :package-version '(Org . "8.3")
:type 'function)
string, see `org-ascii--justify-string'.
Return nil if S isn't a string."
- ;; Don't fill paragraph when break should be preserved.
- (cond ((not (stringp s)) nil)
- ((plist-get info :preserve-breaks) s)
- (t (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 s)
- (fill-region (point-min) (point-max) justify))
- (buffer-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-string (s text-width how)
"Justify string S.
Empty lines are not indented."
(when (stringp s)
(replace-regexp-in-string
- "\\(^\\)\\(?:.*\\S-\\)" (make-string width ? ) s nil nil 1)))
+ "\\(^\\)[ \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 "â\95â\94\80â\94\80â\94\80â\94\80\n%s\nâ\95°────" ",----\n%s\n`----")
+ (format (if utf8p "â\94\8câ\94\80â\94\80â\94\80â\94\80\n%s\nâ\94\94────" ",----\n%s\n`----")
(replace-regexp-in-string
"^" (if utf8p "│ " "| ")
;; Remove last newline character.
(case (org-element-type element)
;; Elements with an absolute width: `headline' and `inlinetask'.
(inlinetask org-ascii-inlinetask-width)
- ('headline
+ (headline
(- org-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))))
(+ (- (org-list-get-ind beg-item struct)
(org-list-get-ind
(org-list-get-top-point struct) struct))
- (length (org-ascii--checkbox parent-item info))
- (length
+ (string-width (or (org-ascii--checkbox parent-item info)
+ ""))
+ (string-width
(or (org-list-get-tag beg-item struct)
(org-list-get-bullet beg-item struct)))))))))))))
(when tags
(format
(format " %%%ds"
- (max (- text-width (1+ (length first-part))) (length tags)))
+ (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.
org-ascii-underline)))))
(and under-char
(concat "\n"
- (make-string (length first-part) under-char))))))))
+ (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.
(let ((title (org-ascii--translate "Table of Contents" info)))
(concat
title "\n"
- (make-string (length title)
+ (make-string (string-width title)
(if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
"\n\n"
(let ((text-width
(let ((title (org-ascii--translate "List of Listings" info)))
(concat
title "\n"
- (make-string (length title)
+ (make-string (string-width title)
(if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
"\n\n"
(let ((text-width
;; 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)
- (incf count))))
+ (let* ((initial-text
+ (format (org-ascii--translate "Listing %d:" info)
+ (incf count)))
+ (initial-width (string-width initial-text)))
(concat
initial-text " "
(org-trim
(let ((caption (or (org-export-get-caption src-block t)
(org-export-get-caption src-block))))
(org-export-data caption info))
- (- text-width (length initial-text)) info)
- (length initial-text))))))
+ (- text-width initial-width) info)
+ initial-width)))))
(org-export-collect-listings info) "\n")))))
(defun org-ascii--list-tables (keyword info)
(let ((title (org-ascii--translate "List of Tables" info)))
(concat
title "\n"
- (make-string (length title)
+ (make-string (string-width title)
(if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
"\n\n"
(let ((text-width
;; 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)
- (incf count))))
+ (let* ((initial-text
+ (format (org-ascii--translate "Table %d:" info)
+ (incf count)))
+ (initial-width (string-width initial-text)))
(concat
initial-text " "
(org-trim
(let ((caption (or (org-export-get-caption table t)
(org-export-get-caption table))))
(org-export-data caption info))
- (- text-width (length initial-text)) info)
- (length initial-text))))))
+ (- 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
;; Update SEEN links along the way.
(lambda (link)
(let ((footprint
+ ;; Normalize description in footprints.
(cons (org-element-property :raw-link link)
- (org-element-contents 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.
((and (org-string-nw-p date) (org-string-nw-p author))
(concat
author
- (make-string (- text-width (length date) (length 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 (length date) (length email)) ? )
+ (make-string (- text-width (string-width date) (string-width email))
+ ?\s)
date "\n\n\n"))
((org-string-nw-p date)
(concat
(formatted-title (org-ascii--fill-string title title-len info))
(line
(make-string
- (min (+ (max title-len (length author) (length email)) 2)
+ (min (+ (max title-len
+ (string-width (or author ""))
+ (string-width (or email "")))
+ 2)
text-width) (if utf8p ?━ ?_))))
(org-ascii--justify-string
(concat line "\n"
(concat
title "\n"
(make-string
- (length title)
+ (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)))
holding contextual information."
(let ((name (org-element-property :drawer-name drawer))
(width (org-ascii--current-text-width drawer info)))
- (if (functionp org-ascii-format-drawer-function)
- (funcall org-ascii-format-drawer-function name contents width)
- ;; If there's no user defined function: simply
- ;; display contents of the drawer.
- contents)))
+ (funcall org-ascii-format-drawer-function name contents width)))
;;;; Dynamic 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 org-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.
+ (- org-ascii-text-width org-ascii-global-margin
+ (if (not (org-export-get-parent-headline inlinetask)) 0
+ org-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)))
- ;; If `org-ascii-format-inlinetask-function' is provided, call it
- ;; with appropriate arguments.
- (if (functionp org-ascii-format-inlinetask-function)
- (funcall org-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)
- ;; Otherwise, use a default template.
- (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
- (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 (<= (length 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.
- (- org-ascii-text-width org-ascii-global-margin
- (if (not (org-export-get-parent-headline inlinetask)) 0
- org-ascii-inner-margin)
- (org-ascii--current-text-width inlinetask info)))))))
+ (funcall org-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
;; 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 (length bullet))))
+ (let ((contents (org-ascii--indent-string contents (string-width bullet))))
(if (eq (org-element-type (car (org-element-contents item))) 'paragraph)
(org-trim contents)
(concat "\n" contents))))))
(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")
- (let ((destination (org-export-resolve-radio-link link info)))
- (when destination
- (org-export-data (org-element-contents destination) info))))
+ ((string= type "radio") desc)
;; Do not apply a special syntax on fuzzy links pointing to
;; targets.
((string= type "fuzzy")
"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."
- (let ((contents (if (not (wholenump org-ascii-indented-line-width)) contents
- (concat
- (make-string org-ascii-indented-line-width ? )
- (replace-regexp-in-string "\\`[ \t]+" "" contents)))))
- (org-ascii--fill-string
- contents (org-ascii--current-text-width paragraph info) info)))
+ (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))
;;;; Plain List
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)))
+ (format "^{%s}" contents)
+ (format "^%s" contents)))
;;;; Strike-through
(or (gethash key cache)
(puthash
key
- (or (and (not org-ascii-table-widen-columns)
- (org-export-table-cell-width table-cell info))
- (let* ((max-width 0))
- (org-element-map table 'table-row
- (lambda (row)
- (setq max-width
- (max (length
- (org-export-data
- (org-element-contents
- (elt (org-element-contents row) col))
- info))
- max-width)))
- info)
- max-width))
+ (let ((cookie-width (org-export-table-cell-width table-cell info)))
+ (or (and (not org-ascii-table-widen-columns) 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)
+ (org-ascii-table-widen-columns
+ (max cookie-width contents-width))
+ (t cookie-width)))))
cache))))
(defun org-ascii-table-cell (table-cell contents info)
;; 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 (<= (length contents) width))
+ (unless (or org-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)
(org-ascii--justify-string
contents width
(org-export-table-cell-alignment table-cell info)))))
- (setq contents (concat data (make-string (- width (length data)) ? ))))
+ (setq contents
+ (concat data
+ (make-string (- width (string-width (or data ""))) ?\s))))
;; Return cell.
(concat (format " %s " contents)
(when (memq 'right (org-export-table-cell-borders table-cell info))