From 82db669de6a4b7cd1f39bbf692a42c32a96ebb31 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Mon, 5 Jun 2017 17:05:58 +0200 Subject: [PATCH] org-macro: Expand macros only within narrowed part of buffer * lisp/org-macro.el (org-macro-replace-all): Expand macros only within narrowed part of buffer. * testing/lisp/test-org-macro.el (test-org/macro-replace-all): Update test. Expanding macros outside in the whole buffer could make sense, e.g., if a macro expands to some Babel code, which, in turn, is evaluated prior to export. However, by principle of least surprise, it is better to limit expansion to current accessible part of the buffer. --- lisp/org-macro.el | 102 ++++++++++++++++----------------- testing/lisp/test-org-macro.el | 6 +- 2 files changed, 51 insertions(+), 57 deletions(-) diff --git a/lisp/org-macro.el b/lisp/org-macro.el index 71e917b71..8927b3a80 100644 --- a/lisp/org-macro.el +++ b/lisp/org-macro.el @@ -49,11 +49,7 @@ (declare-function org-element-at-point "org-element" ()) (declare-function org-element-context "org-element" (&optional element)) -(declare-function org-element-map "org-element" - (data types fun &optional info first-match no-recursion - with-affiliated)) -(declare-function org-element-parse-buffer "org-element" - (&optional granularity visible-only)) +(declare-function org-element-macro-parser "org-element" ()) (declare-function org-element-property "org-element" (property element)) (declare-function org-element-type "org-element" (element)) (declare-function org-file-contents "org" (file &optional noerror)) @@ -189,55 +185,53 @@ found in the buffer with no definition in TEMPLATES. Optional argument KEYWORDS, when non-nil is a list of keywords, as strings, where macro expansion is allowed." - (org-with-wide-buffer - (goto-char (point-min)) - (let ((properties-regexp - (format "\\`EXPORT_%s\\+?\\'" (regexp-opt keywords))) - record) - (while (re-search-forward "{{{[-A-Za-z0-9_]" nil t) - (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) - (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 - 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. - (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))))))))))) + (save-excursion + (goto-char (point-min)) + (let ((properties-regexp + (format "\\`EXPORT_%s\\+?\\'" (regexp-opt keywords))) + record) + (while (re-search-forward "{{{[-A-Za-z0-9_]" nil t) + (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) + (string-match-p properties-regexp + (org-element-property :key datum)))) + (save-excursion + (goto-char (match-beginning 0)) + (org-element-macro-parser)))))) + (when macro + (let* ((value (org-macro-expand macro templates)) + (begin (org-element-property :begin macro)) + (signature (list begin + 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. + (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/testing/lisp/test-org-macro.el b/testing/lisp/test-org-macro.el index 26c56745c..bc493b3d1 100644 --- a/testing/lisp/test-org-macro.el +++ b/testing/lisp/test-org-macro.el @@ -108,10 +108,10 @@ "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n{{{property(A,*???)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates))) - ;; Macro expansion ignores narrowing. + ;; Macro expansion preserves narrowing. (should - (string-match - "expansion" + (string-match-p + "{{{macro}}}" (org-test-with-temp-text "#+MACRO: macro expansion\n{{{macro}}}\nContents" (narrow-to-region (point) (point-max))