ox-ascii: Implement "justifyright" and "justifyleft" blocks

* lisp/ox-ascii.el
(org-ascii--justify-lines): New function.  Renamed from
`org-ascii--justify-string'.
(org-ascii--justify-element, org-ascii--current-justification): New
functions.
(org-ascii-verse-block, org-ascii-table-cell, org-ascii-table
  org-ascii-src-block, org-ascii-property-drawer, org-ascii-planning,
  org-ascii-paragraph, org-ascii-paragraph,
  org-ascii-latex-environment, org-ascii-keyword,
  org-ascii-horizontal-rule, org-ascii-fixed-width,
  org-ascii-export-block, org-ascii-example-block, org-ascii-clock,
  org-ascii-template--document-title,
  org-ascii-template--document-title): Use new functions.
(org-ascii-center-block): Do not process contents anymore since they
are already justified.

* doc/org.texi (ASCII/Latin-1/UTF-8 export): Document new feature.
  Also document `org-ascii-text-width' since this is closely related.

This patches also fixes centering of tables.
This commit is contained in:
Nicolas Goaziou 2014-05-21 23:37:00 +02:00
parent 9a6234ff81
commit a7b7c3de2b
2 changed files with 204 additions and 96 deletions

View File

@ -10750,6 +10750,10 @@ ASCII export produces a simple and very readable version of an Org mode
file, containing only plain ASCII@. Latin-1 and UTF-8 export augment the file
with special characters and symbols available in these encodings.
@vindex org-ascii-text-width
Upon exporting, text is filled and justified, when appropriate, according the
text width set in @code{org-ascii-text-width}.
@vindex org-ascii-links-to-notes
Links are exported in a footnote-like style, with the descriptive part in the
text and the link in a note before the next heading. See the variable
@ -10804,6 +10808,25 @@ specified using an @code{ATTR_ASCII} line, directly preceding the rule.
-----
@end example
@subheading ASCII special blocks
@cindex special blocks, in ASCII export
@cindex #+BEGIN_JUSTIFYLEFT
@cindex #+BEGIN_JUSTIFYRIGHT
In addition to @code{#+BEGIN_CENTER} blocks (@pxref{Paragraphs}), it is
possible to justify contents to the left or the right of the page with the
following dedicated blocks.
@example
#+BEGIN_JUSTIFYLEFT
It's just a jump to the left...
#+END_JUSTIFYLEFT
#+BEGIN_JUSTIFYRIGHT
...and then a step to the right.
#+END_JUSTIFYRIGHT
@end example
@node Beamer export
@section Beamer export
@cindex Beamer export

View File

@ -385,14 +385,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'
@ -421,7 +425,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."
;; Don't fill paragraph when break should be preserved.
@ -436,8 +441,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'."
@ -453,6 +458,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)))
(if (eq how 'left) contents
;; Paragraphs are treated specially as they also need to be
;; filled.
(if (eq (org-element-type element) 'paragraph)
(org-ascii--fill-string contents text-width info how)
(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."
@ -539,6 +586,21 @@ INFO is a plist used as a communication channel."
(or (org-list-get-tag beg-item struct)
(org-list-get-bullet beg-item struct)))))))))))))
(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)
"Format ELEMENT title and return it.
@ -879,7 +941,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"))
@ -900,7 +962,7 @@ 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)
@ -1021,8 +1083,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
@ -1031,16 +1094,18 @@ 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-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 ":"))))))
clock info))
;;;; Code
@ -1088,8 +1153,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
@ -1107,7 +1174,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
@ -1115,9 +1183,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
@ -1193,7 +1263,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)
@ -1332,23 +1402,26 @@ contextual information."
"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)))
(let ((key (org-element-property :key keyword)))
(cond
((string= key "ASCII") value)
((string= key "ASCII")
(org-ascii--justify-element
(org-element-property :value keyword) 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 ((value (downcase (org-element-property :value keyword))))
(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))))
keyword info)))))
;;;; Latex Environment
@ -1358,7 +1431,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
@ -1433,7 +1508,7 @@ 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
(org-ascii--justify-element
(if (not (wholenump org-ascii-indented-line-width)) contents
(concat
;; Do not indent first paragraph in a section.
@ -1442,7 +1517,7 @@ the plist used as a communication channel."
'section))
(make-string org-ascii-indented-line-width ?\s))
(replace-regexp-in-string "\\`[ \t]+" "" contents)))
(org-ascii--current-text-width paragraph info) info))
paragraph info))
;;;; Plain List
@ -1479,25 +1554,27 @@ 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-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)))))))
" ")
planning info))
;;;; Property Drawer
@ -1506,7 +1583,8 @@ channel."
"Transcode a PROPERTY-DRAWER element from Org to ASCII.
CONTENTS holds the contents of the drawer. INFO is a plist
holding contextual information."
(org-string-nw-p contents))
(and (org-string-nw-p contents)
(org-ascii--justify-element contents property-drawer info)))
;;;; Quote Block
@ -1555,6 +1633,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)
@ -1567,11 +1648,13 @@ contextual information."
(let ((caption (org-ascii--build-caption src-block info))
(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
(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)))
src-block info))))
;;;; Statistics Cookie
@ -1620,25 +1703,27 @@ holding contextual information."
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))))
(org-ascii--justify-element
(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))
table info)))
;;;; Table Cell
@ -1701,7 +1786,7 @@ a communication channel."
(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
@ -1800,7 +1885,7 @@ 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--justify-element contents verse-block info)
org-ascii-quote-margin)))