From fa64b59b05a2344de2d377bf30004d50a73dd4cf Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sun, 5 Apr 2015 13:40:44 +0200 Subject: [PATCH] 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. --- lisp/org-macro.el | 68 ++++++++++++++++++++++++++--------------- lisp/ox.el | 10 ++++-- testing/lisp/test-ox.el | 19 ++++++++++++ 3 files changed, 70 insertions(+), 27 deletions(-) diff --git a/lisp/org-macro.el b/lisp/org-macro.el index 71ac3ed9d..6cafc6bdb 100644 --- a/lisp/org-macro.el +++ b/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. diff --git a/lisp/ox.el b/lisp/ox.el index 393d970e1..90c7e7d69 100644 --- a/lisp/ox.el +++ b/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 diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el index bde9fcaba..85713c293 100644 --- a/testing/lisp/test-ox.el +++ b/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"