Browse Source

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.
Nicolas Goaziou 1 year ago
parent
commit
82db669de6
2 changed files with 51 additions and 57 deletions
  1. 48 54
      lisp/org-macro.el
  2. 3 3
      testing/lisp/test-org-macro.el

+ 48 - 54
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.

+ 3 - 3
testing/lisp/test-org-macro.el

@@ -108,10 +108,10 @@
        "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n{{{property(A,*???)}}}<point>"
      (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}}}\n<point>Contents"
       (narrow-to-region (point) (point-max))