From mboxrd@z Thu Jan 1 00:00:00 1970 From: Carsten Dominik Subject: Re: [RFC] Changing internal representation of back-ends to defstructs Date: Wed, 3 Jul 2013 23:48:54 +0200 Message-ID: <0DC61A88-1225-40A8-851F-D1CE751A505C@gmail.com> References: <87r4fgkqql.fsf@gmail.com> Mime-Version: 1.0 (Mac OS X Mail 6.5 \(1508\)) Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: quoted-printable Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:56067) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UuUvD-0007yG-HO for emacs-orgmode@gnu.org; Wed, 03 Jul 2013 17:49:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UuUv4-0003xX-Co for emacs-orgmode@gnu.org; Wed, 03 Jul 2013 17:49:07 -0400 Received: from mail-ea0-x233.google.com ([2a00:1450:4013:c01::233]:43583) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UuUv3-0003xC-LH for emacs-orgmode@gnu.org; Wed, 03 Jul 2013 17:48:58 -0400 Received: by mail-ea0-f179.google.com with SMTP id b15so372097eae.24 for ; Wed, 03 Jul 2013 14:48:56 -0700 (PDT) In-Reply-To: <87r4fgkqql.fsf@gmail.com> List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: Nicolas Goaziou Cc: Org Mode List Hi Nicolas, this does look like a good change - and hopefully few enough external = backends have been written for this patch to not pose a problem. - Carsten On 3.7.2013, at 22:57, Nicolas Goaziou wrote: > Hello, >=20 > Currently, a back-end is a symbol which may point to an entry in > `org-export-registered-backends' variable. Therefore a back-end must = be > registered (with a unique name) before one can call it. Or, to put it > differently, it is impossible to use an anonymous and temporary > back-end. >=20 > This is not satisfying for developers, as there are situations when = you > need to use a one-shot back-end but don't want to clutter registered > back-ends list. You can of course let-bind > `org-export-registered-backends' to something else, but it leads to > clunky code. The process should be abstracted a bit more. >=20 > The following (long) patches address this by defining back-ends as > structures (see `defstruct'), possibly anonymous and by separating > creation from registration process. It allows to quickly create and = use > temporary back-ends. In the example below, we quickly export a string > using a temporary back-end: >=20 > (org-export-string-as > "* H1\n** H2\nSome string" > (org-export-create-backend > :transcoders > '((headline . (lambda (h contents i) > (let ((m (make-string = (org-export-get-relative-level h i) ?=3D))) > (concat m " " (org-element-property :raw-value h) = " " m "\n" > contents)))) > ;; Contents only. > (section . (lambda (e c i) c)) > (paragraph . (lambda (e c i) c))))) >=20 > It is also possible to create a temporary derived back-end. The > following export will use registered `latex' back-end, excepted for > `bold' type objects. >=20 > (org-export-string-as > "Some *bold* /string/" > (org-export-create-backend > :parent 'latex > :transcoders '((italic . (lambda (o c i) (format "\\texit{%s}" = c))))) > 'body-only) >=20 > Besides `org-export-create-backend', tools provided are: > - `org-export-get-backend' > - `org-export-register-backend' > - `org-export-get-all-transcoders' (handles inheritance) > - `org-export-get-all-options' (handles inheritance) > - `org-export-get-all-filters' (handles inheritance) >=20 > At a higher level, `org-export-define-backend' and > `org-export-define-derived-backend' do not change (they are equivalent > to create and register in a row). So this change only matters for > back-end developers who used advanced features like > `org-export-with-translations' (which should now be > `org-export-with-backend' coupled with an anonymous back-end). >=20 > Also, it leads to a cleaner implementation as it removes the confusion > between a back-end and its name. >=20 > The next step after applying this patch will be to make > orgtbl-to-BACKEND functions use anonymous functions in order to > support :splice property, which is tedious with the new export > framework. >=20 > Feedback welcome. >=20 >=20 > Regards, >=20 > --=20 > Nicolas Goaziou > =46rom 35bf951a1cd4c455f01863e128a899d36e76a76c Mon Sep 17 00:00:00 = 2001 > From: Nicolas Goaziou > Date: Mon, 24 Jun 2013 20:52:10 +0200 > Subject: [PATCH 1/2] ox: Change back-ends internal representation to > structures >=20 > * lisp/ox.el (org-export--registered-backends): Renamed from > `org-export-registered-backends'. > (org-export-invisible-backends): Removed variable. > (org-export-get-backend, org-export-get-all-transcoders > org-export-get-all-options, org-export-get-all-filters): New > functions. It replaces `org-export-backend-translate-table'. > (org-export-barf-if-invalid-backend, org-export-derived-backend-p, > org-export-define-backend, org-export-define-derived-backend): > Rewrite functions using new representation. > (org-export-backend-translate-table): Remove function. > (org-export-get-environment): Use new function. > (org-export--get-subtree-options, org-export--parse-option-keyword, > org-export--get-inbuffer-options, org-export--get-global-options, > org-export-to-buffer org-export-to-file, org-export-string-as > org-export-replace-region-by): Update docstring. > (org-export-data-with-translations): Remove function. Use > `org-export-data-with-backend' with a temporary back-end instead. > (org-export-data-with-backend, org-export-as): Reflect new definition > for back-ends. > (org-export--dispatch-action, org-export--dispatch-ui): Reflect new > definition for back-ends and variable removal. Refactoring. > (org-export-filter-apply-functions): Call functions with > current back-end's name, not full back-end. > * lisp/org.el (org-export-backends, org-create-formula--latex-header): > Use new structure and variables. > * testing/lisp/test-ox.el: Update tests. >=20 > This patch separates back-end definition from its registration. Thus, > it allows to use anonymous or unregistered back-ends. > --- > lisp/org.el | 58 ++-- > lisp/ox.el | 527 +++++++++++++++++--------------- > testing/lisp/test-ox.el | 781 = +++++++++++++++++++++++++++--------------------- > 3 files changed, 764 insertions(+), 602 deletions(-) >=20 > diff --git a/lisp/org.el b/lisp/org.el > index 6233972..89cc328 100644 > --- a/lisp/org.el > +++ b/lisp/org.el > @@ -436,8 +436,9 @@ For export specific modules, see also = `org-export-backends'." > (const :tag "C wl: Links to Wanderlust = folders/messages" org-wl) > (repeat :tag "External packages" :inline t (symbol :tag = "Package")))) >=20 > -(defvar org-export-registered-backends) ; =46rom ox.el > +(defvar org-export--registered-backends) ; =46rom ox.el. > (declare-function org-export-derived-backend-p "ox" (backend &rest = backends)) > +(declare-function org-export-backend-name "ox" (backend)) > (defcustom org-export-backends '(ascii html icalendar latex) > "List of export back-ends that should be always available. >=20 > @@ -451,30 +452,29 @@ needed. >=20 > This variable needs to be set before org.el is loaded. If you > need to make a change while Emacs is running, use the customize > -interface or run the following code, where VALUE stands for the > -new value of the variable, after updating it: > +interface or run the following code, where VAL stands for the new > +value of the variable, after updating it: >=20 > \(progn > - \(setq org-export-registered-backends > + \(setq org-export--registered-backends > \(org-remove-if-not > \(lambda (backend) > - \(or (memq backend val) > - \(catch 'parentp > - \(mapc > - \(lambda (b) > - \(and (org-export-derived-backend-p b (car = backend)) > - \(throw 'parentp t))) > - val) > - nil))) > - org-export-registered-backends)) > - \(let ((new-list (mapcar 'car org-export-registered-backends))) > + \(let ((name (org-export-backend-name backend))) > + \(or (memq name val) > + \(catch 'parentp > + \(dolist (b val) > + \(and (org-export-derived-backend-p b name) > + \(throw 'parentp t))))))) > + org-export--registered-backends)) > + \(let ((new-list (mapcar 'org-export-backend-name > + org-export--registered-backends))) > \(dolist (backend val) > \(cond > \((not (load (format \"ox-%s\" backend) t t)) > \(message \"Problems while trying to load export back-end = `%s'\" > backend)) > \((not (memq backend new-list)) (push backend new-list)))) > - \(set-default var new-list))) > + \(set-default 'org-export-backends new-list))) >=20 > Adding a back-end to this list will also pull the back-end it > depends on, if any." > @@ -488,21 +488,20 @@ depends on, if any." > ;; Any back-end not required anymore (not present in VAL and = not > ;; a parent of any back-end in the new value) is removed from = the > ;; list of registered back-ends. > - (setq org-export-registered-backends > + (setq org-export--registered-backends > (org-remove-if-not > (lambda (backend) > - (or (memq backend val) > - (catch 'parentp > - (mapc > - (lambda (b) > - (and (org-export-derived-backend-p b (car = backend)) > - (throw 'parentp t))) > - val) > - nil))) > - org-export-registered-backends)) > + (let ((name (org-export-backend-name backend))) > + (or (memq name val) > + (catch 'parentp > + (dolist (b val) > + (and (org-export-derived-backend-p b name) > + (throw 'parentp t))))))) > + org-export--registered-backends)) > ;; Now build NEW-LIST of both new back-ends and required > ;; parents. > - (let ((new-list (mapcar 'car = org-export-registered-backends))) > + (let ((new-list (mapcar 'org-export-backend-name > + org-export--registered-backends))) > (dolist (backend val) > (cond > ((not (load (format "ox-%s" backend) t t)) > @@ -18494,14 +18493,17 @@ share a good deal of logic." > "Invalid value of = `org-latex-create-formula-image-program'"))) > string tofile options buffer)) >=20 > +(declare-function org-export-get-backend "ox" (name)) > (declare-function org-export--get-global-options "ox" (&optional = backend)) > (declare-function org-export--get-inbuffer-options "ox" (&optional = backend)) > (declare-function org-latex-guess-inputenc "ox-latex" (header)) > (declare-function org-latex-guess-babel-language "ox-latex" (header = info)) > (defun org-create-formula--latex-header () > "Return LaTeX header appropriate for previewing a LaTeX snippet." > - (let ((info (org-combine-plists (org-export--get-global-options = 'latex) > - (org-export--get-inbuffer-options = 'latex)))) > + (let ((info (org-combine-plists (org-export--get-global-options > + (org-export-get-backend 'latex)) > + (org-export--get-inbuffer-options > + (org-export-get-backend 'latex))))) > (org-latex-guess-babel-language > (org-latex-guess-inputenc > (org-splice-latex-header > diff --git a/lisp/ox.el b/lisp/ox.el > index 92ad356..71435b7 100644 > --- a/lisp/ox.el > +++ b/lisp/ox.el > @@ -47,15 +47,10 @@ > ;; The core function is `org-export-as'. It returns the transcoded > ;; buffer as a string. > ;; > -;; An export back-end is defined with `org-export-define-backend', > -;; which defines one mandatory information: his translation table. > -;; Its value is an alist whose keys are elements and objects types = and > -;; values translator functions. See function's docstring for more > -;; information about translators. > -;; > -;; Optionally, `org-export-define-backend' can also support specific > -;; buffer keywords, OPTION keyword's items and filters. Also refer = to > -;; function documentation for more information. > +;; An export back-end is defined with `org-export-define-backend'. > +;; This function can also support specific buffer keywords, OPTION > +;; keyword's items and filters. Refer to function's documentation = for > +;; more information. > ;; > ;; If the new back-end shares most properties with another one, > ;; `org-export-define-derived-backend' can be used to simplify the > @@ -280,14 +275,8 @@ containing the back-end used, as a symbol, and = either a process > or the time at which it finished. It is used to build the menu > from `org-export-stack'.") >=20 > -(defvar org-export-registered-backends nil > +(defvar org-export--registered-backends nil > "List of backends currently available in the exporter. > - > -A backend is stored as a list where CAR is its name, as a symbol, > -and CDR is a plist with the following properties: > -`:filters-alist', `:menu-entry', `:options-alist' and > -`:translate-alist'. > - > This variable is set with `org-export-define-backend' and > `org-export-define-derived-backend' functions.") >=20 > @@ -830,20 +819,6 @@ process faster and the export more portable." > :package-version '(Org . "8.0") > :type '(file :must-match t)) >=20 > -(defcustom org-export-invisible-backends nil > - "List of back-ends that shouldn't appear in the dispatcher. > - > -Any back-end belonging to this list or derived from a back-end > -belonging to it will not appear in the dispatcher menu. > - > -Indeed, Org may require some export back-ends without notice. If > -these modules are never to be used interactively, adding them > -here will avoid cluttering the dispatcher menu." > - :group 'org-export-general > - :version "24.4" > - :package-version '(Org . "8.0") > - :type '(repeat (symbol :tag "Back-End"))) > - > (defcustom org-export-dispatch-use-expert-ui nil > "Non-nil means using a non-intrusive `org-export-dispatch'. > In that case, no help buffer is displayed. Though, an indicator > @@ -863,25 +838,147 @@ mode." >=20 > ;;; Defining Back-ends > ;; > -;; `org-export-define-backend' is the standard way to define an = export > -;; back-end. It allows to specify translators, filters, buffer > -;; options and a menu entry. If the new back-end shares translators > -;; with another back-end, `org-export-define-derived-backend' may be > -;; used instead. > +;; An export back-end is a structure with `org-export-backend' type > +;; and `name', `parent', `transcoders', `options', `filters', = `blocks' > +;; and `menu' slots. > +;; > +;; At the lowest level, a back-end is created with > +;; `org-export-create-backend' function. > +;; > +;; A named back-end can be registered with > +;; `org-export-register-backend' function. A registered back-end can > +;; later be referred to by its name, with `org-export-get-backend' > +;; function. Also, such a back-end can become the parent of a = derived > +;; back-end from which slot values will be inherited by default. > +;; `org-export-derived-backend-p' can check if a given back-end is > +;; derived from a list of back-end names. > +;; > +;; `org-export-get-all-transcoders', `org-export-get-all-options' and > +;; `org-export-get-all-filters' return the full alist of transcoders, > +;; options and filters, including those inherited from ancestors. > ;; > -;; Internally, a back-end is stored as a list, of which CAR is the > -;; name of the back-end, as a symbol, and CDR a plist. Accessors to > -;; properties of a given back-end are: `org-export-backend-filters', > -;; `org-export-backend-menu', `org-export-backend-options' and > -;; `org-export-backend-translate-table'. > +;; At a higher level, `org-export-define-backend' is the standard way > +;; to define an export back-end. If the new back-end is similar to > +;; a registered back-end, `org-export-define-derived-backend' may be > +;; used instead. > ;; > ;; Eventually `org-export-barf-if-invalid-backend' returns an error > ;; when a given back-end hasn't been registered yet. >=20 > -(defun org-export-define-backend (backend translators &rest body) > +(defstruct (org-export-backend (:constructor = org-export-create-backend) > + (:copier nil)) > + name parent transcoders options filters blocks menu) > + > +(defun org-export-get-backend (name) > + "Return export back-end named after NAME. > +NAME is a symbol. Return nil if no such back-end is found." > + (catch 'found > + (dolist (b org-export--registered-backends) > + (when (eq (org-export-backend-name b) name) > + (throw 'found b))))) > + > +(defun org-export-register-backend (backend) > + "Register BACKEND as a known export back-end. > +BACKEND is a structure with `org-export-backend' type." > + ;; Refuse to register an unnamed back-end. > + (unless (org-export-backend-name backend) > + (error "Cannot register a unnamed export back-end")) > + ;; Refuse to register a back-end with an unknown parent. > + (let ((parent (org-export-backend-parent backend))) > + (when (and parent (not (org-export-get-backend parent))) > + (error "Cannot use unknown \"%s\" back-end as a parent" = parent))) > + ;; Register dedicated export blocks in the parser. > + (dolist (name (org-export-backend-blocks backend)) > + (add-to-list 'org-element-block-name-alist > + (cons name 'org-element-export-block-parser))) > + ;; If a back-end with the same name as BACKEND is already > + ;; registered, replace it with BACKEND. Otherwise, simply add > + ;; BACKEND to the list of registered back-ends. > + (let ((old (org-export-get-backend (org-export-backend-name = backend)))) > + (if old (setcar (memq old org-export--registered-backends) = backend) > + (push backend org-export--registered-backends)))) > + > +(defun org-export-barf-if-invalid-backend (backend) > + "Signal an error if BACKEND isn't defined." > + (unless (org-export-backend-p backend) > + (error "Unknown \"%s\" back-end: Aborting export" backend))) > + > +(defun org-export-derived-backend-p (backend &rest backends) > + "Non-nil if BACKEND is derived from one of BACKENDS. > +BACKEND is an export back-end, as returned by, e.g., > +`org-export-create-backend', or a symbol referring to > +a registered back-end. BACKENDS is constituted of symbols." > + (when (symbolp backend) (setq backend (org-export-get-backend = backend))) > + (when backend > + (catch 'exit > + (while (org-export-backend-parent backend) > + (when (memq (org-export-backend-name backend) backends) > + (throw 'exit t)) > + (setq backend > + (org-export-get-backend (org-export-backend-parent = backend)))) > + (memq (org-export-backend-name backend) backends)))) > + > +(defun org-export-get-all-transcoders (backend) > + "Return full translation table for BACKEND. > + > +BACKEND is an export back-end, as return by, e.g,, > +`org-export-create-backend'. Return value is an alist where > +keys are element or object types, as symbols, and values are > +transcoders. > + > +Unlike to `org-export-backend-transcoders', this function > +also returns transcoders inherited from parent back-ends, > +if any." > + (when (symbolp backend) (setq backend (org-export-get-backend = backend))) > + (when backend > + (let ((transcoders (org-export-backend-transcoders backend)) > + parent) > + (while (setq parent (org-export-backend-parent backend)) > + (setq backend (org-export-get-backend parent)) > + (setq transcoders > + (append transcoders (org-export-backend-transcoders = backend)))) > + transcoders))) > + > +(defun org-export-get-all-options (backend) > + "Return export options for BACKEND. > + > +BACKEND is an export back-end, as return by, e.g,, > +`org-export-create-backend'. See `org-export-options-alist' > +for the shape of the return value. > + > +Unlike to `org-export-backend-options', this function also > +returns options inherited from parent back-ends, if any." > + (when (symbolp backend) (setq backend (org-export-get-backend = backend))) > + (when backend > + (let ((options (org-export-backend-options backend)) > + parent) > + (while (setq parent (org-export-backend-parent backend)) > + (setq backend (org-export-get-backend parent)) > + (setq options (append options (org-export-backend-options = backend)))) > + options))) > + > +(defun org-export-get-all-filters (backend) > + "Return complete list of filters for BACKEND. > + > +BACKEND is an export back-end, as return by, e.g,, > +`org-export-create-backend'. Return value is an alist where > +keys are symbols and values lists of functions. > + > +Unlike to `org-export-backend-filters', this function also > +returns filters inherited from parent back-ends, if any." > + (when (symbolp backend) (setq backend (org-export-get-backend = backend))) > + (when backend > + (let ((filters (org-export-backend-filters backend)) > + parent) > + (while (setq parent (org-export-backend-parent backend)) > + (setq backend (org-export-get-backend parent)) > + (setq filters (append filters (org-export-backend-filters = backend)))) > + filters))) > + > +(defun org-export-define-backend (backend transcoders &rest body) > "Define a new back-end BACKEND. >=20 > -TRANSLATORS is an alist between object or element types and > +TRANSCODERS is an alist between object or element types and > functions handling them. >=20 > These functions should return a string without any trailing > @@ -997,32 +1094,23 @@ keywords are understood: > `org-export-options-alist' for more information about > structure of the values." > (declare (indent 1)) > - (let (export-block filters menu-entry options contents) > + (let (blocks filters menu-entry options contents) > (while (keywordp (car body)) > (case (pop body) > (:export-block (let ((names (pop body))) > - (setq export-block > - (if (consp names) (mapcar 'upcase names) > - (list (upcase names)))))) > + (setq blocks (if (consp names) (mapcar 'upcase = names) > + (list (upcase names)))))) > (:filters-alist (setq filters (pop body))) > (:menu-entry (setq menu-entry (pop body))) > (:options-alist (setq options (pop body))) > (t (pop body)))) > - (setq contents (append (list :translate-alist translators) > - (and filters (list :filters-alist filters)) > - (and options (list :options-alist options)) > - (and menu-entry (list :menu-entry = menu-entry)))) > - ;; Register back-end. > - (let ((registeredp (assq backend = org-export-registered-backends))) > - (if registeredp (setcdr registeredp contents) > - (push (cons backend contents) org-export-registered-backends))) > - ;; Tell parser to not parse EXPORT-BLOCK blocks. > - (when export-block > - (mapc > - (lambda (name) > - (add-to-list 'org-element-block-name-alist > - `(,name . org-element-export-block-parser))) > - export-block)))) > + (org-export-register-backend > + (org-export-create-backend :name backend > + :transcoders transcoders > + :options options > + :filters filters > + :blocks blocks > + :menu menu-entry)))) >=20 > (defun org-export-define-derived-backend (child parent &rest body) > "Create a new back-end as a variant of an existing one. > @@ -1077,75 +1165,25 @@ The back-end could then be called with, for = example: >=20 > \(org-export-to-buffer 'my-latex \"*Test my-latex*\")" > (declare (indent 2)) > - (let (export-block filters menu-entry options translators contents) > + (let (blocks filters menu-entry options transcoders contents) > (while (keywordp (car body)) > (case (pop body) > (:export-block (let ((names (pop body))) > - (setq export-block > - (if (consp names) (mapcar 'upcase names) > - (list (upcase names)))))) > + (setq blocks (if (consp names) (mapcar 'upcase = names) > + (list (upcase names)))))) > (:filters-alist (setq filters (pop body))) > (:menu-entry (setq menu-entry (pop body))) > (:options-alist (setq options (pop body))) > - (:translate-alist (setq translators (pop body))) > + (:translate-alist (setq transcoders (pop body))) > (t (pop body)))) > - (setq contents (append > - (list :parent parent) > - (let ((p-table (org-export-backend-translate-table = parent))) > - (list :translate-alist (append translators = p-table))) > - (let ((p-filters (org-export-backend-filters = parent))) > - (list :filters-alist (append filters p-filters))) > - (let ((p-options (org-export-backend-options = parent))) > - (list :options-alist (append options p-options))) > - (and menu-entry (list :menu-entry menu-entry)))) > - (org-export-barf-if-invalid-backend parent) > - ;; Register back-end. > - (let ((registeredp (assq child org-export-registered-backends))) > - (if registeredp (setcdr registeredp contents) > - (push (cons child contents) org-export-registered-backends))) > - ;; Tell parser to not parse EXPORT-BLOCK blocks. > - (when export-block > - (mapc > - (lambda (name) > - (add-to-list 'org-element-block-name-alist > - `(,name . org-element-export-block-parser))) > - export-block)))) > - > -(defun org-export-backend-parent (backend) > - "Return back-end from which BACKEND is derived, or nil." > - (plist-get (cdr (assq backend org-export-registered-backends)) = :parent)) > - > -(defun org-export-backend-filters (backend) > - "Return filters for BACKEND." > - (plist-get (cdr (assq backend org-export-registered-backends)) > - :filters-alist)) > - > -(defun org-export-backend-menu (backend) > - "Return menu entry for BACKEND." > - (plist-get (cdr (assq backend org-export-registered-backends)) > - :menu-entry)) > - > -(defun org-export-backend-options (backend) > - "Return export options for BACKEND." > - (plist-get (cdr (assq backend org-export-registered-backends)) > - :options-alist)) > - > -(defun org-export-backend-translate-table (backend) > - "Return translate table for BACKEND." > - (plist-get (cdr (assq backend org-export-registered-backends)) > - :translate-alist)) > - > -(defun org-export-barf-if-invalid-backend (backend) > - "Signal an error if BACKEND isn't defined." > - (unless (org-export-backend-translate-table backend) > - (error "Unknown \"%s\" back-end: Aborting export" backend))) > - > -(defun org-export-derived-backend-p (backend &rest backends) > - "Non-nil if BACKEND is derived from one of BACKENDS." > - (let ((parent backend)) > - (while (and (not (memq parent backends)) > - (setq parent (org-export-backend-parent parent)))) > - parent)) > + (org-export-register-backend > + (org-export-create-backend :name child > + :parent parent > + :transcoders transcoders > + :options options > + :filters filters > + :blocks blocks > + :menu menu-entry)))) >=20 >=20 >=20 > @@ -1448,14 +1486,15 @@ The back-end could then be called with, for = example: > ;; `org-export--get-subtree-options' and > ;; `org-export--get-inbuffer-options' > ;; > -;; Also, `org-export--install-letbind-maybe' takes care of the part > -;; relative to "#+BIND:" keywords. > +;; Also, `org-export--list-bound-variables' collects bound variables > +;; along with their value in order to set them as buffer local > +;; variables later in the process. >=20 > (defun org-export-get-environment (&optional backend subtreep = ext-plist) > "Collect export options from the current buffer. >=20 > -Optional argument BACKEND is a symbol specifying which back-end > -specific options to read, if any. > +Optional argument BACKEND is an export back-end, as returned by > +`org-export-create-backend'. >=20 > When optional argument SUBTREEP is non-nil, assume the export is > done against the current sub-tree. > @@ -1481,8 +1520,7 @@ inferior to file-local settings." > (list > :back-end > backend > - :translate-alist > - (org-export-backend-translate-table backend) > + :translate-alist (org-export-get-all-transcoders backend) > :footnote-definition-alist > ;; Footnotes definitions must be collected in the original > ;; buffer, as there's no insurance that they will still be in > @@ -1518,11 +1556,12 @@ inferior to file-local settings." >=20 > (defun org-export--parse-option-keyword (options &optional backend) > "Parse an OPTIONS line and return values as a plist. > -Optional argument BACKEND is a symbol specifying which back-end > +Optional argument BACKEND is an export back-end, as returned by, > +e.g., `org-export-create-backend'. It specifies which back-end > specific items to read, if any." > (let* ((all > ;; Priority is given to back-end specific options. > - (append (and backend (org-export-backend-options backend)) > + (append (and backend (org-export-get-all-options backend)) > org-export-options-alist)) > plist) > (dolist (option all) > @@ -1542,7 +1581,8 @@ specific items to read, if any." >=20 > (defun org-export--get-subtree-options (&optional backend) > "Get export options in subtree at point. > -Optional argument BACKEND is a symbol specifying back-end used > +Optional argument BACKEND is an export back-end, as returned by, > +e.g., `org-export-create-backend'. It specifies back-end used > for export. Return options as a plist." > ;; For each buffer keyword, create a headline property setting the > ;; same property in communication channel. The name for the property > @@ -1594,7 +1634,7 @@ for export. Return options as a plist." > (t value))))))))) > ;; Look for both general keywords and back-end specific > ;; options, with priority given to the latter. > - (append (and backend (org-export-backend-options backend)) > + (append (and backend (org-export-get-all-options backend)) > org-export-options-alist))) > ;; Return value. > plist))) > @@ -1602,7 +1642,8 @@ for export. Return options as a plist." > (defun org-export--get-inbuffer-options (&optional backend) > "Return current buffer export options, as a plist. >=20 > -Optional argument BACKEND, when non-nil, is a symbol specifying > +Optional argument BACKEND, when non-nil, is an export back-end, > +as returned by, e.g., `org-export-create-backend'. It specifies > which back-end specific options should also be read in the > process. >=20 > @@ -1612,7 +1653,7 @@ Assume buffer is in Org mode. Narrowing, if = any, is ignored." > (case-fold-search t) > (options (append > ;; Priority is given to back-end specific options. > - (and backend (org-export-backend-options backend)) > + (and backend (org-export-get-all-options backend)) > org-export-options-alist)) > (regexp (format "^[ \t]*#\\+%s:" > (regexp-opt (nconc (delq nil (mapcar 'cadr = options)) > @@ -1725,12 +1766,13 @@ name." >=20 > (defun org-export--get-global-options (&optional backend) > "Return global export options as a plist. > -Optional argument BACKEND, if non-nil, is a symbol specifying > +Optional argument BACKEND, if non-nil, is an export back-end, as > +returned by, e.g., `org-export-create-backend'. It specifies > which back-end specific export options should also be read in the > process." > (let (plist > ;; Priority is given to back-end specific options. > - (all (append (and backend (org-export-backend-options backend)) > + (all (append (and backend (org-export-get-all-options backend)) > org-export-options-alist))) > (dolist (cell all plist) > (let ((prop (car cell))) > @@ -2058,11 +2100,10 @@ a tree with a select tag." > ;; back-end output. It takes care of filtering out elements or > ;; objects according to export options and organizing the output blank > ;; lines and white space are preserved. The function memoizes its > -;; results, so it is cheap to call it within translators. > +;; results, so it is cheap to call it within transcoders. > ;; > ;; It is possible to modify locally the back-end used by > ;; `org-export-data' or even use a temporary back-end by using > -;; `org-export-data-with-translations' and > ;; `org-export-data-with-backend'. > ;; > ;; Internally, three functions handle the filtering of objects and > @@ -2190,24 +2231,6 @@ Return transcoded string." > results))) > (plist-get info :exported-data)))))) >=20 > -(defun org-export-data-with-translations (data translations info) > - "Convert DATA into another format using a given translation table. > -DATA is an element, an object, a secondary string or a string. > -TRANSLATIONS is an alist between element or object types and > -a functions handling them. See `org-export-define-backend' for > -more information. INFO is a plist used as a communication > -channel." > - (org-export-data > - data > - ;; Set-up a new communication channel with TRANSLATIONS as the > - ;; translate table and a new hash table for memoization. > - (org-combine-plists > - info > - (list :translate-alist translations > - ;; Size of the hash table is reduced since this function > - ;; will probably be used on short trees. > - :exported-data (make-hash-table :test 'eq :size 401))))) > - > (defun org-export-data-with-backend (data backend info) > "Convert DATA into BACKEND format. >=20 > @@ -2217,9 +2240,18 @@ channel. >=20 > Unlike to `org-export-with-backend', this function will > recursively convert DATA using BACKEND translation table." > - (org-export-barf-if-invalid-backend backend) > - (org-export-data-with-translations > - data (org-export-backend-translate-table backend) info)) > + (when (symbolp backend) (setq backend (org-export-get-backend = backend))) > + (org-export-data > + data > + ;; Set-up a new communication channel with translations defined in > + ;; BACKEND as the translate table and a new hash table for > + ;; memoization. > + (org-combine-plists > + info > + (list :translate-alist (org-export-get-all-transcoders backend) > + ;; Size of the hash table is reduced since this function > + ;; will probably be used on short trees. > + :exported-data (make-hash-table :test 'eq :size 401))))) >=20 > (defun org-export--interpret-p (blob info) > "Non-nil if element or object BLOB should be interpreted during = export. > @@ -2713,18 +2745,19 @@ channel, as a plist. It must return a string = or nil.") > "Call every function in FILTERS. >=20 > Functions are called with arguments VALUE, current export > -back-end and INFO. A function returning a nil value will be > -skipped. If it returns the empty string, the process ends and > +back-end's name and INFO. A function returning a nil value will > +be skipped. If it returns the empty string, the process ends and > VALUE is ignored. >=20 > Call is done in a LIFO fashion, to be sure that developer > specified filters, if any, are called first." > (catch 'exit > - (dolist (filter filters value) > - (let ((result (funcall filter value (plist-get info :back-end) = info))) > - (cond ((not result) value) > - ((equal value "") (throw 'exit nil)) > - (t (setq value result))))))) > + (let ((backend-name (plist-get info :back-end))) > + (dolist (filter filters value) > + (let ((result (funcall filter value backend-name info))) > + (cond ((not result) value) > + ((equal value "") (throw 'exit nil)) > + (t (setq value result)))))))) >=20 > (defun org-export-install-filters (info) > "Install filters properties in communication channel. > @@ -2755,7 +2788,7 @@ Return the updated communication channel." > plist key > (if (atom value) (cons value (plist-get plist = key)) > (append value (plist-get plist key)))))))) > - (org-export-backend-filters (plist-get info :back-end))) > + (org-export-get-all-filters (plist-get info :back-end))) > ;; Return new communication channel. > (org-combine-plists info plist))) >=20 > @@ -2891,6 +2924,10 @@ The function assumes BUFFER's major mode is = `org-mode'." > (backend &optional subtreep visible-only body-only ext-plist) > "Transcode current Org buffer into BACKEND code. >=20 > +BACKEND is either an export back-end, as returned by, e.g., > +`org-export-create-backend', or a symbol referring to > +a registered back-end. > + > If narrowing is active in the current buffer, only transcode its > narrowed part. >=20 > @@ -2911,6 +2948,7 @@ with external parameters overriding Org default = settings, but > still inferior to file-local settings. >=20 > Return code as a string." > + (when (symbolp backend) (setq backend (org-export-get-backend = backend))) > (org-export-barf-if-invalid-backend backend) > (save-excursion > (save-restriction > @@ -2943,8 +2981,9 @@ Return code as a string." > ;; created, where include keywords, macros are expanded and > ;; code blocks are evaluated. > (org-export-with-buffer-copy > - ;; Run first hook with current back-end as argument. > - (run-hook-with-args 'org-export-before-processing-hook backend) > + ;; Run first hook with current back-end's name as argument. > + (run-hook-with-args 'org-export-before-processing-hook > + (org-export-backend-name backend)) > (org-export-expand-include-keyword) > ;; Update macro templates since #+INCLUDE keywords might have > ;; added some new ones. > @@ -2954,10 +2993,11 @@ Return code as a string." > ;; Update radio targets since keyword inclusion might have > ;; added some more. > (org-update-radio-target-regexp) > - ;; Run last hook with current back-end as argument. > + ;; Run last hook with current back-end's name as argument. > (goto-char (point-min)) > (save-excursion > - (run-hook-with-args 'org-export-before-parsing-hook backend)) > + (run-hook-with-args 'org-export-before-parsing-hook > + (org-export-backend-name backend))) > ;; Update communication channel with environment. Also > ;; install user's and developer's filters. > (setq info > @@ -2980,9 +3020,10 @@ Return code as a string." > ;; Call options filters and update export options. We do not > ;; use `org-export-filter-apply-functions' here since the > ;; arity of such filters is different. > - (dolist (filter (plist-get info :filter-options)) > - (let ((result (funcall filter info backend))) > - (when result (setq info result)))) > + (let ((backend-name (org-export-backend-name backend))) > + (dolist (filter (plist-get info :filter-options)) > + (let ((result (funcall filter info backend-name))) > + (when result (setq info result))))) > ;; Parse buffer and call parse-tree filter on it. > (setq tree > (org-export-filter-apply-functions > @@ -3018,7 +3059,9 @@ Return code as a string." > (backend buffer &optional subtreep visible-only body-only ext-plist) > "Call `org-export-as' with output to a specified buffer. >=20 > -BACKEND is the back-end used for transcoding, as a symbol. > +BACKEND is either an export back-end, as returned by, e.g., > +`org-export-create-backend', or a symbol referring to > +a registered back-end. >=20 > BUFFER is the output buffer. If it already exists, it will be > erased first, otherwise, it will be created. > @@ -3046,8 +3089,10 @@ to kill ring. Return buffer." > (backend file &optional subtreep visible-only body-only ext-plist) > "Call `org-export-as' with output to a specified file. >=20 > -BACKEND is the back-end used for transcoding, as a symbol. FILE > -is the name of the output file, as a string. > +BACKEND is either an export back-end, as returned by, e.g., > +`org-export-create-backend', or a symbol referring to > +a registered back-end. FILE is the name of the output file, as > +a string. >=20 > Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY and > EXT-PLIST are similar to those used in `org-export-as', which > @@ -3074,6 +3119,10 @@ to kill ring. Return output file's name." > (defun org-export-string-as (string backend &optional body-only = ext-plist) > "Transcode STRING into BACKEND code. >=20 > +BACKEND is either an export back-end, as returned by, e.g., > +`org-export-create-backend', or a symbol referring to > +a registered back-end. > + > When optional argument BODY-ONLY is non-nil, only return body > code, without preamble nor postamble. >=20 > @@ -3089,7 +3138,10 @@ Return code as a string." >=20 > ;;;###autoload > (defun org-export-replace-region-by (backend) > - "Replace the active region by its export to BACKEND." > + "Replace the active region by its export to BACKEND. > +BACKEND is either an export back-end, as returned by, e.g., > +`org-export-create-backend', or a symbol referring to > +a registered back-end." > (if (not (org-region-active-p)) > (user-error "No active region to replace") > (let* ((beg (region-beginning)) > @@ -3103,10 +3155,10 @@ Return code as a string." > (defun org-export-insert-default-template (&optional backend subtreep) > "Insert all export keywords with default values at beginning of = line. >=20 > -BACKEND is a symbol representing the export back-end for which > -specific export options should be added to the template, or > -`default' for default template. When it is nil, the user will be > -prompted for a category. > +BACKEND is a symbol referring to the name of a registered export > +back-end, for which specific export options should be added to > +the template, or `default' for default template. When it is nil, > +the user will be prompted for a category. >=20 > If SUBTREEP is non-nil, export configuration will be set up > locally for the subtree through node properties." > @@ -3115,17 +3167,22 @@ locally for the subtree through node = properties." > (when (and subtreep (org-before-first-heading-p)) > (user-error "No subtree to set export options for")) > (let ((node (and subtreep (save-excursion (org-back-to-heading t) = (point)))) > - (backend (or backend > - (intern > - (org-completing-read > - "Options category: " > - (cons "default" > - (mapcar (lambda (b) (symbol-name (car = b))) > - = org-export-registered-backends)))))) > + (backend > + (or backend > + (intern > + (org-completing-read > + "Options category: " > + (cons "default" > + (mapcar (lambda (b) > + (symbol-name (org-export-backend-name = b))) > + org-export--registered-backends)))))) > options keywords) > ;; Populate OPTIONS and KEYWORDS. > - (dolist (entry (if (eq backend 'default) org-export-options-alist > - (org-export-backend-options backend))) > + (dolist (entry (cond ((eq backend 'default) = org-export-options-alist) > + ((org-export-backend-p backend) > + (org-export-get-all-options backend)) > + (t (org-export-get-all-options > + (org-export-backend-name backend))))) > (let ((keyword (nth 1 entry)) > (option (nth 2 entry))) > (cond > @@ -3502,16 +3559,20 @@ Caption lines are separated by a white space." > ;; back-end, it may be used as a fall-back function once all specific > ;; cases have been treated. >=20 > -(defun org-export-with-backend (back-end data &optional contents = info) > - "Call a transcoder from BACK-END on DATA. > -CONTENTS, when non-nil, is the transcoded contents of DATA > -element, as a string. INFO, when non-nil, is the communication > -channel used for export, as a plist.." > - (org-export-barf-if-invalid-backend back-end) > +(defun org-export-with-backend (backend data &optional contents info) > + "Call a transcoder from BACKEND on DATA. > +BACKEND is an export back-end, as returned by, e.g., > +`org-export-create-backend', or a symbol referring to > +a registered back-end. DATA is an Org element, object, secondary > +string or string. CONTENTS, when non-nil, is the transcoded > +contents of DATA element, as a string. INFO, when non-nil, is > +the communication channel used for export, as a plist." > + (when (symbolp backend) (setq backend (org-export-get-backend = backend))) > + (org-export-barf-if-invalid-backend backend) > (let ((type (org-element-type data))) > (if (memq type '(nil org-data)) (error "No foreign transcoder = available") > (let ((transcoder > - (cdr (assq type (org-export-backend-translate-table = back-end))))) > + (cdr (assq type (org-export-get-all-transcoders = backend))))) > (if (functionp transcoder) (funcall transcoder data contents = info) > (error "No foreign transcoder available")))))) >=20 > @@ -5849,43 +5910,31 @@ back to standard interface." > (lambda (value) > ;; Fontify VALUE string. > (org-propertize value 'face 'font-lock-variable-name-face))) > - ;; Prepare menu entries by extracting them from > - ;; `org-export-registered-backends', and sorting them by > - ;; access key and by ordinal, if any. > - (backends > - (sort > - (sort > - (delq nil > - (mapcar > - (lambda (b) > - (let ((name (car b))) > - (catch 'ignored > - ;; Ignore any back-end belonging to > - ;; `org-export-invisible-backends' or derived > - ;; from one of them. > - (dolist (ignored org-export-invisible-backends) > - (when (org-export-derived-backend-p name = ignored) > - (throw 'ignored nil))) > - (org-export-backend-menu name)))) > - org-export-registered-backends)) > - (lambda (a b) > - (let ((key-a (nth 1 a)) > - (key-b (nth 1 b))) > - (cond ((and (numberp key-a) (numberp key-b)) > - (< key-a key-b)) > - ((numberp key-b) t))))) > - (lambda (a b) (< (car a) (car b))))) > + ;; Prepare menu entries by extracting them from registered > + ;; back-ends and sorting them by access key and by ordinal, > + ;; if any. > + (entries > + (sort (sort (delq nil > + (mapcar 'org-export-backend-menu > + org-export--registered-backends)) > + (lambda (a b) > + (let ((key-a (nth 1 a)) > + (key-b (nth 1 b))) > + (cond ((and (numberp key-a) (numberp key-b)) > + (< key-a key-b)) > + ((numberp key-b) t))))) > + 'car-less-than-car)) > ;; Compute a list of allowed keys based on the first key > ;; pressed, if any. Some keys > ;; (?^B, ?^V, ?^S, ?^F, ?^A, ?&, ?# and ?q) are always > ;; available. > (allowed-keys > (nconc (list 2 22 19 6 1) > - (if (not first-key) (org-uniquify (mapcar 'car = backends)) > + (if (not first-key) (org-uniquify (mapcar 'car = entries)) > (let (sub-menu) > - (dolist (backend backends (sort (mapcar 'car = sub-menu) '<)) > - (when (eq (car backend) first-key) > - (setq sub-menu (append (nth 2 backend) = sub-menu)))))) > + (dolist (entry entries (sort (mapcar 'car sub-menu) = '<)) > + (when (eq (car entry) first-key) > + (setq sub-menu (append (nth 2 entry) = sub-menu)))))) > (cond ((eq first-key ?P) (list ?f ?p ?x ?a)) > ((not first-key) (list ?P))) > (list ?& ?#) > @@ -5944,7 +5993,7 @@ back to standard interface." > (nth 1 sub-entry))) > sub-menu "") > (when (zerop (mod index 2)) "\n")))))))) > - backends "")) > + entries "")) > ;; Publishing menu is hard-coded. > (format "\n[%s] Publish > [%s] Current file [%s] Current project > @@ -5979,7 +6028,7 @@ back to standard interface." > ;; UI, display an intrusive help buffer. > (if expertp > (org-export--dispatch-action > - expert-prompt allowed-keys backends options first-key expertp) > + expert-prompt allowed-keys entries options first-key expertp) > ;; At first call, create frame layout in order to display menu. > (unless (get-buffer "*Org Export Dispatcher*") > (delete-other-windows) > @@ -6002,15 +6051,15 @@ back to standard interface." > (set-window-start nil pos))) > (org-fit-window-to-buffer) > (org-export--dispatch-action > - standard-prompt allowed-keys backends options first-key = expertp)))) > + standard-prompt allowed-keys entries options first-key = expertp)))) >=20 > (defun org-export--dispatch-action > - (prompt allowed-keys backends options first-key expertp) > + (prompt allowed-keys entries options first-key expertp) > "Read a character from command input and act accordingly. >=20 > PROMPT is the displayed prompt, as a string. ALLOWED-KEYS is > a list of characters available at a given step in the process. > -BACKENDS is a list of menu entries. OPTIONS, FIRST-KEY and > +ENTRIES is a list of menu entries. OPTIONS, FIRST-KEY and > EXPERTP are the same as defined in `org-export--dispatch-ui', > which see. >=20 > @@ -6067,9 +6116,9 @@ options as CDR." > first-key expertp)) > ;; Action selected: Send key and options back to > ;; `org-export-dispatch'. > - ((or first-key (functionp (nth 2 (assq key backends)))) > + ((or first-key (functionp (nth 2 (assq key entries)))) > (cons (cond > - ((not first-key) (nth 2 (assq key backends))) > + ((not first-key) (nth 2 (assq key entries))) > ;; Publishing actions are hard-coded. Send a special > ;; signal to `org-export-dispatch'. > ((eq first-key ?P) > @@ -6082,10 +6131,10 @@ options as CDR." > ;; path. Indeed, derived backends can share the same > ;; FIRST-KEY. > (t (catch 'found > - (mapc (lambda (backend) > - (let ((match (assq key (nth 2 backend)))) > + (mapc (lambda (entry) > + (let ((match (assq key (nth 2 entry)))) > (when match (throw 'found (nth 2 match))))) > - (member (assq first-key backends) backends))))) > + (member (assq first-key entries) entries))))) > options)) > ;; Otherwise, enter sub-menu. > (t (org-export--dispatch-ui options key expertp))))) > diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el > index cbae08a..0ba20f2 100644 > --- a/testing/lisp/test-ox.el > +++ b/testing/lisp/test-ox.el > @@ -24,30 +24,22 @@ > (unless (featurep 'ox) > (signal 'missing-test-dependency "org-export")) >=20 > -(defmacro org-test-with-backend (backend &rest body) > - "Execute body with an export back-end defined. > - > -BACKEND is the name of the back-end. BODY is the body to > -execute. The defined back-end simply returns parsed data as Org > -syntax." > - (declare (debug (form body)) (indent 1)) > - `(let ((org-export-registered-backends > - ',(list > - (list backend > - :translate-alist > - (let (transcode-table) > - (dolist (type (append org-element-all-elements > - org-element-all-objects) > - transcode-table) > - (push > - (cons type > - (lambda (obj contents info) > - (funcall > - (intern (format = "org-element-%s-interpreter" > - type)) > - obj contents))) > - transcode-table))))))) > - (progn ,@body))) > +(defun org-test-default-backend () > + "Return a default export back-end. > +This back-end simply returns parsed data as Org syntax." > + (org-export-create-backend > + :transcoders (let (transcode-table) > + (dolist (type (append org-element-all-elements > + org-element-all-objects) > + transcode-table) > + (push > + (cons type > + (lambda (obj contents info) > + (funcall > + (intern (format = "org-element-%s-interpreter" > + type)) > + obj contents))) > + transcode-table))))) >=20 > (defmacro org-test-with-parsed-data (data &rest body) > "Execute body with parsed data available. > @@ -108,12 +100,12 @@ already filled in `info'." > (should > (equal "Yes\n" > (org-test-with-temp-text "#+BIND: test-ox-var value" > - (let ((org-export-allow-bind-keywords t) > - org-export-registered-backends) > - (org-export-define-backend 'check > + (let ((org-export-allow-bind-keywords t)) > + (org-export-as > + (org-export-create-backend > + :transcoders > '((section . (lambda (s c i) > - (if (eq test-ox-var 'value) "Yes" = "No"))))) > - (org-export-as 'check)))))) > + (if (eq test-ox-var 'value) "Yes" = "No"))))))))))) >=20 > (ert-deftest test-org-export/parse-option-keyword () > "Test reading all standard #+OPTIONS: items." > @@ -276,14 +268,14 @@ Paragraph" > 'equal > (org-test-with-temp-text-in-file "Test" > (org-mode) > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > - '((template . (lambda (text info) > - (org-element-interpret-data > - (plist-get info :title) info))))) > - (list (org-export-as 'test) > - (file-name-nondirectory > - (file-name-sans-extension (buffer-file-name)))))))) > + (list (org-export-as > + (org-export-create-backend > + :transcoders > + '((template . (lambda (text info) > + (org-element-interpret-data > + (plist-get info :title) info)))))) > + (file-name-nondirectory > + (file-name-sans-extension (buffer-file-name))))))) > ;; If no title is specified, and no file is associated to the > ;; buffer, use buffer's name. > (should > @@ -291,36 +283,37 @@ Paragraph" > 'equal > (org-test-with-temp-text "Test" > (org-mode) > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > - '((template . (lambda (text info) > - (org-element-interpret-data > - (plist-get info :title) info))))) > - (list (org-export-as 'test) (buffer-name)))))) > + (list (org-export-as > + (org-export-create-backend > + :transcoders > + '((template . (lambda (text info) > + (org-element-interpret-data > + (plist-get info :title) info)))))) > + (buffer-name))))) > ;; If a title is specified, use it. > (should > (equal > "Title" > (org-test-with-temp-text-in-file "#+TITLE: Title\nTest" > (org-mode) > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > - '((template . (lambda (text info) > - (org-element-interpret-data > - (plist-get info :title) info))))) > - (org-export-as 'test))))) > + (org-export-as > + (org-export-create-backend > + :transcoders > + '((template . (lambda (text info) > + (org-element-interpret-data > + (plist-get info :title) info))))))))) > ;; If an empty title is specified, do not set it. > (should > (equal > "" > (org-test-with-temp-text-in-file "#+TITLE:\nTest" > (org-mode) > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > - '((template . (lambda (text info) > - (org-element-interpret-data > - (plist-get info :title) info))))) > - (org-export-as 'test)))))) > + (org-export-as > + (org-export-create-backend > + :transcoders > + '((template . (lambda (text info) > + (org-element-interpret-data > + (plist-get info :title) info)))))))))) >=20 > (ert-deftest test-org-export/handle-options () > "Test if export options have an impact on output." > @@ -328,142 +321,148 @@ Paragraph" > (should > (equal "" > (org-test-with-temp-text "* Head1 :noexp:" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:exclude-tags = ("noexp"))))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:exclude-tags ("noexp")))))) > ;; Test include tags for headlines and inlinetasks. > (should > (equal "* H2\n** Sub :exp:\n*** Sub Sub\n" > (org-test-with-temp-text "* H1\n* H2\n** Sub :exp:\n*** Sub = Sub\n* H3" > (let ((org-tags-column 0)) > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:select-tags = ("exp")))))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:select-tags ("exp"))))))) > ;; Test mixing include tags and exclude tags. > - (org-test-with-temp-text " > + (should > + (string-match > + "\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n" > + (org-test-with-temp-text " > * Head1 :export: > ** Sub-Head1 :noexport: > ** Sub-Head2 > * Head2 :noexport: > ** Sub-Head1 :export:" > - (org-test-with-backend test > - (should > - (string-match > - "\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n" > - (org-export-as > - 'test nil nil nil > - '(:select-tags ("export") :exclude-tags ("noexport"))))))) > + (org-export-as (org-test-default-backend) nil nil nil > + '(:select-tags ("export") :exclude-tags = ("noexport")))))) > ;; Ignore tasks. > (should > (equal "" > (let ((org-todo-keywords '((sequence "TODO" "DONE")))) > (org-test-with-temp-text "* TODO Head1" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:with-tasks = nil))))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-tasks nil)))))) > (should > (equal "* TODO Head1\n" > (let ((org-todo-keywords '((sequence "TODO" "DONE")))) > (org-test-with-temp-text "* TODO Head1" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:with-tasks t))))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-tasks t)))))) > ;; Archived tree. > - (org-test-with-temp-text "* Head1 :archive:" > - (let ((org-archive-tag "archive")) > - (org-test-with-backend test > - (should > - (equal (org-export-as 'test nil nil nil '(:with-archived-trees = nil)) > - ""))))) > - (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2" > - (let ((org-archive-tag "archive")) > - (org-test-with-backend test > - (should > - (string-match > - "\\* Head1[ \t]+:archive:" > - (org-export-as 'test nil nil nil > - '(:with-archived-trees headline))))))) > - (org-test-with-temp-text "* Head1 :archive:" > - (let ((org-archive-tag "archive")) > - (org-test-with-backend test > - (should > - (string-match > - "\\`\\* Head1[ \t]+:archive:\n\\'" > - (org-export-as 'test nil nil nil '(:with-archived-trees = t))))))) > + (should > + (equal "" > + (org-test-with-temp-text "* Head1 :archive:" > + (let ((org-archive-tag "archive")) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-archived-trees = nil)))))) > + (should > + (string-match > + "\\* Head1[ \t]+:archive:" > + (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2" > + (let ((org-archive-tag "archive")) > + (org-export-as (org-test-default-backend) nil nil nil > + '(:with-archived-trees headline)))))) > + (should > + (string-match > + "\\`\\* Head1[ \t]+:archive:\n\\'" > + (org-test-with-temp-text "* Head1 :archive:" > + (let ((org-archive-tag "archive")) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-archived-trees t)))))) > ;; Clocks. > - (let ((org-clock-string "CLOCK:")) > - (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]" > - (org-test-with-backend test > - (should > - (equal (org-export-as 'test nil nil nil '(:with-clocks t)) > - "CLOCK: [2012-04-29 sun. 10:45]\n")) > - (should > - (equal (org-export-as 'test nil nil nil '(:with-clocks nil)) = ""))))) > + (should > + (equal "CLOCK: [2012-04-29 sun. 10:45]\n" > + (let ((org-clock-string "CLOCK:")) > + (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]" > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-clocks t)))))) > + (should > + (equal "" > + (let ((org-clock-string "CLOCK:")) > + (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]" > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-clocks nil)))))) > ;; Drawers. > - (let ((org-drawers '("TEST"))) > - (org-test-with-temp-text ":TEST:\ncontents\n:END:" > - (org-test-with-backend test > - (should (equal (org-export-as 'test nil nil nil '(:with-drawers = nil)) > - "")) > - (should (equal (org-export-as 'test nil nil nil '(:with-drawers = t)) > - ":TEST:\ncontents\n:END:\n"))))) > - (let ((org-drawers '("FOO" "BAR"))) > - (org-test-with-temp-text = ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:" > - (org-test-with-backend test > - (should > - (equal (org-export-as 'test nil nil nil '(:with-drawers = ("FOO"))) > - ":FOO:\nkeep\n:END:\n"))))) > - (let ((org-drawers '("FOO" "BAR"))) > - (org-test-with-temp-text = ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:" > - (org-test-with-backend test > - (should > - (equal (org-export-as 'test nil nil nil '(:with-drawers (not = "BAR"))) > - ":FOO:\nkeep\n:END:\n"))))) > + (should > + (equal "" > + (let ((org-drawers '("TEST"))) > + (org-test-with-temp-text ":TEST:\ncontents\n:END:" > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-drawers nil)))))) > + (should > + (equal ":TEST:\ncontents\n:END:\n" > + (let ((org-drawers '("TEST"))) > + (org-test-with-temp-text ":TEST:\ncontents\n:END:" > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-drawers t)))))) > + (should > + (equal ":FOO:\nkeep\n:END:\n" > + (let ((org-drawers '("FOO" "BAR"))) > + (org-test-with-temp-text = ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:" > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-drawers ("FOO"))))))) > + (should > + (equal ":FOO:\nkeep\n:END:\n" > + (let ((org-drawers '("FOO" "BAR"))) > + (org-test-with-temp-text = ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:" > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-drawers (not = "BAR"))))))) > ;; Footnotes. > (should > (equal "Footnote?" > (let ((org-footnote-section nil)) > (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def" > - (org-test-with-backend test > - (org-trim > - (org-export-as 'test nil nil nil '(:with-footnotes = nil)))))))) > + (org-trim (org-export-as (org-test-default-backend) > + nil nil nil '(:with-footnotes = nil))))))) > (should > (equal "Footnote?[fn:1]\n\n[fn:1] Def" > (let ((org-footnote-section nil)) > (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def" > - (org-test-with-backend test > - (org-trim > - (org-export-as 'test nil nil nil '(:with-footnotes = t)))))))) > + (org-trim (org-export-as (org-test-default-backend) > + nil nil nil '(:with-footnotes = t))))))) > ;; Inlinetasks. > (when (featurep 'org-inlinetask) > (should > (equal > + "" > (let ((org-inlinetask-min-level 15)) > (org-test-with-temp-text "*************** Task" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:with-inlinetasks = nil))))) > - "")) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-inlinetasks nil)))))) > (should > (equal > + "" > (let ((org-inlinetask-min-level 15)) > (org-test-with-temp-text > "*************** Task\nContents\n*************** END" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:with-inlinetasks = nil))))) > - ""))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-inlinetasks nil))))))) > ;; Plannings. > - (let ((org-closed-string "CLOSED:")) > - (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]" > - (org-test-with-backend test > - (should > - (equal (org-export-as 'test nil nil nil '(:with-planning t)) > - "CLOSED: [2012-04-29 sun. 10:45]\n")) > - (should > - (equal (org-export-as 'test nil nil nil '(:with-planning nil)) > - ""))))) > + (should > + (equal "CLOSED: [2012-04-29 sun. 10:45]\n" > + (let ((org-closed-string "CLOSED:")) > + (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]" > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-planning t)))))) > + (should > + (equal "" > + (let ((org-closed-string "CLOSED:")) > + (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]" > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-planning nil)))))) > ;; Statistics cookies. > (should > (equal "" > (org-test-with-temp-text "[0/0]" > - (org-test-with-backend test > - (org-export-as > - 'test nil nil nil '(:with-statistics-cookies nil))))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-statistics-cookies = nil)))))) >=20 > (ert-deftest test-org-export/with-timestamps () > "Test `org-export-with-timestamps' specifications." > @@ -472,15 +471,15 @@ Paragraph" > (equal > "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>\n" > (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. = 10:45>" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:with-timestamps t)))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-timestamps t))))) > ;; nil value. > (should > (equal > "" > (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. = 10:45>" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:with-timestamps nil)))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-timestamps nil))))) > ;; `active' value. > (should > (equal > @@ -489,9 +488,8 @@ Paragraph" > "<2012-03-29 Thu>[2012-03-29 Thu] >=20 > Paragraph <2012-03-29 Thu>[2012-03-29 Thu]" > - (org-test-with-backend test > - (org-trim > - (org-export-as 'test nil nil nil '(:with-timestamps = active))))))) > + (org-trim (org-export-as (org-test-default-backend) > + nil nil nil '(:with-timestamps = active)))))) > ;; `inactive' value. > (should > (equal > @@ -500,16 +498,16 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]" > "<2012-03-29 Thu>[2012-03-29 Thu] >=20 > Paragraph <2012-03-29 Thu>[2012-03-29 Thu]" > - (org-test-with-backend test > - (org-trim > - (org-export-as 'test nil nil nil '(:with-timestamps = inactive)))))))) > + (org-trim (org-export-as (org-test-default-backend) > + nil nil nil '(:with-timestamps = inactive))))))) >=20 > (ert-deftest test-org-export/comment-tree () > "Test if export process ignores commented trees." > - (let ((org-comment-string "COMMENT")) > - (org-test-with-temp-text "* COMMENT Head1" > - (org-test-with-backend test > - (should (equal (org-export-as 'test) "")))))) > + (should > + (equal "" > + (let ((org-comment-string "COMMENT")) > + (org-test-with-temp-text "* COMMENT Head1" > + (org-export-as (org-test-default-backend))))))) >=20 > (ert-deftest test-org-export/export-scope () > "Test all export scopes." > @@ -518,22 +516,23 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]" > ** Head2 > text > *** Head3" > - (org-test-with-backend test > - ;; Subtree. > - (forward-line 3) > - (should (equal (org-export-as 'test 'subtree) "text\n*** = Head3\n")) > - ;; Visible. > - (goto-char (point-min)) > - (forward-line) > - (org-cycle) > - (should (equal (org-export-as 'test nil 'visible) "* Head1\n")) > - ;; Region. > - (goto-char (point-min)) > - (forward-line 3) > - (transient-mark-mode 1) > - (push-mark (point) t t) > - (goto-char (point-at-eol)) > - (should (equal (org-export-as 'test) "text\n")))) > + ;; Subtree. > + (forward-line 3) > + (should (equal (org-export-as (org-test-default-backend) = 'subtree) > + "text\n*** Head3\n")) > + ;; Visible. > + (goto-char (point-min)) > + (forward-line) > + (org-cycle) > + (should (equal (org-export-as (org-test-default-backend) nil = 'visible) > + "* Head1\n")) > + ;; Region. > + (goto-char (point-min)) > + (forward-line 3) > + (transient-mark-mode 1) > + (push-mark (point) t t) > + (goto-char (point-at-eol)) > + (should (equal (org-export-as (org-test-default-backend)) = "text\n"))) > ;; Subtree with a code block calling another block outside. > (should > (equal ": 3\n" > @@ -547,19 +546,18 @@ text > #+BEGIN_SRC emacs-lisp > \(+ 1 2) > #+END_SRC" > - (org-test-with-backend test > - (forward-line 1) > - (org-export-as 'test 'subtree))))) > + (forward-line 1) > + (org-export-as (org-test-default-backend) 'subtree)))) > ;; Body only. > - (org-test-with-temp-text "Text" > - (org-test-with-backend test > - (plist-put > - (cdr (assq 'test org-export-registered-backends)) > - :translate-alist > - (cons (cons 'template (lambda (body info) (format = "BEGIN\n%sEND" body))) > - (org-export-backend-translate-table 'test))) > - (should (equal (org-export-as 'test nil nil 'body-only) = "Text\n")) > - (should (equal (org-export-as 'test) "BEGIN\nText\nEND"))))) > + (let ((backend (org-test-default-backend))) > + (setf (org-export-backend-transcoders backend) > + (cons '(template . (lambda (body i) > + (format "BEGIN\n%sEND" body))) > + (org-export-backend-transcoders backend))) > + (org-test-with-temp-text "Text" > + (should (equal (org-export-as backend nil nil 'body-only) > + "Text\n")) > + (should (equal (org-export-as backend) "BEGIN\nText\nEND"))))) >=20 > (ert-deftest test-org-export/output-file-name () > "Test `org-export-output-file-name' specifications." > @@ -667,7 +665,7 @@ body\n"))) > (should > (equal "#+MACRO: macro1 value\nvalue\n" > (org-test-with-temp-text "#+MACRO: macro1 value\n{{{macro1}}}" > - (org-test-with-backend test (org-export-as 'test))))) > + (org-export-as (org-test-default-backend))))) > ;; Expand specific macros. > (should > (equal "me 2012-03-29 me@here Title\n" > @@ -678,7 +676,7 @@ body\n"))) > #+AUTHOR: me > #+EMAIL: me@here > {{{author}}} {{{date}}} {{{email}}} {{{title}}}" > - (let ((output (org-test-with-backend test (org-export-as = 'test)))) > + (let ((output (org-export-as (org-test-default-backend)))) > (substring output (string-match ".*\n\\'" output)))))) > ;; Expand specific macros when property contained a regular macro > ;; already. > @@ -688,7 +686,7 @@ body\n"))) > #+MACRO: macro1 value > #+TITLE: {{{macro1}}} > {{{title}}}" > - (let ((output (org-test-with-backend test (org-export-as = 'test)))) > + (let ((output (org-export-as (org-test-default-backend)))) > (substring output (string-match ".*\n\\'" output)))))) > ;; Expand macros with templates in included files. > (should > @@ -696,57 +694,65 @@ body\n"))) > (org-test-with-temp-text > (format "#+INCLUDE: \"%s/examples/macro-templates.org\" > {{{included-macro}}}" org-test-dir) > - (let ((output (org-test-with-backend test (org-export-as = 'test)))) > + (let ((output (org-export-as (org-test-default-backend)))) > (substring output (string-match ".*\n\\'" output))))))) >=20 > (ert-deftest test-org-export/user-ignore-list () > "Test if `:ignore-list' accepts user input." > - (org-test-with-backend test > - (flet ((skip-note-head > - (data backend info) > - ;; Ignore headlines with the word "note" in their title. > - (org-element-map data 'headline > - (lambda (headline) > - (when (string-match "\\" > - (org-element-property :raw-value = headline)) > - (org-export-ignore-element headline info))) > - info) > - data)) > - ;; Install function in parse tree filters. > - (let ((org-export-filter-parse-tree-functions = '(skip-note-head))) > - (org-test-with-temp-text "* Head1\n* Head2 (note)\n" > - (should (equal (org-export-as 'test) "* Head1\n"))))))) > + (let ((backend (org-test-default-backend))) > + (setf (org-export-backend-transcoders backend) > + (cons '(template . (lambda (body i) > + (format "BEGIN\n%sEND" body))) > + (org-export-backend-transcoders backend))) > + (org-test-with-temp-text "Text" > + (should (equal (org-export-as backend nil nil 'body-only) > + "Text\n")) > + (should (equal (org-export-as backend) "BEGIN\nText\nEND")))) > + (should > + (equal > + "* Head1\n" > + (let ((org-export-filter-parse-tree-functions > + '((lambda (data backend info) > + ;; Ignore headlines with the word "note" in their title. > + (org-element-map data 'headline > + (lambda (headline) > + (when (string-match "\\" > + (org-element-property :raw-value > + headline)) > + (org-export-ignore-element headline info))) > + info) > + data)))) > + (org-test-with-temp-text "* Head1\n* Head2 (note)\n" > + (org-export-as (org-test-default-backend))))))) >=20 > (ert-deftest test-org-export/before-processing-hook () > "Test `org-export-before-processing-hook'." > (should > (equal > "#+MACRO: mac val\nTest\n" > - (org-test-with-backend test > - (org-test-with-temp-text "#+MACRO: mac val\n{{{mac}}} Test" > - (let ((org-export-before-processing-hook > - '((lambda (backend) > - (while (re-search-forward "{{{" nil t) > - (let ((object (org-element-context))) > - (when (eq (org-element-type object) 'macro) > - (delete-region > - (org-element-property :begin object) > - (org-element-property :end object))))))))) > - (org-export-as 'test))))))) > + (org-test-with-temp-text "#+MACRO: mac val\n{{{mac}}} Test" > + (let ((org-export-before-processing-hook > + '((lambda (backend) > + (while (re-search-forward "{{{" nil t) > + (let ((object (org-element-context))) > + (when (eq (org-element-type object) 'macro) > + (delete-region > + (org-element-property :begin object) > + (org-element-property :end object))))))))) > + (org-export-as (org-test-default-backend))))))) >=20 > (ert-deftest test-org-export/before-parsing-hook () > "Test `org-export-before-parsing-hook'." > (should > (equal "Body 1\nBody 2\n" > - (org-test-with-backend test > - (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline = 2\nBody 2" > - (let ((org-export-before-parsing-hook > - '((lambda (backend) > - (goto-char (point-min)) > - (while (re-search-forward = org-outline-regexp-bol nil t) > - (delete-region > - (point-at-bol) (progn (forward-line) = (point)))))))) > - (org-export-as 'test))))))) > + (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline = 2\nBody 2" > + (let ((org-export-before-parsing-hook > + '((lambda (backend) > + (goto-char (point-min)) > + (while (re-search-forward org-outline-regexp-bol = nil t) > + (delete-region > + (point-at-bol) (progn (forward-line) = (point)))))))) > + (org-export-as (org-test-default-backend))))))) >=20 >=20 >=20 > @@ -833,37 +839,37 @@ body\n"))) > ;; Translate table. > (should > (equal '((headline . my-headline-test)) > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test '((headline . = my-headline-test))) > - (org-export-backend-translate-table 'test)))) > + (org-export-get-all-transcoders 'test)))) > ;; Filters. > (should > (equal '((:filter-headline . my-filter)) > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test > '((headline . my-headline-test)) > :filters-alist '((:filter-headline . my-filter))) > - (org-export-backend-filters 'test)))) > + (org-export-backend-filters (org-export-get-backend = 'test))))) > ;; Options. > (should > (equal '((:prop value)) > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test > '((headline . my-headline-test)) > :options-alist '((:prop value))) > - (org-export-backend-options 'test)))) > + (org-export-backend-options (org-export-get-backend = 'test))))) > ;; Menu. > (should > (equal '(?k "Test Export" test) > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test > '((headline . my-headline-test)) > :menu-entry '(?k "Test Export" test)) > - (org-export-backend-menu 'test)))) > + (org-export-backend-menu (org-export-get-backend 'test))))) > ;; Export Blocks. > (should > (equal '(("TEST" . org-element-export-block-parser)) > - (let (org-export-registered-backends = org-element-block-name-alist) > + (let (org-export--registered-backends = org-element-block-name-alist) > (org-export-define-backend 'test > '((headline . my-headline-test)) > :export-block '("test")) > @@ -873,115 +879,218 @@ body\n"))) > "Test `org-export-define-derived-backend' specifications." > ;; Error when parent back-end is not defined. > (should-error > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-derived-backend 'test 'parent))) > ;; Append translation table to parent's. > (should > (equal '((:headline . test) (:headline . parent)) > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'parent '((:headline . parent))) > (org-export-define-derived-backend 'test 'parent > :translate-alist '((:headline . test))) > - (org-export-backend-translate-table 'test)))) > + (org-export-get-all-transcoders 'test)))) > ;; Options defined in the new back have priority over those defined > ;; in parent. > (should > (eq 'test > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'parent > '((:headline . parent)) > :options-alist '((:a nil nil 'parent))) > (org-export-define-derived-backend 'test 'parent > :options-alist '((:a nil nil 'test))) > - (plist-get (org-export--get-global-options 'test) :a))))) > + (plist-get (org-export--get-global-options > + (org-export-get-backend 'test)) > + :a))))) >=20 > (ert-deftest test-org-export/derived-backend-p () > "Test `org-export-derived-backend-p' specifications." > ;; Non-nil with direct match. > (should > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test '((headline . test))) > (org-export-derived-backend-p 'test 'test))) > (should > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test '((headline . test))) > (org-export-define-derived-backend 'test2 'test) > (org-export-derived-backend-p 'test2 'test2))) > ;; Non-nil with a direct parent. > (should > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test '((headline . test))) > (org-export-define-derived-backend 'test2 'test) > (org-export-derived-backend-p 'test2 'test))) > ;; Non-nil with an indirect parent. > (should > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test '((headline . test))) > (org-export-define-derived-backend 'test2 'test) > (org-export-define-derived-backend 'test3 'test2) > (org-export-derived-backend-p 'test3 'test))) > ;; Nil otherwise. > (should-not > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test '((headline . test))) > (org-export-define-backend 'test2 '((headline . test2))) > (org-export-derived-backend-p 'test2 'test))) > (should-not > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test '((headline . test))) > (org-export-define-backend 'test2 '((headline . test2))) > (org-export-define-derived-backend 'test3 'test2) > (org-export-derived-backend-p 'test3 'test)))) >=20 > +(ert-deftest test-org-export/get-all-transcoders () > + "Test `org-export-get-all-transcoders' specifications." > + ;; Return nil when back-end cannot be found. > + (should-not (org-export-get-all-transcoders nil)) > + ;; Same as `org-export-transcoders' if no parent. > + (should > + (equal '((headline . ignore)) > + (org-export-get-all-transcoders > + (org-export-create-backend > + :transcoders '((headline . ignore)))))) > + ;; But inherit from all ancestors whenever possible. > + (should > + (equal '((section . ignore) (headline . ignore)) > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 '((headline . ignore))) > + (org-export-get-all-transcoders > + (org-export-create-backend > + :parent 'b1 :transcoders '((section . ignore))))))) > + (should > + (equal '((paragraph . ignore) (section . ignore) (headline . = ignore)) > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 '((headline . ignore))) > + (org-export-define-derived-backend 'b2 'b1 > + :translate-alist '((section . ignore))) > + (org-export-get-all-transcoders > + (org-export-create-backend > + :parent 'b2 :transcoders '((paragraph . ignore))))))) > + ;; Back-end transcoders overrule inherited ones. > + (should > + (eq 'b > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 '((headline . a))) > + (cdr (assq 'headline > + (org-export-get-all-transcoders > + (org-export-create-backend > + :parent 'b1 :transcoders '((headline . b)))))))))) > + > +(ert-deftest test-org-export/get-all-options () > + "Test `org-export-get-all-options' specifications." > + ;; Return nil when back-end cannot be found. > + (should-not (org-export-get-all-options nil)) > + ;; Same as `org-export-options' if no parent. > + (should > + (equal '((headline . ignore)) > + (org-export-get-all-options > + (org-export-create-backend > + :options '((headline . ignore)))))) > + ;; But inherit from all ancestors whenever possible. > + (should > + (equal '((:key2 value2) (:key1 value1)) > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 nil :options-alist '((:key1 = value1))) > + (org-export-get-all-options > + (org-export-create-backend > + :parent 'b1 :options '((:key2 value2))))))) > + (should > + (equal '((:key3 value3) (:key2 value2) (:key1 value1)) > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 nil :options-alist '((:key1 = value1))) > + (org-export-define-derived-backend 'b2 'b1 > + :options-alist '((:key2 value2))) > + (org-export-get-all-options > + (org-export-create-backend > + :parent 'b2 :options '((:key3 value3))))))) > + ;; Back-end options overrule inherited ones. > + (should > + (eq 'b > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 nil :options-alist '((:key1 . = a))) > + (cdr (assq :key1 > + (org-export-get-all-options > + (org-export-create-backend > + :parent 'b1 :options '((:key1 . b)))))))))) > + > +(ert-deftest test-org-export/get-all-filters () > + "Test `org-export-get-all-filters' specifications." > + ;; Return nil when back-end cannot be found. > + (should-not (org-export-get-all-filters nil)) > + ;; Same as `org-export-filters' if no parent. > + (should > + (equal '((:filter-headline . ignore)) > + (org-export-get-all-filters > + (org-export-create-backend > + :filters '((:filter-headline . ignore)))))) > + ;; But inherit from all ancestors whenever possible. > + (should > + (equal '((:filter-section . ignore) (:filter-headline . ignore)) > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 > + nil :filters-alist '((:filter-headline . ignore))) > + (org-export-get-all-filters > + (org-export-create-backend > + :parent 'b1 :filters '((:filter-section . ignore))))))) > + (should > + (equal '((:filter-paragraph . ignore) > + (:filter-section . ignore) > + (:filter-headline . ignore)) > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 > + nil :filters-alist '((:filter-headline . ignore))) > + (org-export-define-derived-backend 'b2 'b1 > + :filters-alist '((:filter-section . ignore))) > + (org-export-get-all-filters > + (org-export-create-backend > + :parent 'b2 :filters '((:filter-paragraph . ignore))))))) > + ;; Back-end filters overrule inherited ones. > + (should > + (eq 'b > + (let (org-export--registered-backends) > + (org-export-define-backend 'b1 '((:filter-headline . a))) > + (cdr (assq :filter-headline > + (org-export-get-all-filters > + (org-export-create-backend > + :parent 'b1 :filters '((:filter-headline . = b)))))))))) > + > (ert-deftest test-org-export/with-backend () > "Test `org-export-with-backend' definition." > ;; Error when calling an undefined back-end > - (should-error > - (let (org-export-registered-backends) > - (org-export-with-backend 'test "Test"))) > + (should-error (org-export-with-backend nil "Test")) > ;; Error when called back-end doesn't have an appropriate > ;; transcoder. > (should-error > - (let (org-export-registered-backends) > - (org-export-define-backend 'test ((headline . ignore))) > - (org-export-with-backend 'test "Test"))) > + (org-export-with-backend > + (org-export-create-backend :transcoders '((headline . ignore))) > + "Test")) > ;; Otherwise, export using correct transcoder > (should > (equal "Success" > - (let (org-export-registered-backends) > + (let (org-export--registered-backends) > (org-export-define-backend 'test > '((plain-text . (lambda (text contents info) "Failure")))) > (org-export-define-backend 'test2 > '((plain-text . (lambda (text contents info) "Success")))) > (org-export-with-backend 'test2 "Test"))))) >=20 > -(ert-deftest test-org-export/data-with-translations () > - "Test `org-export-data-with-translations' specifications." > - (should > - (equal > - "Success!" > - (org-export-data-with-translations > - '(bold nil "Test") > - '((plain-text . (lambda (text info) "Success")) > - (bold . (lambda (bold contents info) (concat contents "!")))) > - '(:with-emphasize t))))) > - > (ert-deftest test-org-export/data-with-backend () > "Test `org-export-data-with-backend' specifications." > ;; Error when calling an undefined back-end. > - (should-error > - (let (org-export-registered-backends) > - (org-export-data-with-backend 'test "Test" nil))) > + (should-error (org-export-data-with-backend nil "nil" nil)) > ;; Otherwise, export data recursively, using correct back-end. > (should > (equal > "Success!" > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > - '((plain-text . (lambda (text info) "Success")) > - (bold . (lambda (bold contents info) (concat contents "!"))))) > - (org-export-data-with-backend > - '(bold nil "Test") 'test '(:with-emphasize t)))))) > + (org-export-data-with-backend > + '(bold nil "Test") > + (org-export-create-backend > + :transcoders > + '((plain-text . (lambda (text info) "Success")) > + (bold . (lambda (bold contents info) (concat contents "!"))))) > + '(:with-emphasize t))))) >=20 >=20 >=20 > @@ -989,28 +1098,30 @@ body\n"))) >=20 > (ert-deftest test-org-export/export-snippet () > "Test export snippets transcoding." > + ;; Standard test. > (org-test-with-temp-text "@@test:A@@@@t:B@@" > - (org-test-with-backend test > - (plist-put > - (cdr (assq 'test org-export-registered-backends)) > - :translate-alist > - (cons (cons 'export-snippet > - (lambda (snippet contents info) > - (when (eq (org-export-snippet-backend snippet) = 'test) > - (org-element-property :value snippet)))) > - (org-export-backend-translate-table 'test))) > + (let ((backend (org-test-default-backend))) > + (setf (org-export-backend-name backend) 'test) > + (setf (org-export-backend-transcoders backend) > + (cons (cons 'export-snippet > + (lambda (snippet contents info) > + (when (eq (org-export-snippet-backend snippet) = 'test) > + (org-element-property :value snippet)))) > + (org-export-backend-transcoders backend))) > (let ((org-export-snippet-translation-alist nil)) > - (should (equal (org-export-as 'test) "A\n"))) > + (should (equal (org-export-as backend) "A\n"))) > (let ((org-export-snippet-translation-alist '(("t" . "test")))) > - (should (equal (org-export-as 'test) "AB\n"))))) > + (should (equal (org-export-as backend) "AB\n"))))) > ;; Ignored export snippets do not remove any blank. > (should > (equal "begin end\n" > (org-test-with-parsed-data "begin @@test:A@@ end" > - (org-export-data-with-translations > + (org-export-data-with-backend > tree > - '((paragraph . (lambda (paragraph contents info) contents)) > - (section . (lambda (section contents info) contents))) > + (org-export-create-backend > + :transcoders > + '((paragraph . (lambda (paragraph contents info) = contents)) > + (section . (lambda (section contents info) contents)))) > info))))) >=20 >=20 > @@ -1036,11 +1147,11 @@ body\n"))) > (car (org-element-contents def)))))))) > info)))) > ;; 2. Test nested footnotes order. > - (org-test-with-parsed-data > - "Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] = [fn::D].\n\n[fn:3] C." > - (should > - (equal > - '((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4)) > + (should > + (equal > + '((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4)) > + (org-test-with-parsed-data > + "Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] = [fn::D].\n\n[fn:3] C." > (org-element-map tree 'footnote-reference > (lambda (ref) > (when (org-export-footnote-first-reference-p ref info) > @@ -1060,29 +1171,30 @@ body\n"))) > (should > (=3D (length (org-export-collect-footnote-definitions tree = info)) 2)))) > ;; 4. Test footnotes definitions collection. > - (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3]. > + (should > + (=3D 4 > + (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3]. >=20 > \[fn:2] B [fn:3] [fn::D]. >=20 > \[fn:3] C." > - (should (=3D (length (org-export-collect-footnote-definitions = tree info)) > - 4))) > + (length (org-export-collect-footnote-definitions tree = info))))) > ;; 5. Test export of footnotes defined outside parsing scope. > - (org-test-with-temp-text "[fn:1] Out of scope > + (should > + (equal > + "ParagraphOut of scope\n" > + (org-test-with-temp-text "[fn:1] Out of scope > * Title > Paragraph[fn:1]" > - (org-test-with-backend test > - (plist-put > - (cdr (assq 'test org-export-registered-backends)) > - :translate-alist > - (cons (cons 'footnote-reference > - (lambda (fn contents info) > - (org-element-interpret-data > - (org-export-get-footnote-definition fn info)))) > - (org-export-backend-translate-table 'test))) > - (forward-line) > - (should (equal "ParagraphOut of scope\n" > - (org-export-as 'test 'subtree))))) > + (let ((backend (org-test-default-backend))) > + (setf (org-export-backend-transcoders backend) > + (cons (cons 'footnote-reference > + (lambda (fn contents info) > + (org-element-interpret-data > + (org-export-get-footnote-definition fn = info)))) > + (org-export-backend-transcoders backend))) > + (forward-line) > + (org-export-as backend 'subtree))))) > ;; 6. Footnotes without a definition should be provided a fallback > ;; definition. > (should > @@ -1378,8 +1490,8 @@ Paragraph[fn:1]" > "" > (let ((org-inlinetask-min-level 3)) > (org-test-with-temp-text "*** Inlinetask :noexp:\nContents\n*** = end" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:exclude-tags = ("noexp")))))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:exclude-tags ("noexp"))))))) > ;; Inlinetask with an include tag. > (should > (equal > @@ -1387,16 +1499,16 @@ Paragraph[fn:1]" > (let ((org-inlinetask-min-level 3) > (org-tags-column 0)) > (org-test-with-temp-text "* H1\n* H2\n*** Inline :exp:" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:select-tags = ("exp")))))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:select-tags ("exp"))))))) > ;; Ignore inlinetask with a TODO keyword and tasks excluded. > (should > (equal "" > (let ((org-todo-keywords '((sequence "TODO" "DONE"))) > (org-inlinetask-min-level 3)) > (org-test-with-temp-text "*** TODO Inline" > - (org-test-with-backend test > - (org-export-as 'test nil nil nil '(:with-tasks = nil))))))))) > + (org-export-as (org-test-default-backend) > + nil nil nil '(:with-tasks nil)))))))) >=20 >=20 >=20 > @@ -2492,41 +2604,40 @@ Another text. (ref:text) > "Test `inner-template' translator specifications." > (should > (equal "Success!" > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > + (org-test-with-temp-text "* Headline" > + (org-export-as > + (org-export-create-backend > + :transcoders > '((inner-template . (lambda (contents info) "Success!")) > - (headline . (lambda (h c i) "Headline")))) > - (org-test-with-temp-text "* Headline" > - (org-export-as 'test))))) > + (headline . (lambda (h c i) "Headline")))))))) > ;; Inner template is applied even in a "body-only" export. > (should > (equal "Success!" > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > - '((inner-template . (lambda (contents info) "Success!")) > - (headline . (lambda (h c i) "Headline")))) > - (org-test-with-temp-text "* Headline" > - (org-export-as 'test nil nil 'body-only)))))) > + (org-test-with-temp-text "* Headline" > + (org-export-as > + (org-export-create-backend > + :transcoders '((inner-template . (lambda (c i) = "Success!")) > + (headline . (lambda (h c i) "Headline")))) > + nil nil 'body-only))))) >=20 > (ert-deftest test-org-export/template () > "Test `template' translator specifications." > (should > (equal "Success!" > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > - '((template . (lambda (contents info) "Success!")) > - (headline . (lambda (h c i) "Headline")))) > - (org-test-with-temp-text "* Headline" > - (org-export-as 'test))))) > + (org-test-with-temp-text "* Headline" > + (org-export-as > + (org-export-create-backend > + :transcoders '((template . (lambda (contents info) = "Success!")) > + (headline . (lambda (h c i) = "Headline")))))))) > ;; Template is not applied in a "body-only" export. > (should-not > (equal "Success!" > - (let (org-export-registered-backends) > - (org-export-define-backend 'test > - '((template . (lambda (contents info) "Success!")) > - (headline . (lambda (h c i) "Headline")))) > - (org-test-with-temp-text "* Headline" > - (org-export-as 'test nil nil 'body-only)))))) > + (org-test-with-temp-text "* Headline" > + (org-export-as > + (org-export-create-backend > + :transcoders '((template . (lambda (contents info) = "Success!")) > + (headline . (lambda (h c i) "Headline")))) > + nil nil 'body-only))))) >=20 >=20 >=20 > --=20 > 1.8.3.2 >=20 > =46rom 5c3b1765d219fc55edac393460128b9cd8d0d013 Mon Sep 17 00:00:00 = 2001 > From: Nicolas Goaziou > Date: Mon, 24 Jun 2013 20:55:24 +0200 > Subject: [PATCH 2/2] Export back-ends: Apply changes to back-end = structure >=20 > * lisp/ox-html.el (org-html--format-toc-headline): Make use of > anonymous back-ends. > * lisp/ox-odt.el (org-odt-footnote-reference): Make use of anonymous > back-ends. > (org-odt-format-label, org-odt-toc, org-odt-format-headline--wrap): > Use `org-export-with-backend' instead of > `org-export-with-translations'. > * contrib/lisp/ox-freemind.el (org-freemind--build-node-contents): Use > `org-export-with-backend' instead of `org-export-with-translations'. > --- > contrib/lisp/ox-freemind.el | 11 +++--- > lisp/ox-html.el | 19 ++++++----- > lisp/ox-odt.el | 83 = +++++++++++++++++++++++---------------------- > 3 files changed, 57 insertions(+), 56 deletions(-) >=20 > diff --git a/contrib/lisp/ox-freemind.el b/contrib/lisp/ox-freemind.el > index 4e90eff..d31c65f 100644 > --- a/contrib/lisp/ox-freemind.el > +++ b/contrib/lisp/ox-freemind.el > @@ -316,12 +316,11 @@ will result in following node: > (element-contents (org-element-contents element)) > (section (assoc 'section element-contents)) > (section-contents > - (let* ((translations > - (nconc (list (cons 'section > - (lambda (section contents info) > - contents))) > - (plist-get info :translate-alist)))) > - (org-export-data-with-translations section translations = info))) > + (let ((backend (org-export-create-backend > + :parent (org-export-backend-name > + (plist-get info :back-end)) > + :translations '(section . (lambda (e c i) = c))))) > + (org-export-data-with-backend section backend info))) > (itemized-contents-p (let ((first-child-headline > (org-element-map element-contents > 'headline 'identity info t))) > diff --git a/lisp/ox-html.el b/lisp/ox-html.el > index 9ce73c4..0c997b4 100644 > --- a/lisp/ox-html.el > +++ b/lisp/ox-html.el > @@ -1983,16 +1983,17 @@ INFO is a plist used as a communication = channel." > headline-number "-")))) > ;; Body. > (concat section-number > - (org-export-data-with-translations > + (org-export-data-with-backend > (org-export-get-alt-title headline info) > - ;; Ignore any footnote-reference, link, > - ;; radio-target and target in table of contents. > - (append > - '((footnote-reference . ignore) > - (link . (lambda (link desc i) desc)) > - (radio-target . (lambda (radio desc i) desc)) > - (target . ignore)) > - (org-export-backend-translate-table 'html)) > + ;; Create an anonymous back-end that will ignore > + ;; any footnote-reference, link, radio-target and > + ;; target in table of contents. > + (org-export-create-backend > + :parent 'html > + :transcoders '((footnote-reference . ignore) > + (link . (lambda (object c i) c)) > + (radio-target . (lambda (object c = i) c)) > + (target . ignore))) > info) > (and tags "   ") (org-html--tags = tags))))) >=20 > diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el > index 1cccdc6..abf88cd 100644 > --- a/lisp/ox-odt.el > +++ b/lisp/ox-odt.el > @@ -1152,20 +1152,19 @@ See `org-odt--build-date-styles' for = implementation details." > (let* ((title (org-export-translate "Table of Contents" :utf-8 = info)) > (headlines (org-export-collect-headlines > info (and (wholenump depth) depth))) > - (translations (nconc (mapcar > - (lambda (type) > - (cons type (lambda (data contents info) > - contents))) > - (list 'radio-target)) > - (plist-get info :translate-alist)))) > + (backend (org-export-create-backend > + :parent (org-export-backend-name > + (plist-get info :back-end)) > + :transcoders (mapcar > + (lambda (type) (cons type (lambda (d c = i) c))) > + (list 'radio-target))))) > (when headlines > (concat > (org-odt-begin-toc title depth) > (mapconcat > (lambda (headline) > (let* ((entry (org-odt-format-headline--wrap > - headline translations info > - 'org-odt-format-toc-headline)) > + headline backend info = 'org-odt-format-toc-headline)) > (level (org-export-get-relative-level headline info)) > (style (format "Contents_20_%d" level))) > (format "\n%s" > @@ -1731,18 +1730,22 @@ CONTENTS is nil. INFO is a plist holding = contextual information." > (t > (let* ((raw (org-export-get-footnote-definition > footnote-reference info)) > - (translations > - (cons (cons 'paragraph > - (lambda (p c i) > - (org-odt--format-paragraph > - p c "Footnote" "OrgFootnoteCenter" > - "OrgFootnoteQuotations"))) > - (org-export-backend-translate-table 'odt))) > - (def (let ((def (org-trim = (org-export-data-with-translations > - raw translations info)))) > - (if (eq (org-element-type raw) 'org-data) def > - (format "\n%s" > - "Footnote" def))))) > + (def > + (let ((def (org-trim > + (org-export-data-with-backend > + raw > + (org-export-create-backend > + :parent 'odt > + :transcoders > + '((paragraph . (lambda (p c i) > + = (org-odt--format-paragraph > + p c "Footnote" > + "OrgFootnoteCenter" > + = "OrgFootnoteQuotations"))))) > + info)))) > + (if (eq (org-element-type raw) 'org-data) def > + (format "\n%s" > + "Footnote" def))))) > (funcall --format-footnote-definition n def)))))))) >=20 >=20 > @@ -1775,13 +1778,12 @@ CONTENTS is nil. INFO is a plist holding = contextual information." > "%s" > "OrgTag" tag)) tags " : ")))))) >=20 > -(defun org-odt-format-headline--wrap (headline translations info > - &optional = format-function > - &rest extra-keys) > - "Transcode a HEADLINE element from Org to ODT. > -CONTENTS holds the contents of the headline. INFO is a plist > -holding contextual information." > - (setq translations (or translations (plist-get info = :translate-alist))) > +(defun org-odt-format-headline--wrap (headline backend info > + &optional format-function > + &rest extra-keys) > + "Transcode a HEADLINE element using BACKEND. > +INFO is a plist holding contextual information." > + (setq backend (or backend (plist-get info :back-end))) > (let* ((level (+ (org-export-get-relative-level headline info))) > (headline-number (org-export-get-headline-number headline = info)) > (section-number (and (org-export-numbered-headline-p headline = info) > @@ -1789,13 +1791,13 @@ holding contextual information." > headline-number "."))) > (todo (and (plist-get info :with-todo-keywords) > (let ((todo (org-element-property :todo-keyword = headline))) > - (and todo (org-export-data-with-translations > - todo translations info))))) > + (and todo > + (org-export-data-with-backend todo backend = info))))) > (todo-type (and todo (org-element-property :todo-type = headline))) > (priority (and (plist-get info :with-priority) > (org-element-property :priority headline))) > - (text (org-export-data-with-translations > - (org-element-property :title headline) translations = info)) > + (text (org-export-data-with-backend > + (org-element-property :title headline) backend info)) > (tags (and (plist-get info :with-tags) > (org-export-get-tags headline info))) > (headline-label (concat "sec-" (mapconcat 'number-to-string > @@ -1805,7 +1807,7 @@ holding contextual information." > ((functionp org-odt-format-headline-function) > (function* > (lambda (todo todo-type priority text tags > - &allow-other-keys) > + &allow-other-keys) > (funcall org-odt-format-headline-function > todo todo-type priority text = tags)))) > (t 'org-odt-format-headline)))) > @@ -1934,7 +1936,7 @@ holding contextual information." > (let ((format-function > (function* > (lambda (todo todo-type priority text tags > - &key contents &allow-other-keys) > + &key contents &allow-other-keys) > (funcall org-odt-format-inlinetask-function > todo todo-type priority text tags contents))))) > (org-odt-format-headline--wrap > @@ -2149,15 +2151,14 @@ SHORT-CAPTION are strings." > ;; will do. > (short-caption > (let ((short-caption (or short-caption caption)) > - (translations (nconc (mapcar > - (lambda (type) > - (cons type (lambda (data = contents info) > - contents))) > - org-element-all-objects) > - (plist-get info = :translate-alist)))) > + (backend (org-export-create-backend > + :parent (org-export-backend-name > + (plist-get info :back-end)) > + :transcoders > + (mapcar (lambda (type) (cons type (lambda (o c = i) c))) > + org-element-all-objects)))) > (when short-caption > - (org-export-data-with-translations short-caption > - translations info))))) > + (org-export-data-with-backend short-caption backend = info))))) > (when (or label caption) > (let* ((default-category > (case (org-element-type element) > --=20 > 1.8.3.2 >=20