org-table: Use "ox.el" internally for radio tables

* lisp/org-table.el (org-table-clean-before-export, orgtbl-get-fmt,
  orgtbl-apply-fmt, orgtbl-eval-str, orgtbl-format-line,
  orgtbl-format-section): Remove functions.
(org-table-clean-did-remove-column, *orgtbl-table*, *orgtbl-rtn*,
  *orgtbl-hline*, *orgtbl-sep*, *orgtbl-default-fmt*, *orgtbl-fmt*,
  *orgtbl-efmt*, *orgtbl-lfmt*, *orgtbl-llfmt*, *orgtbl-lstart*,
  *orgtbl-llstart*, *orgtbl-lend*, *orgtbl-llend*): Remove variables.

(org-table-export, orgtbl-send-table): Apply function removal.  Do not
set `org-table-last-alignment' and `org-table-last-column-widths'
anymore.
(org-table-to-lisp, orgtbl-send-replace-tbl): Small refactoring.

(org-table--to-generic-table, org-table--to-generic-row,
org-table--to-generic-cell): New functions.
(orgtbl-to-generic): Rewrite function.  Handle :skip and :skipcols
parameters.
(orgtbl-to-latex, orgtbl-to-html, orgtbl-to-texinfo, orgtbl-to-orgtbl,
orgtbl-to-unicode): Use new `orgtbl-to-generic' features.

* testing/lisp/test-org-table.el (test-org-table/to-generic,
  test-org-table/to-latex, test-org-table/to-texinfo,
  test-org-table/to-html, test-org-table/to-unicode,
  test-org-table/send-region): New tests.

* doc/org.texi (Radio tables, A @LaTeX{} example, Translator
  functions): Update documentation.

* etc/ORG-NEWS (argument): Document new features.
This commit is contained in:
Nicolas Goaziou 2014-08-24 01:31:56 +02:00
parent 66b1263d9a
commit 9209aa3c9d
4 changed files with 908 additions and 535 deletions

View File

@ -17508,10 +17508,6 @@ calculation marks, that column is automatically discarded as well.
Please note that the translator function sees the table @emph{after} the
removal of these columns, the function never knows that there have been
additional columns.
@item :no-escape t
When non-@code{nil}, do not escape special characters @code{&%#_^} when exporting
the table. The default value is @code{nil}.
@end table
@noindent
@ -17619,14 +17615,15 @@ Month & \multicolumn@{1@}@{c@}@{Days@} & Nr.\ sold & per day\\
@end example
The @LaTeX{} translator function @code{orgtbl-to-latex} is already part of
Orgtbl mode. It uses a @code{tabular} environment to typeset the table
and marks horizontal lines with @code{\hline}. Furthermore, it
interprets the following parameters (see also @pxref{Translator functions}):
Orgtbl mode. By default, it uses a @code{tabular} environment to typeset the
table and marks horizontal lines with @code{\hline}. You can control the
output through several parameters (see also @pxref{Translator functions}),
including the following ones :
@table @code
@item :splice nil/t
When set to t, return only table body lines, don't wrap them into a
tabular environment. Default is @code{nil}.
When non-nil, return only table body lines, don't wrap them into a tabular
environment. Default is @code{nil}.
@item :fmt fmt
A format to be used to wrap each field, it should contain @code{%s} for the
@ -17637,14 +17634,14 @@ A function of one argument can be used in place of the strings; the
function must return a formatted string.
@item :efmt efmt
Use this format to print numbers with exponentials. The format should
have @code{%s} twice for inserting mantissa and exponent, for example
@code{"%s\\times10^@{%s@}"}. The default is @code{"%s\\,(%s)"}. This
may also be a property list with column numbers and formats, for example
@code{:efmt (2 "$%s\\times10^@{%s@}$" 4 "$%s\\cdot10^@{%s@}$")}. After
@code{efmt} has been applied to a value, @code{fmt} will also be
applied. Similar to @code{fmt}, functions of two arguments can be
supplied instead of strings.
Use this format to print numbers with exponentials. The format should have
@code{%s} twice for inserting mantissa and exponent, for example
@code{"%s\\times10^@{%s@}"}. This may also be a property list with column
numbers and formats, for example @code{:efmt (2 "$%s\\times10^@{%s@}$"
4 "$%s\\cdot10^@{%s@}$")}. After @code{efmt} has been applied to a value,
@code{fmt} will also be applied. Similar to @code{fmt}, functions of two
arguments can be supplied instead of strings. By default, no special
formatting is applied.
@end table
@node Translator functions
@ -17654,54 +17651,36 @@ supplied instead of strings.
Orgtbl mode has several translator functions built-in: @code{orgtbl-to-csv}
(comma-separated values), @code{orgtbl-to-tsv} (TAB-separated values)
@code{orgtbl-to-latex}, @code{orgtbl-to-html}, and @code{orgtbl-to-texinfo}.
Except for @code{orgtbl-to-html}@footnote{The HTML translator uses the same
code that produces tables during HTML export.}, these all use a generic
translator, @code{orgtbl-to-generic}. For example, @code{orgtbl-to-latex}
itself is a very short function that computes the column definitions for the
@code{tabular} environment, defines a few field and line separators and then
hands processing over to the generic translator. Here is the entire code:
@code{orgtbl-to-latex}, @code{orgtbl-to-html}, @code{orgtbl-to-texinfo},
@code{orgtbl-to-unicode} and @code{orgtbl-to-orgtbl}. These all use
a generic translator, @code{orgtbl-to-generic}, which, in turn, can delegate
translations to various export back-ends (@pxref{Export back-ends}).
@lisp
@group
(defun orgtbl-to-latex (table params)
"Convert the Orgtbl mode TABLE to LaTeX."
(let* ((alignment (mapconcat (lambda (x) (if x "r" "l"))
org-table-last-alignment ""))
(params2
(list
:tstart (concat "\\begin@{tabular@}@{" alignment "@}")
:tend "\\end@{tabular@}"
:lstart "" :lend " \\\\" :sep " & "
:efmt "%s\\,(%s)" :hline "\\hline")))
(orgtbl-to-generic table (org-combine-plists params2 params))))
@end group
@end lisp
As you can see, the properties passed into the function (variable
@var{PARAMS}) are combined with the ones newly defined in the function
(variable @var{PARAMS2}). The ones passed into the function (i.e., the
ones set by the @samp{ORGTBL SEND} line) take precedence. So if you
would like to use the @LaTeX{} translator, but wanted the line endings to
be @samp{\\[2mm]} instead of the default @samp{\\}, you could just
overrule the default with
In particular, properties passed into the function (i.e., the ones set by the
@samp{ORGTBL SEND} line) take precedence over translations defined in the
function. So if you would like to use the @LaTeX{} translator, but wanted
the line endings to be @samp{\\[2mm]} instead of the default @samp{\\}, you
could just overrule the default with
@example
#+ORGTBL: SEND test orgtbl-to-latex :lend " \\\\[2mm]"
@end example
For a new language, you can either write your own converter function in
analogy with the @LaTeX{} translator, or you can use the generic function
directly. For example, if you have a language where a table is started
with @samp{!BTBL!}, ended with @samp{!ETBL!}, and where table lines are
started with @samp{!BL!}, ended with @samp{!EL!}, and where the field
separator is a TAB, you could call the generic translator like this (on
a single line!):
For a new language, you can use the generic function to write your own
converter function. For example, if you have a language where a table is
started with @samp{!BTBL!}, ended with @samp{!ETBL!}, and where table lines
are started with @samp{!BL!}, ended with @samp{!EL!}, and where the field
separator is a TAB, you could define your generic translator like this:
@example
#+ORGTBL: SEND test orgtbl-to-generic :tstart "!BTBL!" :tend "!ETBL!"
:lstart "!BL! " :lend " !EL!" :sep "\t"
@end example
@lisp
(defun orgtbl-to-language (table params)
"Convert the orgtbl-mode TABLE to language."
(orgtbl-to-generic
table
(org-combine-plists
'(:tstart "!BTBL!" :tend "!ETBL!" :lstart "!BL!" :lend "!EL!" :sep "\t")
params)))
@end lisp
@noindent
Please check the documentation string of the function

View File

@ -77,6 +77,13 @@ These functions now support any element or object, not only headlines.
*** New filter: ~org-export-filter-body-functions~
Functions in this filter are applied on the body of the exported
document, befor wrapping it within the template.
*** Improve radio tables
Radio tables feature now relies on Org's export framework ("ox.el").
~:no-escape~ parameter no longer exists, but additional global
parameters are now supported: ~:raw~, ~:backend~. Moreover, there are
new parameters specific to some pre-defined translators, e.g.,
~:environment~ and ~:booktabs~ for ~orgtbl-to-latex~. See translators
docstrings (including ~orgtbl-to-generic~) for details.
** Miscellaneous
*** File names in links accept are now compatible with URI syntax
Absolute file names can now start with =///= in addition to =/=. E.g.,

File diff suppressed because it is too large Load Diff

View File

@ -1168,6 +1168,352 @@ See also `test-org-table/copy-field'."
(should (string= got
expect)))))
;;; Radio Tables
(ert-deftest test-org-table/to-generic ()
"Test `orgtbl-to-generic' specifications."
;; Test :hline parameter.
(should
(equal "a\nb"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:hline nil))))
(should
(equal "a\n~\nb"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:hline "~"))))
;; Test :sep parameter.
(should
(equal "a!b\nc!d"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:sep "!"))))
;; Test :hsep parameter.
(should
(equal "a!b\nc?d"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:sep "?" :hsep "!"))))
;; Test :tstart parameter.
(should
(equal "<begin>\na"
(orgtbl-to-generic (org-table-to-lisp "| a |") '(:tstart "<begin>"))))
(should
(equal "<begin>\na"
(orgtbl-to-generic (org-table-to-lisp "| a |")
'(:tstart (lambda () "<begin>")))))
(should
(equal "a"
(orgtbl-to-generic (org-table-to-lisp "| a |")
'(:tstart "<begin>" :splice t))))
;; Test :tend parameter.
(should
(equal "a\n<end>"
(orgtbl-to-generic (org-table-to-lisp "| a |") '(:tend "<end>"))))
(should
(equal "a\n<end>"
(orgtbl-to-generic (org-table-to-lisp "| a |")
'(:tend (lambda () "<end>")))))
(should
(equal "a"
(orgtbl-to-generic (org-table-to-lisp "| a |")
'(:tend "<end>" :splice t))))
;; Test :lstart parameter.
(should
(equal "> a"
(orgtbl-to-generic
(org-table-to-lisp "| a |") '(:lstart "> "))))
(should
(equal "> a"
(orgtbl-to-generic (org-table-to-lisp "| a |")
'(:lstart (lambda () "> ")))))
;; Test :llstart parameter.
(should
(equal "> a\n>> b"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:lstart "> " :llstart ">> "))))
;; Test :hlstart parameter.
(should
(equal "!> a\n> b"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:lstart "> " :hlstart "!> "))))
;; Test :hllstart parameter.
(should
(equal "!> a\n!!> b\n> c"
(orgtbl-to-generic (org-table-to-lisp "| a |\n| b |\n|---|\n| c |")
'(:lstart "> " :hlstart "!> " :hllstart "!!> "))))
;; Test :lend parameter.
(should
(equal "a <"
(orgtbl-to-generic (org-table-to-lisp "| a |") '(:lend " <"))))
;; Test :llend parameter.
(should
(equal "a <\nb <<"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:lend " <" :llend " <<"))))
;; Test :hlend parameter.
(should
(equal "a <!\nb <"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:lend " <" :hlend " <!"))))
;; Test :hllend parameter.
(should
(equal "a <!\nb <!!\nc <"
(orgtbl-to-generic (org-table-to-lisp "| a |\n| b |\n|---|\n| c |")
'(:lend " <" :hlend " <!" :hllend " <!!"))))
;; Test :lfmt parameter.
(should
(equal "a!b"
(orgtbl-to-generic (org-table-to-lisp "| a | b |")
'(:lfmt "%s!%s"))))
(should
(equal "a+b"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |")
'(:lfmt (lambda (c) (concat (car c) "+" (cadr c)))))))
(should
(equal "a!b"
(orgtbl-to-generic (org-table-to-lisp "| a | b |")
'(:lfmt "%s!%s" :lstart ">" :lend "<" :sep " "))))
;; Test :llfmt parameter.
(should
(equal "a!b"
(orgtbl-to-generic (org-table-to-lisp "| a | b |")
'(:llfmt "%s!%s"))))
(should
(equal "a!b\nc+d"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n| c | d |")
'(:lfmt "%s!%s" :llfmt (lambda (c) (concat (car c) "+" (cadr c)))))))
(should
(equal "a!b"
(orgtbl-to-generic (org-table-to-lisp "| a | b |")
'(:llfmt "%s!%s" :lstart ">" :lend "<" :sep " "))))
;; Test :hlfmt parameter.
(should
(equal "a!b\ncd"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:hlfmt "%s!%s"))))
(should
(equal "a+b\ncd"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:hlfmt (lambda (c) (concat (car c) "+" (cadr c)))))))
(should
(equal "a!b\n>c d<"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:hlfmt "%s!%s" :lstart ">" :lend "<" :sep " "))))
;; Test :hllfmt parameter.
(should
(equal "a!b\ncd"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:hllfmt "%s!%s"))))
(should
(equal "a+b\ncd"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:hllfmt (lambda (c) (concat (car c) "+" (cadr c)))))))
(should
(equal "a!b\n>c d<"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:hllfmt "%s!%s" :lstart ">" :lend "<" :sep " "))))
;; Test :fmt parameter.
(should
(equal ">a<\n>b<"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:fmt ">%s<"))))
(should
(equal ">a<b"
(orgtbl-to-generic (org-table-to-lisp "| a | b |")
'(:fmt (1 ">%s<" 2 (lambda (c) c))))))
(should
(equal "a b"
(orgtbl-to-generic (org-table-to-lisp "| a | b |")
'(:fmt (2 " %s")))))
(should
(equal ">a<"
(orgtbl-to-generic (org-table-to-lisp "| a |")
'(:fmt (lambda (c) (format ">%s<" c))))))
;; Test :hfmt parameter.
(should
(equal ">a<\nb"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:hfmt ">%s<"))))
(should
(equal ">a<b\ncd"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:hfmt (1 ">%s<" 2 identity)))))
(should
(equal "a b\ncd"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
'(:hfmt (2 " %s")))))
(should
(equal ">a<\nb"
(orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
'(:hfmt (lambda (c) (format ">%s<" c))))))
;; Test :efmt parameter.
(should
(equal "2x10^3"
(orgtbl-to-generic (org-table-to-lisp "| 2e3 |")
'(:efmt "%sx10^%s"))))
(should
(equal "2x10^3"
(orgtbl-to-generic (org-table-to-lisp "| 2e3 |")
'(:efmt (lambda (m e) (concat m "x10^" e))))))
(should
(equal "2x10^3"
(orgtbl-to-generic (org-table-to-lisp "| 2e3 |")
'(:efmt (1 "%sx10^%s")))))
(should
(equal "2x10^3"
(orgtbl-to-generic
(org-table-to-lisp "| 2e3 |")
'(:efmt (1 (lambda (m e) (format "%sx10^%s" m e)))))))
(should
(equal "2e3"
(orgtbl-to-generic (org-table-to-lisp "| 2e3 |") '(:efmt nil))))
;; Test :skip parameter.
(should
(equal "cd"
(orgtbl-to-generic
(org-table-to-lisp "| \ | <c> |\n| a | b |\n|---+---|\n| c | d |")
'(:skip 2))))
;; Test :skipcols parameter.
(should
(equal "a\nc"
(orgtbl-to-generic
(org-table-to-lisp "| a | b |\n| c | d |") '(:skipcols (2)))))
(should
(equal "a\nc"
(orgtbl-to-generic
(org-table-to-lisp
"| / | <c> | <c> |\n| # | a | b |\n|---+---+---|\n| | c | d |")
'(:skipcols (2)))))
;; Test :raw parameter.
(when (featurep 'ox-latex)
(should
(org-string-match-p
"/a/"
(orgtbl-to-generic (org-table-to-lisp "| /a/ | b |")
'(:backend latex :raw t))))))
(ert-deftest test-org-table/to-latex ()
"Test `orgtbl-to-latex' specifications."
(should
(equal "\\begin{tabular}{l}\na\\\\\n\\end{tabular}"
(orgtbl-to-latex (org-table-to-lisp "| a |") nil)))
;; Test :environment parameter.
(should
(equal "\\begin{tabularx}{l}\na\\\\\n\\end{tabularx}"
(orgtbl-to-latex (org-table-to-lisp "| a |")
'(:environment "tabularx"))))
;; Test :booktabs parameter.
(should
(org-string-match-p
"\\toprule" (orgtbl-to-latex (org-table-to-lisp "| a |") '(:booktabs t)))))
(ert-deftest test-org-table/to-html ()
"Test `orgtbl-to-html' specifications."
(should
(equal (orgtbl-to-html (org-table-to-lisp "| a |") nil)
"<table border=\"2\" cellspacing=\"0\" cellpadding=\"6\" rules=\"groups\" frame=\"hsides\">
<colgroup>
<col class=\"left\" />
</colgroup>
<tbody>
<tr>
<td class=\"left\">a</td>
</tr>
</tbody>
</table>"))
;; Test :attributes parameter.
(should
(org-string-match-p
"<table>"
(orgtbl-to-html (org-table-to-lisp "| a |") '(:attributes nil))))
(should
(org-string-match-p
"<table border=\"2\">"
(orgtbl-to-html (org-table-to-lisp "| a |") '(:attributes (:border "2"))))))
(ert-deftest test-org-table/to-texinfo ()
"Test `orgtbl-to-texinfo' specifications."
(should
(equal "@multitable {a}\n@item a\n@end multitable"
(orgtbl-to-texinfo (org-table-to-lisp "| a |") nil)))
;; Test :columns parameter.
(should
(equal "@multitable @columnfractions .4 .6\n@item a\n@tab b\n@end multitable"
(orgtbl-to-texinfo (org-table-to-lisp "| a | b |")
'(:columns ".4 .6"))))
(should
(equal "@multitable @columnfractions .4 .6\n@item a\n@tab b\n@end multitable"
(orgtbl-to-texinfo (org-table-to-lisp "| a | b |")
'(:columns "@columnfractions .4 .6"))))
(should
(equal "@multitable {xxx} {xx}\n@item a\n@tab b\n@end multitable"
(orgtbl-to-texinfo (org-table-to-lisp "| a | b |")
'(:columns "{xxx} {xx}")))))
(ert-deftest test-org-table/to-orgtbl ()
"Test `orgtbl-to-orgtbl' specifications."
(should
(equal "| a | b |\n|---+---|\n| c | d |"
(orgtbl-to-orgtbl
(org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") nil))))
(ert-deftest test-org-table/to-unicode ()
"Test `orgtbl-to-unicode' specifications."
(should
(equal "━━━\n a \n━━━"
(orgtbl-to-unicode (org-table-to-lisp "| a |") nil)))
;; Test :narrow parameter.
(should
(equal "━━━━\n => \n━━━━"
(orgtbl-to-unicode (org-table-to-lisp "| <2> |\n| xxx |")
'(:narrow t)))))
(ert-deftest test-org-table/send-region ()
"Test `orgtbl-send-table' specifications."
;; Error when not at a table.
(should-error
(org-test-with-temp-text "Paragraph"
(orgtbl-send-table)))
;; Error when destination is missing.
(should-error
(org-test-with-temp-text "#+ORGTBL: SEND\n<point>| a |"
(orgtbl-send-table)))
;; Error when transformation function is not specified.
(should-error
(org-test-with-temp-text "
# BEGIN RECEIVE ORGTBL table
# END RECEIVE ORGTBL table
#+ORGTBL: SEND table
<point>| a |"
(orgtbl-send-table)))
;; Standard test.
(should
(equal "| a |\n|---|\n| b |\n"
(org-test-with-temp-text "
# BEGIN RECEIVE ORGTBL table
# END RECEIVE ORGTBL table
#+ORGTBL: SEND table orgtbl-to-orgtbl :hlines nil
<point>| a |\n|---|\n| b |"
(orgtbl-send-table)
(goto-char (point-min))
(buffer-substring-no-properties
(search-forward "# BEGIN RECEIVE ORGTBL table\n")
(progn (search-forward "# END RECEIVE ORGTBL table")
(match-beginning 0)))))))
(provide 'test-org-table)
;;; test-org-table.el ends here