Prechádzať zdrojové kódy

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.
Nicolas Goaziou 13 rokov pred
rodič
commit
c91bcb368f
2 zmenil súbory, kde vykonal 90 pridanie a 47 odobranie
  1. 15 13
      contrib/lisp/org-export.el
  2. 75 34
      testing/lisp/test-org-export.el

+ 15 - 13
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 ""))))
 

+ 75 - 34
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<<target>>][[test]][[target]]\n[1] A\n\n[2] <<test>>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