From 3d56f5639936b38efd2f7a8979850505dffc7aaa Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Mon, 12 Nov 2012 23:21:05 +0100 Subject: [PATCH] org-export: Internal changes to back-end definition * contrib/lisp/org-export.el (org-export-registered-backends): New variable. (org-export-define-backend, org-export-define-derived-backend): Use new variable. Also redefine how sub-menus are defined. (org-export-backend-filters, org-export-backend-menu, org-export-backend-options, org-export-backend-translate-table): New functions. (org-export-get-environment, org-export--parse-option-keyword, org-export--get-subtree-options, org-export--get-inbuffer-options, org-export--get-global-options, org-export-install-filters, org-export-with-backend): Access to data stored in new variable. (org-export-dispatch-ui): Display sub-menus according to new definition. (org-export-dispatch-menu-entries): Removed variable. * contrib/lisp/org-e-beamer.el: Use new sub-menu definition. (org-e-beamer--format-section, org-e-beamer-item, org-e-beamer-keyword): Use `org-export-with-backend' instead of relying on removed variables. * testing/lisp/test-org-export.el: Update tests. This patch gets rid of "invisible" variables, that is variables defvar'ed within a macro. --- contrib/lisp/org-e-beamer.el | 25 ++- contrib/lisp/org-export.el | 336 +++++++++++++++----------------- testing/lisp/test-org-export.el | 135 +++++++------ 3 files changed, 241 insertions(+), 255 deletions(-) diff --git a/contrib/lisp/org-e-beamer.el b/contrib/lisp/org-e-beamer.el index 7eefb5fab..55fe81bb1 100644 --- a/contrib/lisp/org-e-beamer.el +++ b/contrib/lisp/org-e-beamer.el @@ -262,13 +262,14 @@ brackets. Return overlay specification, as a string, or nil." (org-export-define-derived-backend e-beamer e-latex :export-block "BEAMER" - :sub-menu-entry - (?l (?B "As TEX buffer (Beamer)" org-e-beamer-export-as-latex) - (?b "As TEX file (Beamer)" org-e-beamer-export-to-latex) - (?P "As PDF file (Beamer)" org-e-beamer-export-to-pdf) - (?O "As PDF file and open (Beamer)" - (lambda (s v b) - (org-open-file (org-e-beamer-export-to-pdf s v b))))) + :menu-entry + (?l 1 + ((?B "As TEX buffer (Beamer)" org-e-beamer-export-as-latex) + (?b "As TEX file (Beamer)" org-e-beamer-export-to-latex) + (?P "As PDF file (Beamer)" org-e-beamer-export-to-pdf) + (?O "As PDF file and open (Beamer)" + (lambda (s v b) + (org-open-file (org-e-beamer-export-to-pdf s v b)))))) :options-alist ((:beamer-theme "BEAMER_THEME" nil org-e-beamer-theme) (:beamer-color-theme "BEAMER_COLOR_THEME" nil nil t) @@ -393,9 +394,7 @@ CONTENTS holds the contents of the headline. INFO is a plist used as a communication channel." ;; Use `e-latex' back-end output, inserting overlay specifications ;; if possible. - (let ((latex-headline - (funcall (cdr (assq 'headline org-e-latex-translate-alist)) - headline contents info)) + (let ((latex-headline (org-export-with-backend 'e-latex headline contents info)) (mode-specs (org-element-property :beamer-act headline))) (if (and mode-specs (string-match "\\`\\\\\\(.*?\\)\\(?:\\*\\|\\[.*\\]\\)?{" @@ -645,8 +644,7 @@ contextual information." (let ((action (let ((first-element (car (org-element-contents item)))) (and (eq (org-element-type first-element) 'paragraph) (org-e-beamer--element-has-overlay-p first-element)))) - (output (funcall (cdr (assq 'item org-e-latex-translate-alist)) - item contents info))) + (output (org-export-with-backend 'e-latex item contents info))) (if (not action) output ;; If the item starts with a paragraph and that paragraph starts ;; with an export snippet specifying an overlay, insert it after @@ -677,8 +675,7 @@ channel." (when (wholenump depth) (format "\\setcounter{tocdepth}{%s}\n" depth)) "\\tableofcontents" options "\n" "\\end{frame}"))) - (t (funcall (cdr (assq 'keyword org-e-latex-translate-alist)) - keyword contents info))))) + (t (org-export-with-backend 'e-latex keyword contents info))))) ;;;; Link diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index f87ab3ec1..19e79c07f 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -48,11 +48,10 @@ ;; buffer as a string. ;; ;; An export back-end is defined with `org-export-define-backend', -;; which sets one mandatory variable: his translation table. Its name -;; is always `org-BACKEND-translate-alist' where BACKEND stands for -;; the name chosen for the back-end. 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. +;; 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 @@ -252,6 +251,17 @@ whose extension is either \"png\", \"jpeg\", \"jpg\", \"gif\", See `org-export-inline-image-p' for more information about rules.") +(defconst 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.") + ;;; User-configurable Variables @@ -702,7 +712,7 @@ to standard mode." -;;; Defining New Back-ends +;;; Defining Back-ends ;; ;; `org-export-define-backend' is the standard way to define an export ;; back-end. It allows to specify translators, filters, buffer @@ -710,6 +720,12 @@ to standard mode." ;; with another back-end, `org-export-define-derived-backend' may be ;; used instead. ;; +;; 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'. +;; ;; Eventually `org-export-barf-if-invalid-backend' returns an error ;; when a given back-end hasn't been registered yet. @@ -774,12 +790,20 @@ keywords are understood: Menu entry for the export dispatcher. It should be a list like: - \(KEY DESCRIPTION ACTION-OR-MENU) + \(KEY DESCRIPTION-OR-ORDINAL ACTION-OR-MENU) where : KEY is a free character selecting the back-end. - DESCRIPTION is a string naming the back-end. + + DESCRIPTION-OR-ORDINAL is either a string or a number. + + If it is a string, is will be used to name the back-end in + its menu entry. If it is a number, the following menu will + be displayed as a sub-menu of the back-end with the same + KEY. Also, the number will be used to determine in which + order such sub-menus will appear (lowest first). + ACTION-OR-MENU is either a function or an alist. If it is an action, it will be called with three arguments: @@ -828,21 +852,14 @@ keywords are understood: (:options-alist (setq options (pop body))) (t (pop body)))) `(progn - ;; Define translators. - (defvar ,(intern (format "org-%s-translate-alist" backend)) ',translators - "Alist between element or object types and translators.") - ;; Define options. - ,(when options - `(defconst ,(intern (format "org-%s-options-alist" backend)) ',options - ,(format "Alist between %s export properties and ways to set them. -See `org-export-options-alist' for more information on the -structure of the values." - backend))) - ;; Define filters. - ,(when filters - `(defconst ,(intern (format "org-%s-filters-alist" backend)) ',filters - "Alist between filters keywords and back-end specific filters. -See `org-export-filters-alist' for more information.")) + ;; Register back-end. + (add-to-list + 'org-export-registered-backends + ',(cons backend + (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))))) ;; Tell parser to not parse EXPORT-BLOCK blocks. ,(when export-block `(mapc @@ -850,10 +867,6 @@ See `org-export-filters-alist' for more information.")) (add-to-list 'org-element-block-name-alist `(,name . org-element-export-block-parser))) ',export-block)) - ;; Add an entry for back-end in `org-export-dispatch'. - ,(when menu-entry - `(unless (assq (car ',menu-entry) org-export-dispatch-menu-entries) - (add-to-list 'org-export-dispatch-menu-entries ',menu-entry))) ;; Splice in the body, if any. ,@body))) @@ -893,28 +906,6 @@ keywords are understood: `org-export-options-alist' for more information about structure of the values. - :sub-menu-entry - - Append entries to an existing menu in the export dispatcher. - The associated value should be a list whose CAR is the - character selecting the menu to expand and CDR a list of - entries following the pattern: - - \(KEY DESCRIPTION ACTION) - - where KEY is a free character triggering the action, - DESCRIPTION is a string defining the action, and ACTION is - a function that will be called with three arguments: - SUBTREEP, VISIBLE-ONLY and BODY-ONLY. See `org-export-as' - for further explanations. - - Valid values include: - - \(?l (?P \"As PDF file (Beamer)\" org-e-beamer-export-to-pdf) - \(?O \"As PDF file and open (Beamer)\" - \(lambda (s v b) - \(org-open-file (org-e-beamer-export-to-pdf s v b))))) - :translate-alist Alist of element and object types and transcoders that will @@ -934,7 +925,7 @@ The back-end could then be called with, for example: \(org-export-to-buffer 'my-latex \"*Test my-latex*\")" (declare (debug (&define name sexp [&rest [keywordp sexp]] def-body)) (indent 2)) - (let (export-block filters menu-entry options sub-menu-entry translate) + (let (export-block filters menu-entry options sub-menu-entry translators) (while (keywordp (car body)) (case (pop body) (:export-block (let ((names (pop body))) @@ -945,9 +936,21 @@ The back-end could then be called with, for example: (:menu-entry (setq menu-entry (pop body))) (:options-alist (setq options (pop body))) (:sub-menu-entry (setq sub-menu-entry (pop body))) - (:translate-alist (setq translate (pop body))) + (:translate-alist (setq translators (pop body))) (t (pop body)))) `(progn + ;; Register back-end. + (add-to-list + 'org-export-registered-backends + ',(cons child + (append + (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))))) ;; Tell parser to not parse EXPORT-BLOCK blocks. ,(when export-block `(mapc @@ -955,49 +958,32 @@ The back-end could then be called with, for example: (add-to-list 'org-element-block-name-alist `(,name . org-element-export-block-parser))) ',export-block)) - ;; Define filters. - ,(let ((parent-filters (intern (format "org-%s-filters-alist" parent)))) - (when (or (boundp parent-filters) filters) - `(defconst ,(intern (format "org-%s-filters-alist" child)) - ',(append filters - (and (boundp parent-filters) - (copy-sequence (symbol-value parent-filters)))) - "Alist between filters keywords and back-end specific filters. -See `org-export-filters-alist' for more information."))) - ;; Define options. - ,(let ((parent-options (intern (format "org-%s-options-alist" parent)))) - (when (or (boundp parent-options) options) - `(defconst ,(intern (format "org-%s-options-alist" child)) - ',(append options - (and (boundp parent-options) - (copy-sequence (symbol-value parent-options)))) - ,(format "Alist between %s export properties and ways to set them. -See `org-export-options-alist' for more information on the -structure of the values." - child)))) - ;; Define translators. - (defvar ,(intern (format "org-%s-translate-alist" child)) - ',(append translate - (copy-sequence - (symbol-value - (intern (format "org-%s-translate-alist" parent))))) - "Alist between element or object types and translators.") - ;; Add an entry for back-end in `org-export-dispatch'. - ,(when menu-entry - `(unless (assq (car ',menu-entry) org-export-dispatch-menu-entries) - (add-to-list 'org-export-dispatch-menu-entries ',menu-entry))) - ,(when sub-menu-entry - (let ((menu (nth 2 (assq (car sub-menu-entry) - org-export-dispatch-menu-entries)))) - (when menu `(nconc ',menu - ',(org-remove-if (lambda (e) (member e menu)) - (cdr sub-menu-entry)))))) ;; Splice in the body, if any. ,@body))) +(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 (boundp (intern (format "org-%s-translate-alist" backend))) + (unless (org-export-backend-translate-table backend) (error "Unknown \"%s\" back-end: Aborting export" backend))) @@ -1327,8 +1313,7 @@ inferior to file-local settings." :back-end backend :translate-alist - (let ((trans-alist (intern (format "org-%s-translate-alist" backend)))) - (when (boundp trans-alist) (symbol-value trans-alist))) + (org-export-backend-translate-table backend) :footnote-definition-alist ;; Footnotes definitions must be collected in the original ;; buffer, as there's no insurance that they will still be in @@ -1366,12 +1351,8 @@ inferior to file-local settings." "Parse an OPTIONS line and return values as a plist. Optional argument BACKEND is a symbol specifying which back-end specific items to read, if any." - (let* ((all - (append org-export-options-alist - (and backend - (let ((var (intern - (format "org-%s-options-alist" backend)))) - (and (boundp var) (eval var)))))) + (let* ((all (append org-export-options-alist + (and backend (org-export-backend-options backend)))) ;; Build an alist between #+OPTION: item and property-name. (alist (delq nil (mapcar (lambda (e) @@ -1441,10 +1422,7 @@ for export. Return options as a plist." value)))))))) ;; Also look for both general keywords and back-end specific ;; options if BACKEND is provided. - (append (and backend - (let ((var (intern - (format "org-%s-options-alist" backend)))) - (and (boundp var) (symbol-value var)))) + (append (and backend (org-export-backend-options backend)) org-export-options-alist))) ;; Return value. plist))) @@ -1494,13 +1472,9 @@ Assume buffer is in Org mode. Narrowing, if any, is ignored." (setq plist (org-combine-plists plist prop))))))) ;; 2. Standard options, as in `org-export-options-alist'. (let* ((all (append org-export-options-alist - ;; Also look for back-end specific options - ;; if BACKEND is defined. - (and backend - (let ((var - (intern - (format "org-%s-options-alist" backend)))) - (and (boundp var) (eval var)))))) + ;; Also look for back-end specific options if + ;; BACKEND is defined. + (and backend (org-export-backend-options backend)))) ;; Build ALIST between keyword name and property name. (alist (delq nil (mapcar @@ -1569,10 +1543,7 @@ Optional argument BACKEND, if non-nil, is a symbol specifying which back-end specific export options should also be read in the process." (let ((all (append org-export-options-alist - (and backend - (let ((var (intern - (format "org-%s-options-alist" backend)))) - (and (boundp var) (symbol-value var)))))) + (and backend (org-export-backend-options backend)))) ;; Output value. plist) (mapc @@ -2064,9 +2035,9 @@ Any element in `:ignore-list' will be skipped when using ;; ;; From the developer side, filters sets can be installed in the ;; process with the help of `org-export-define-backend', which -;; internally sets `org-BACKEND-filters-alist' variable. Each -;; association has a key among the following symbols and a function or -;; a list of functions as value. +;; internally stores filters as an alist. Each association has a key +;; among the following symbols and a function or a list of functions +;; as value. ;; ;; - `:filter-parse-tree' applies directly on the complete parsed ;; tree. It's the only filters set that doesn't apply to a string. @@ -2513,19 +2484,16 @@ Return the updated communication channel." (setq plist (plist-put plist (car p) (eval (cdr p))))) org-export-filters-alist) ;; Prepend back-end specific filters to that list. - (let ((back-end-filters (intern (format "org-%s-filters-alist" - (plist-get info :back-end))))) - (when (boundp back-end-filters) - (mapc (lambda (p) - ;; Single values get consed, lists are prepended. - (let ((key (car p)) (value (cdr p))) - (when value - (setq plist - (plist-put - plist key - (if (atom value) (cons value (plist-get plist key)) - (append value (plist-get plist key)))))))) - (eval back-end-filters)))) + (mapc (lambda (p) + ;; Single values get consed, lists are prepended. + (let ((key (car p)) (value (cdr p))) + (when value + (setq plist + (plist-put + 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))) ;; Return new communication channel. (org-combine-plists info plist))) @@ -3007,13 +2975,10 @@ Caption lines are separated by a white space." (let ((type (org-element-type data))) (if (or (memq type '(nil org-data))) (error "No foreign transcoder available") - (let ((transcoder (cdr (assq type - (symbol-value - (intern (format "org-%s-translate-alist" - back-end))))))) - (if (not (functionp transcoder)) - (error "No foreign transcoder available") - (apply transcoder data args)))))) + (let ((transcoder + (cdr (assq type (org-export-backend-translate-table back-end))))) + (if (functionp transcoder) (apply transcoder data args) + (error "No foreign transcoder available")))))) ;;;; For Export Snippets @@ -4745,12 +4710,6 @@ to `:default' encoding. If it fails, return S." ;; for its interface, which, in turn, delegates response to key ;; pressed to `org-export-dispatch-action'. -(defvar org-export-dispatch-menu-entries nil - "List of menu entries available for `org-export-dispatch'. -This variable shouldn't be set directly. Set-up :menu-entry -keyword in either `org-export-define-backend' or -`org-export-define-derived-backend' instead.") - ;;;###autoload (defun org-export-dispatch () "Export dispatcher for Org mode. @@ -4815,22 +4774,35 @@ back to standard interface." (if (or (eq access-key t) (eq access-key first-key)) (org-add-props key nil 'face 'org-warning) (org-no-properties key)))) - ;; Make sure order of menu doesn't depend on the order in - ;; which back-ends are loaded. - (backends (sort (copy-sequence org-export-dispatch-menu-entries) - (lambda (a b) (< (car a) (car b))))) + ;; 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) + (org-export-backend-menu (car b))) + 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))))) ;; Compute a list of allowed keys based on the first key ;; pressed, if any. Some keys (?1, ?2, ?3, ?4 and ?q) are ;; always available. (allowed-keys - (nconc (list ?1 ?2 ?3 ?4) - (mapcar 'car - (if (not first-key) backends - (nth 2 (assq first-key backends)))) - (cond ((eq first-key ?P) (list ?f ?p ?x ?a)) - ((not first-key) (list ?P))) - (when expertp (list ??)) - (list ?q))) + (org-uniquify + (nconc (list ?1 ?2 ?3 ?4) + (mapcar 'car + (if (not first-key) backends + (nth 2 (assq first-key backends)))) + (cond ((eq first-key ?P) (list ?f ?p ?x ?a)) + ((not first-key) (list ?P))) + (when expertp (list ??)) + (list ?q)))) ;; Build the help menu for standard UI. (help (unless expertp @@ -4838,7 +4810,7 @@ back to standard interface." ;; Options are hard-coded. (format "Options [%s] Body only: %s [%s] Visible only: %s - [%s] Export scope: %s [%s] Force publishing: %s\n\n" + [%s] Export scope: %s [%s] Force publishing: %s\n" (funcall fontify-key "1" t) (if (memq 'body options) "On " "Off") (funcall fontify-key "2" t) @@ -4847,31 +4819,37 @@ back to standard interface." (if (memq 'subtree options) "Subtree" "Buffer ") (funcall fontify-key "4" t) (if (memq 'force options) "On " "Off")) - ;; Display registered back-end entries. - (mapconcat - (lambda (entry) - (let ((top-key (car entry))) - (concat - (format "[%s] %s\n" - (funcall fontify-key (char-to-string top-key)) - (nth 1 entry)) - (let ((sub-menu (nth 2 entry))) - (unless (functionp sub-menu) - ;; Split sub-menu into two columns. - (let ((index -1)) - (concat - (mapconcat - (lambda (sub-entry) - (incf index) - (format (if (zerop (mod index 2)) " [%s] %-24s" - "[%s] %s\n") - (funcall fontify-key - (char-to-string (car sub-entry)) - top-key) - (nth 1 sub-entry))) - sub-menu "") - (when (zerop (mod index 2)) "\n")))))))) - backends "\n") + ;; Display registered back-end entries. When a key + ;; appears for the second time, do not create another + ;; entry, but append its sub-menu to existing menu. + (let (last-key) + (mapconcat + (lambda (entry) + (let ((top-key (car entry))) + (concat + (unless (eq top-key last-key) + (setq last-key top-key) + (format "\n[%s] %s\n" + (funcall fontify-key (char-to-string top-key)) + (nth 1 entry))) + (let ((sub-menu (nth 2 entry))) + (unless (functionp sub-menu) + ;; Split sub-menu into two columns. + (let ((index -1)) + (concat + (mapconcat + (lambda (sub-entry) + (incf index) + (format + (if (zerop (mod index 2)) " [%s] %-24s" + "[%s] %s\n") + (funcall fontify-key + (char-to-string (car sub-entry)) + top-key) + (nth 1 sub-entry))) + sub-menu "") + (when (zerop (mod index 2)) "\n")))))))) + backends "")) ;; Publishing menu is hard-coded. (format "\n[%s] Publish [%s] Current file [%s] Current project diff --git a/testing/lisp/test-org-export.el b/testing/lisp/test-org-export.el index 132b5902e..ba70e208c 100644 --- a/testing/lisp/test-org-export.el +++ b/testing/lisp/test-org-export.el @@ -23,18 +23,22 @@ 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 ((,(intern (format "org-%s-translate-alist" backend)) - ',(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))))) + `(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))) (defmacro org-test-with-parsed-data (data &rest body) @@ -353,13 +357,6 @@ text (forward-line) (org-cycle) (should (equal (org-export-as 'test nil 'visible) "* Head1\n")) - ;; Body only. - (flet ((org-test-template (body info) (format "BEGIN\n%sEND" body))) - (push '(template . org-test-template) org-test-translate-alist) - (should (equal (org-export-as 'test nil nil 'body-only) - "* Head1\n** Head2\ntext\n*** Head3\n")) - (should (equal (org-export-as 'test) - "BEGIN\n* Head1\n** Head2\ntext\n*** Head3\nEND"))) ;; Region. (goto-char (point-min)) (forward-line 3) @@ -382,7 +379,17 @@ text #+END_SRC" (org-test-with-backend test (forward-line 1) - (org-export-as 'test 'subtree)))))) + (org-export-as 'test '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"))))) (ert-deftest test-org-export/expand-include () "Test file inclusion in an Org buffer." @@ -571,16 +578,18 @@ body\n"))) "Test export snippets transcoding." (org-test-with-temp-text "@@test:A@@@@t:B@@" (org-test-with-backend test - (let ((org-test-translate-alist - (cons (cons 'export-snippet - (lambda (snippet contents info) - (when (eq (org-export-snippet-backend snippet) 'test) - (org-element-property :value snippet)))) - org-test-translate-alist))) - (let ((org-export-snippet-translation-alist nil)) - (should (equal (org-export-as 'test) "A\n"))) - (let ((org-export-snippet-translation-alist '(("t" . "test")))) - (should (equal (org-export-as 'test) "AB\n"))))))) + (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 ((org-export-snippet-translation-alist nil)) + (should (equal (org-export-as 'test) "A\n"))) + (let ((org-export-snippet-translation-alist '(("t" . "test")))) + (should (equal (org-export-as 'test) "AB\n")))))) @@ -595,29 +604,29 @@ body\n"))) (equal '((1 . "A\n") (2 . "B") (3 . "C") (4 . "D")) (org-test-with-parsed-data - "Text[fn:1] [1] [fn:label:C] [fn::D]\n\n[fn:1] A\n\n[1] B" - (org-element-map - tree 'footnote-reference - (lambda (ref) - (let ((def (org-export-get-footnote-definition ref info))) - (cons (org-export-get-footnote-number ref info) - (if (eq (org-element-property :type ref) 'inline) (car def) - (car (org-element-contents - (car (org-element-contents def)))))))) - info)))) + "Text[fn:1] [1] [fn:label:C] [fn::D]\n\n[fn:1] A\n\n[1] B" + (org-element-map + tree 'footnote-reference + (lambda (ref) + (let ((def (org-export-get-footnote-definition ref info))) + (cons (org-export-get-footnote-number ref info) + (if (eq (org-element-property :type ref) 'inline) (car def) + (car (org-element-contents + (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)) - (org-element-map - tree 'footnote-reference - (lambda (ref) - (when (org-export-footnote-first-reference-p ref info) - (cons (org-export-get-footnote-number ref info) - (org-element-property :label ref)))) - info)))) + "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)) + (org-element-map + tree 'footnote-reference + (lambda (ref) + (when (org-export-footnote-first-reference-p ref info) + (cons (org-export-get-footnote-number ref info) + (org-element-property :label ref)))) + info)))) ;; 3. Test nested footnote in invisible definitions. (org-test-with-temp-text "Text[1]\n\n[1] B [2]\n\n[2] C." ;; Hide definitions. @@ -636,22 +645,24 @@ body\n"))) \[fn:2] B [fn:3] [fn::D]. \[fn:3] C." - (should (= (length (org-export-collect-footnote-definitions tree info)) - 4))) + (should (= (length (org-export-collect-footnote-definitions tree info)) + 4))) ;; 5. Test export of footnotes defined outside parsing scope. (org-test-with-temp-text "[fn:1] Out of scope * Title Paragraph[fn:1]" (org-test-with-backend test - (let ((org-test-translate-alist - (cons (cons 'footnote-reference - (lambda (fn contents info) - (org-element-interpret-data - (org-export-get-footnote-definition fn info)))) - org-test-translate-alist))) - (forward-line) - (should (equal "ParagraphOut of scope\n" - (org-export-as 'test 'subtree)))))))) + (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)))))))