From c91bcb368f0ec483467da9cbe607eb89ac505f5b Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sun, 20 May 2012 11:53:39 +0200 Subject: [PATCH] org-export: Fix macro expansion when back-end protects dollar signs * contrib/lisp/org-export.el (org-export-get-inbuffer-options): Return an "eval" macro as a regular secondary string instead of a plain string. (org-export-expand-macro): Replace arguments before exporting them (and possibly protecting dollar signs). Refactor code. * testing/lisp/test-org-export.el: Add tests. --- contrib/lisp/org-export.el | 28 ++++---- testing/lisp/test-org-export.el | 109 ++++++++++++++++++++++---------- 2 files changed, 90 insertions(+), 47 deletions(-) diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index a5edc0ada..abd3e1d20 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -1181,9 +1181,9 @@ Assume buffer is in Org mode. Narrowing, if any, is ignored." (value (org-match-string-no-properties 2 val))) (cond ((not value) nil) - ;; Value will be evaled. Leave it as-is. + ;; Value will be evaled: do not parse it. ((string-match "\\`(eval\\>" value) - (list key value)) + (list key (list value))) ;; Value has to be parsed for nested ;; macros. (t @@ -3033,19 +3033,21 @@ INFO is a plist holding export options." (let* ((key (org-element-property :key macro)) (args (org-element-property :args macro)) ;; User's macros are stored in the communication channel with - ;; a ":macro-" prefix. + ;; a ":macro-" prefix. Replace arguments in VALUE. Also + ;; expand recursively macros within. (value (org-export-data - (plist-get info (intern (format ":macro-%s" key))) info))) - ;; Replace arguments in VALUE. - (let ((s 0) n) - (while (string-match "\\$\\([0-9]+\\)" value s) - (setq s (1+ (match-beginning 0)) - n (string-to-number (match-string 1 value))) - (and (>= (length args) n) - (setq value (replace-match (nth (1- n) args) t t value))))) + (mapcar + (lambda (obj) + (if (not (stringp obj)) (org-export-data obj info) + (replace-regexp-in-string + "\\$[0-9]+" + (lambda (arg) + (nth (1- (string-to-number (substring arg 1))) args)) + obj))) + (plist-get info (intern (format ":macro-%s" key)))) + info))) ;; VALUE starts with "(eval": it is a s-exp, `eval' it. - (when (string-match "\\`(eval\\>" value) - (setq value (eval (read value)))) + (when (string-match "\\`(eval\\>" value) (setq value (eval (read value)))) ;; Return string. (format "%s" (or value "")))) diff --git a/testing/lisp/test-org-export.el b/testing/lisp/test-org-export.el index 5075c87ac..78a6b0896 100644 --- a/testing/lisp/test-org-export.el +++ b/testing/lisp/test-org-export.el @@ -59,7 +59,8 @@ already filled in `info'." (declare (debug (form body)) (indent 1)) `(org-test-with-temp-text ,data (let* ((tree (org-element-parse-buffer)) - (info (org-export-collect-tree-properties tree nil))) + (info (org-export-collect-tree-properties + tree (org-export-get-environment)))) ,@body))) (ert-deftest test-org-export/parse-option-keyword () @@ -111,38 +112,6 @@ already filled in `info'." :exclude-tags ("noexport" "invisible") :keywords "test" :language "en" :select-tags ("export") :title ("Some title with spaces"))))) -(ert-deftest test-org-export/define-macro () - "Try defining various Org macro using in-buffer #+MACRO: keyword." - ;; Parsed macro. - (should (equal (org-test-with-temp-text "#+MACRO: one 1" - (org-export-get-inbuffer-options)) - '(:macro-one ("1")))) - ;; Evaled macro. - (should (equal (org-test-with-temp-text "#+MACRO: two (eval (+ 1 1))" - (org-export-get-inbuffer-options)) - '(:macro-two "(eval (+ 1 1))"))) - ;; Incomplete macro. - (should-not (org-test-with-temp-text "#+MACRO: three" - (org-export-get-inbuffer-options))) - ;; Macro with newline character. - (should (equal (org-test-with-temp-text "#+MACRO: four a\\nb" - (org-export-get-inbuffer-options)) - '(:macro-four ("a\nb")))) - ;; Macro with protected newline character. - (should (equal (org-test-with-temp-text "#+MACRO: five a\\\\nb" - (org-export-get-inbuffer-options)) - '(:macro-five ("a\\nb")))) - ;; Recursive macro. - (org-test-with-temp-text "#+MACRO: six 6\n#+MACRO: seven 1 + {{{six}}}" - (should - (equal - (org-export-get-inbuffer-options) - '(:macro-six - ("6") - :macro-seven - ("1 + " (macro (:key "six" :value "{{{six}}}" :args nil :begin 5 :end 14 - :post-blank 0)))))))) - (ert-deftest test-org-export/handle-options () "Test if export options have an impact on output." ;; Test exclude tags. @@ -544,7 +513,7 @@ Paragraph[1][2][fn:lbl3:C<>][[test]][[target]]\n[1] A\n\n[2] <>B" (org-export-get-ordinal (org-export-resolve-fuzzy-link link info) info)) info t))))) -(defun test-org-export/resolve-coderef () +(ert-deftest test-org-export/resolve-coderef () "Test `org-export-resolve-coderef' specifications." (let ((org-coderef-label-format "(ref:%s)")) ;; 1. A link to a "-n -k -r" block returns line number. @@ -659,6 +628,78 @@ Another text. (ref:text) info))))) + +;;; Macro + +(ert-deftest test-org-export/define-macro () + "Try defining various Org macro using in-buffer #+MACRO: keyword." + ;; Parsed macro. + (should (equal (org-test-with-temp-text "#+MACRO: one 1" + (org-export-get-inbuffer-options)) + '(:macro-one ("1")))) + ;; Evaled macro. + (should (equal (org-test-with-temp-text "#+MACRO: two (eval (+ 1 1))" + (org-export-get-inbuffer-options)) + '(:macro-two ("(eval (+ 1 1))")))) + ;; Incomplete macro. + (should-not (org-test-with-temp-text "#+MACRO: three" + (org-export-get-inbuffer-options))) + ;; Macro with newline character. + (should (equal (org-test-with-temp-text "#+MACRO: four a\\nb" + (org-export-get-inbuffer-options)) + '(:macro-four ("a\nb")))) + ;; Macro with protected newline character. + (should (equal (org-test-with-temp-text "#+MACRO: five a\\\\nb" + (org-export-get-inbuffer-options)) + '(:macro-five ("a\\nb")))) + ;; Recursive macro. + (org-test-with-temp-text "#+MACRO: six 6\n#+MACRO: seven 1 + {{{six}}}" + (should + (equal + (org-export-get-inbuffer-options) + '(:macro-six + ("6") + :macro-seven + ("1 + " (macro (:key "six" :value "{{{six}}}" :args nil :begin 5 :end 14 + :post-blank 0)))))))) + +(ert-deftest test-org-export/expand-macro () + "Test `org-export-expand-macro' specifications." + ;; Standard test. + (should + (equal + "some text" + (org-test-with-parsed-data "#+MACRO: macro some text\n{{{macro}}}" + (org-export-expand-macro + (org-element-map tree 'macro 'identity info t) info)))) + ;; Macro with arguments. + (should + (equal + "some text" + (org-test-with-parsed-data "#+MACRO: macro $1 $2\n{{{macro(some,text)}}}" + (org-export-expand-macro + (org-element-map tree 'macro 'identity info t) info)))) + ;; Macro with "eval" + (should + (equal + "3" + (org-test-with-parsed-data "#+MACRO: add (eval (+ $1 $2))\n{{{add(1,2)}}}" + (org-export-expand-macro + (org-element-map tree 'macro 'identity info t) info)))) + ;; Nested macros. + (should + (equal + "inner outer" + (org-test-with-parsed-data + "#+MACRO: in inner\n#+MACRO: out {{{in}}} outer\n{{{out}}}" + (flet ((translate-macro (macro contents info) + (org-export-expand-macro macro info))) + (org-export-expand-macro + (org-element-map tree 'macro 'identity info t) + (org-combine-plists + info `(:translate-alist ((macro . translate-macro)))))))))) + + ;;; Src-block and example-block