diff options
author | Nicolas Goaziou <n.goaziou@gmail.com> | 2012-10-30 09:24:55 +0100 |
---|---|---|
committer | Nicolas Goaziou <n.goaziou@gmail.com> | 2012-10-30 09:24:55 +0100 |
commit | 6290da183c5b01962f2f2dad453a4aef1f55b34b (patch) | |
tree | 6f5ad30a130a553576edf67f4b18bcbaa2f3e7d7 | |
parent | 1f3a2c42c6317a4b0e97b237f079c51f0028cc90 (diff) | |
download | org-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.el | 46 | ||||
-rw-r--r-- | testing/lisp/test-org.el | 8 |
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)))) |