Browse Source

org-export: Expand correctly {{{title}}} and such

* contrib/lisp/org-export.el (org-export-as): Expand correctly
  {{{title}}} and such when they already contain a regular macro.
  This is done by expanding macros in two steps: at first regular
  macros,  then document specific macros.
(org-export-expand-macro): Remove function.
* testing/lisp/test-org-export.el: Add test.
Nicolas Goaziou 5 years ago
parent
commit
a2120a9d73
2 changed files with 75 additions and 77 deletions
  1. 56 60
      contrib/lisp/org-export.el
  2. 19 17
      testing/lisp/test-org-export.el

+ 56 - 60
contrib/lisp/org-export.el

@@ -2563,49 +2563,65 @@ Return code as a string."
 	     (goto-char (point-min))
 	     (forward-line)
 	     (narrow-to-region (point) (point-max))))
-      ;; 1. Get export environment from original buffer.  Also install
-      ;;    user's and developer's filters.
-      (let* ((info (org-export-install-filters
-		    (org-export-get-environment backend subtreep ext-plist)))
-	     ;; 2. Get parse tree.  Buffer isn't parsed directly.
-	     ;;    Instead, a temporary copy is created, where include
-	     ;;    keywords and macros are expanded and code blocks
-	     ;;    are evaluated.
-	     (tree (org-export-with-current-buffer-copy
-		    (unless noexpand
-		      (org-export-expand-include-keyword)
-		      ;; Update radio targets since keyword
-		      ;; inclusion might have added some more.
-		      (org-update-radio-target-regexp)
-		      (org-export-expand-macro info)
-		      ;; TODO: Setting `org-current-export-file' is
-		      ;; required by Org Babel to properly resolve
-		      ;; noweb references.  Once "org-exp.el" is
-		      ;; removed, modify
-		      ;; `org-export-blocks-preprocess' so it
-		      ;; accepts the value as an argument instead.
-		      (let ((org-current-export-file (current-buffer)))
-			(org-export-blocks-preprocess)))
-		    (goto-char (point-min))
-		    ;; Run hook
-		    ;; `org-export-before-parsing-hook'. with current
-		    ;; back-end as argument.
-		    (run-hook-with-args
-		     'org-export-before-parsing-hook backend)
-		    ;; Eventually parse buffer.
-		    (org-element-parse-buffer nil visible-only))))
-	;; 3. Call parse-tree filters to get the final tree.
-	(setq tree
-	      (org-export-filter-apply-functions
-	       (plist-get info :filter-parse-tree) tree info))
-	;; 4. Now tree is complete, compute its properties and add
-	;;    them to communication channel.
+      ;; Install user's and developer's filters in communication
+      ;; channel.
+      (let (info tree)
+	(org-export-with-current-buffer-copy
+	 ;; Update communication channel and get parse tree.  Buffer
+	 ;; isn't parsed directly.  Instead, a temporary copy is
+	 ;; created, where include keywords, macros are expanded and
+	 ;; code blocks are evaluated.
+	 (unless noexpand
+	   (org-export-expand-include-keyword)
+	   ;; Update macro templates since #+INCLUDE keywords might
+	   ;; have added some new ones.
+	   (org-macro-initialize-templates)
+	   (org-macro-replace-all org-macro-templates)
+	   ;; TODO: Setting `org-current-export-file' is required by
+	   ;; Org Babel to properly resolve noweb references.  Once
+	   ;; "org-exp.el" is removed, modify
+	   ;; `org-export-blocks-preprocess' so it accepts the value
+	   ;; as an argument instead.
+	   (let ((org-current-export-file (current-buffer)))
+	     (org-export-blocks-preprocess)))
+	 ;; Update radio targets since keyword inclusion might have
+	 ;; added some more.
+	 (org-update-radio-target-regexp)
+	 ;; Run hook `org-export-before-parsing-hook'. with current
+	 ;; back-end as argument.
+	 (goto-char (point-min))
+	 (run-hook-with-args 'org-export-before-parsing-hook backend)
+	 ;; Initialize communication channel.
+	 (setq info
+	       (org-export-install-filters
+		(org-export-get-environment backend subtreep ext-plist)))
+	 ;; Expand export-specific set of macros: {{{author}}},
+	 ;; {{{date}}}, {{{email}}} and {{{title}}}.  It must be done
+	 ;; once regular macros have been expanded, since document
+	 ;; keywords may contain one of them.
+	 (unless noexpand
+	   (org-macro-replace-all
+	    (list (cons "author"
+			(org-element-interpret-data (plist-get info :author)))
+		  (cons "date"
+			(org-element-interpret-data (plist-get info :date)))
+		  ;; EMAIL is not a parsed keyword: store it as-is.
+		  (cons "email" (or (plist-get info :email) ""))
+		  (cons "title"
+			(org-element-interpret-data (plist-get info :title))))))
+	 ;; Eventually parse buffer.  Call parse-tree filters to get
+	 ;; the final tree.
+	 (setq tree
+	       (org-export-filter-apply-functions
+		(plist-get info :filter-parse-tree)
+		(org-element-parse-buffer nil visible-only) info)))
+	;; Now tree is complete, compute its properties and add them
+	;; to communication channel.
 	(setq info
 	      (org-combine-plists
 	       info (org-export-collect-tree-properties tree info)))
-	;; 5. Eventually transcode TREE.  Wrap the resulting string
-	;;    into a template, if required.  Eventually call
-	;;    final-output filter.
+	;; Eventually transcode TREE.  Wrap the resulting string into
+	;; a template, if required.  Finally call final-output filter.
 	(let* ((body (org-element-normalize-string (org-export-data tree info)))
 	       (template (cdr (assq 'template
 				    (plist-get info :translate-alist))))
@@ -2742,26 +2758,6 @@ Point is at buffer's beginning when BODY is applied."
 	   (goto-char (point-min))
 	   (progn ,@body))))))
 
-(defun org-export-expand-macro (info)
-  "Expand every macro in buffer.
-INFO is a plist containing export options and buffer properties."
-  ;; First update macro templates since #+INCLUDE keywords might have
-  ;; added some new ones.
-  (org-macro-initialize-templates)
-  (org-macro-replace-all
-   ;; Before expanding macros, install {{{author}}}, {{{date}}},
-   ;; {{{email}}} and {{{title}}} templates.
-   (nconc
-    (list (cons "author"
-		(org-element-interpret-data (plist-get info :author)))
-	  (cons "date"
-		(org-element-interpret-data (plist-get info :date)))
-	  ;; EMAIL is not a parsed keyword: store it as-is.
-	  (cons "email" (or (plist-get info :email) ""))
-	  (cons "title"
-		(org-element-interpret-data (plist-get info :title))))
-    org-macro-templates)))
-
 (defun org-export-expand-include-keyword (&optional included dir)
   "Expand every include keyword in buffer.
 Optional argument INCLUDED is a list of included file names along

+ 19 - 17
testing/lisp/test-org-export.el

@@ -439,13 +439,12 @@ body\n")))
   "Test macro expansion in an Org buffer."
   ;; Standard macro expansion.
   (should
-   (equal "#+MACRO: macro1 value\nvalue"
+   (equal "#+MACRO: macro1 value\nvalue\n"
 	  (org-test-with-temp-text "#+MACRO: macro1 value\n{{{macro1}}}"
-	    (let (info)
-	      (org-export-expand-macro info) (buffer-string)))))
-  ;; Export specific macros.
+	    (org-test-with-backend test (org-export-as 'test)))))
+  ;; Expand specific macros.
   (should
-   (equal "me 2012-03-29 me@here Title"
+   (equal "me 2012-03-29 me@here Title\n"
 	  (org-test-with-temp-text
 	      "
 #+TITLE: Title
@@ -453,23 +452,26 @@ body\n")))
 #+AUTHOR: me
 #+EMAIL: me@here
 {{{author}}} {{{date}}} {{{email}}} {{{title}}}"
-	    (let ((info (org-export-get-environment)))
-	      (org-export-expand-macro info)
-	      (goto-char (point-max))
-	      (buffer-substring (line-beginning-position)
-				(line-end-position))))))
+	    (let ((output (org-test-with-backend test (org-export-as 'test))))
+	      (substring output (string-match ".*\n\\'" output))))))
+  ;; Expand specific macros when property contained a regular macro
+  ;; already.
+  (should
+   (equal "value\n"
+	  (org-test-with-temp-text "
+#+MACRO: macro1 value
+#+TITLE: {{{macro1}}}
+{{{title}}}"
+	    (let ((output (org-test-with-backend test (org-export-as 'test))))
+	      (substring output (string-match ".*\n\\'" output))))))
   ;; Expand macros with templates in included files.
   (should
-   (equal "success"
+   (equal "success\n"
 	  (org-test-with-temp-text
 	      (format "#+INCLUDE: \"%s/examples/macro-templates.org\"
 {{{included-macro}}}" org-test-dir)
-	    (let (info)
-	      (org-export-expand-include-keyword)
-	      (org-export-expand-macro info)
-	      (goto-char (point-max))
-	      (buffer-substring (line-beginning-position)
-				(line-end-position)))))))
+	    (let ((output (org-test-with-backend test (org-export-as 'test))))
+	      (substring output (string-match ".*\n\\'" output)))))))
 
 (ert-deftest test-org-export/user-ignore-list ()
   "Test if `:ignore-list' accepts user input."