Browse Source

org-macro: Allow macros in parsed keywords and associated properties

* lisp/org-macro.el (org-macro-replace-all): Add optional argument.
  Now accept macros in parsed keywords and associated properties.

* lisp/ox.el (org-export-as): Apply signature change.

* testing/lisp/test-ox.el (test-org-export/expand-macro): Add test.
Nicolas Goaziou 10 years ago
parent
commit
fa64b59b05
3 changed files with 70 additions and 27 deletions
  1. 43 25
      lisp/org-macro.el
  2. 8 2
      lisp/ox.el
  3. 19 0
      testing/lisp/test-ox.el

+ 43 - 25
lisp/org-macro.el

@@ -159,7 +159,7 @@ default value.  Return nil if no template was found."
         ;; Return string.
         (format "%s" (or value ""))))))
 
-(defun org-macro-replace-all (templates &optional finalize)
+(defun org-macro-replace-all (templates &optional finalize keywords)
   "Replace all macros in current buffer by their expansion.
 
 TEMPLATES is an alist of templates used for expansion.  See
@@ -169,35 +169,53 @@ If optional arg FINALIZE is non-nil, raise an error if a macro is
 found in the buffer with no definition in TEMPLATES."
   (save-excursion
     (goto-char (point-min))
-    (let (record)
+    (let ((properties-regexp
+	   (format "\\`EXPORT_%s\\+?\\'" (regexp-opt keywords)))
+	  record)
       (while (re-search-forward "{{{[-A-Za-z0-9_]" nil t)
-	(let ((object (org-element-context)))
-	  (when (eq (org-element-type object) 'macro)
-	    (let* ((value (org-macro-expand object templates))
-		   (begin (org-element-property :begin object))
+	(let* ((datum (save-match-data (org-element-context)))
+	       (type (org-element-type datum))
+	       (macro
+		(cond
+		 ((eq type 'macro) datum)
+		 ;; In parsed keywords and associated node properties,
+		 ;; force macro recognition.
+		 ((or (and (eq type 'keyword)
+			   (member (org-element-property :key datum) keywords))
+		      (and (eq type 'node-property)
+			   (org-string-match-p
+			    properties-regexp
+			    (org-element-property :key datum))))
+		  (save-restriction
+		    (narrow-to-region (match-beginning 0) (line-end-position))
+		    (org-element-map (org-element-parse-buffer) 'macro
+		      #'identity nil t))))))
+	  (when macro
+	    (let* ((value (org-macro-expand macro templates))
+		   (begin (org-element-property :begin macro))
 		   (signature (list begin
-				    object
-				    (org-element-property :args object))))
+				    macro
+				    (org-element-property :args macro))))
 	      ;; Avoid circular dependencies by checking if the same
 	      ;; macro with the same arguments is expanded at the same
 	      ;; position twice.
-	      (if (member signature record)
-		  (error "Circular macro expansion: %s"
-			 (org-element-property :key object))
-		(cond (value
-		       (push signature record)
-		       (delete-region
-			begin
-			;; Preserve white spaces after the macro.
-			(progn (goto-char (org-element-property :end object))
-			       (skip-chars-backward " \t")
-			       (point)))
-		       ;; Leave point before replacement in case of recursive
-		       ;; expansions.
-		       (save-excursion (insert value)))
-		      (finalize
-		       (error "Undefined Org macro: %s; aborting"
-			      (org-element-property :key object))))))))))))
+	      (cond ((member signature record)
+		     (error "Circular macro expansion: %s"
+			    (org-element-property :key macro)))
+		    (value
+		     (push signature record)
+		     (delete-region
+		      begin
+		      ;; Preserve white spaces after the macro.
+		      (progn (goto-char (org-element-property :end macro))
+			     (skip-chars-backward " \t")
+			     (point)))
+		     ;; Leave point before replacement in case of
+		     ;; recursive expansions.
+		     (save-excursion (insert value)))
+		    (finalize
+		     (error "Undefined Org macro: %s; aborting"
+			    (org-element-property :key macro)))))))))))
 
 (defun org-macro-escape-arguments (&rest args)
   "Build macro's arguments string from ARGS.

+ 8 - 2
lisp/ox.el

@@ -2850,6 +2850,11 @@ Return code as a string."
 				      (and visible-only 'visible-only)
 				      (and body-only 'body-only))))
 		    (org-export--get-buffer-attributes)))
+	     (parsed-keywords
+	      (delq nil
+		    (mapcar (lambda (o) (and (eq (nth 4 o) 'parse) (nth 1 o)))
+			    (append (org-export-get-all-options backend)
+				    org-export-options-alist))))
 	     tree)
 	;; Update communication channel and get parse tree.  Buffer
 	;; isn't parsed directly.  Instead, a temporary copy is
@@ -2864,7 +2869,7 @@ Return code as a string."
 	 ;; Update macro templates since #+INCLUDE keywords might have
 	 ;; added some new ones.
 	 (org-macro-initialize-templates)
-	 (org-macro-replace-all org-macro-templates)
+	 (org-macro-replace-all org-macro-templates nil parsed-keywords)
 	 (org-export-execute-babel-code)
 	 ;; Update radio targets since keyword inclusion might have
 	 ;; added some more.
@@ -2908,7 +2913,8 @@ Return code as a string."
 	   (cons "email" (org-element-interpret-data (plist-get info :email)))
 	   (cons "title" (org-element-interpret-data (plist-get info :title)))
 	   (cons "results" "$1"))
-	  'finalize)
+	  'finalize
+	  parsed-keywords)
 	 ;; Parse buffer.
 	 (setq tree (org-element-parse-buffer nil visible-only))
 	 ;; Prune tree from non-exported elements and transform

+ 19 - 0
testing/lisp/test-ox.el

@@ -1091,6 +1091,25 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
    (equal "#+MACRO: macro1 value\nvalue\n"
 	  (org-test-with-temp-text "#+MACRO: macro1 value\n{{{macro1}}}"
 	    (org-export-as (org-test-default-backend)))))
+  ;; Allow macro in parsed keywords and associated properties.
+  ;; Standard macro expansion.
+  (should
+   (string-match
+    "#\\+K: value"
+    (let ((backend (org-export-create-backend
+		    :parent 'org
+		    :options '((:k "K" nil nil parse)))))
+      (org-test-with-temp-text "#+MACRO: macro value\n#+K: {{{macro}}}"
+	(org-export-as backend)))))
+  (should
+   (string-match
+    ":EXPORT_K: v"
+    (let ((backend (org-export-create-backend
+		    :parent 'org
+		    :options '((:k "K" nil nil parse)))))
+      (org-test-with-temp-text
+	  "#+MACRO: m v\n* H\n:PROPERTIES:\n:EXPORT_K: {{{m}}}\n:END:"
+	(org-export-as backend nil nil nil '(:with-properties t))))))
   ;; Expand specific macros.
   (should
    (equal "me 2012-03-29 me@here Title\n"