summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <n.goaziou@gmail.com>2012-10-30 09:24:55 +0100
committerNicolas Goaziou <n.goaziou@gmail.com>2012-10-30 09:24:55 +0100
commit6290da183c5b01962f2f2dad453a4aef1f55b34b (patch)
tree6f5ad30a130a553576edf67f4b18bcbaa2f3e7d7
parent1f3a2c42c6317a4b0e97b237f079c51f0028cc90 (diff)
downloadorg-mode-6290da183c5b01962f2f2dad453a4aef1f55b34b.tar.gz
Signal an error when a circular macro expansion happens
* lisp/org.el (org-macro-replace-all): Signal an error when a circular macro expansion happens. (org-macro-initialize-templates): Fix docstring. * testing/lisp/test-org.el: Add test.
-rw-r--r--lisp/org.el46
-rw-r--r--testing/lisp/test-org.el8
2 files changed, 36 insertions, 18 deletions
diff --git a/lisp/org.el b/lisp/org.el
index 63c4323..9ba39a7 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20927,28 +20927,40 @@ TEMPLATES is an alist of templates used for expansion. See
`org-macro-templates' for a buffer-local default value."
(save-excursion
(goto-char (point-min))
- (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)))
- (when value
- (delete-region
- (org-element-property :begin object)
- ;; 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)))))))))
+ (let (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))
+ (signature (list begin
+ object
+ (org-element-property :args object))))
+ ;; 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))
+ (when 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)))))))))))
(defun org-macro-initialize-templates ()
"Collect macro templates defined in current buffer.
Templates are stored in buffer-local variable
`org-macro-templates'. In addition to buffer-defined macros, the
-function installs the following ones: \"property\", \"date\",
-\"time\". and, if appropriate, \"input-file\" and
-\"modification-time\"."
+function installs the following ones: \"property\",
+\"time\". and, if the buffer is associated to a file,
+\"input-file\" and \"modification-time\"."
(let ((case-fold-search t)
(set-template
(lambda (cell)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 58a8b30..b0a073a 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -410,7 +410,13 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/"
"#+MACRO: in inner\n#+MACRO: out {{{in}}} outer\n{{{out}}}"
(progn (org-macro-initialize-templates)
(org-macro-replace-all org-macro-templates)
- (buffer-string))))))
+ (buffer-string)))))
+ ;; Error out when macro expansion is circular.
+ (should-error
+ (org-test-with-temp-text
+ "#+MACRO: mac1 {{{mac2}}}\n#+MACRO: mac2 {{{mac1}}}\n{{{mac1}}}"
+ (org-macro-initialize-templates)
+ (org-macro-replace-all org-macro-templates))))