diff options
author | Nicolas Goaziou <mail@nicolasgoaziou.fr> | 2019-04-29 20:35:23 +0200 |
---|---|---|
committer | Nicolas Goaziou <mail@nicolasgoaziou.fr> | 2019-04-29 20:35:23 +0200 |
commit | 14132a356a241dbea95e4d63364bf9dbb90b669e (patch) | |
tree | 7028b81dd461cd6d436bc5c39b25830768533eec | |
parent | 42abf5c6954dee8410e33d0c5140d3b36c9d1c15 (diff) | |
download | org-mode-14132a356a241dbea95e4d63364bf9dbb90b669e.tar.gz |
Collect buffer-specific built-in macros in setup files
* lisp/org-macro.el (org-macro--set-template): New function.
(org-macro--collect-macros): Also collect "author", "email", "title"
and "date" macros.
(org-macro-initialize-templates): Do not collect previous macros here,
when it is too late.
(org-macro--find-date): New function.
* testing/lisp/test-org-macro.el (test-org/macro-replace-all): Remove
test, since we cannot guarantee anymore that user-defined macros can
take precedence over built-in ones.
Reported-by: emsenn <emsenn@emsenn.net>
<http://lists.gnu.org/r/emacs-orgmode/2019-04/msg00234.html>
-rw-r--r-- | lisp/org-macro.el | 132 | ||||
-rw-r--r-- | testing/lisp/test-org-macro.el | 13 |
2 files changed, 72 insertions, 73 deletions
diff --git a/lisp/org-macro.el b/lisp/org-macro.el index c14703c..d7ec953 100644 --- a/lisp/org-macro.el +++ b/lisp/org-macro.el @@ -83,51 +83,61 @@ directly, use instead: ;;; Functions -(defun org-macro--collect-macros () +(defun org-macro--set-template (name value templates) + "Set template for the macro NAME. +VALUE is the template of the macro. The new value override the +previous one, unless VALUE is nil. TEMPLATES is the list of +templates. Return the updated list." + (when value + (let ((old-definition (assoc name templates))) + (if old-definition + (setcdr old-definition value) + (push (cons name value) templates)))) + templates) + +(defun org-macro--collect-macros (&optional files templates) "Collect macro definitions in current buffer and setup files. -Return an alist containing all macro templates found." - (letrec ((collect-macros - (lambda (files templates) - ;; Return an alist of macro templates. FILES is a list - ;; of setup files names read so far, used to avoid - ;; circular dependencies. TEMPLATES is the alist - ;; collected so far. - (let ((case-fold-search t)) - (org-with-wide-buffer - (goto-char (point-min)) - (while (re-search-forward - "^[ \t]*#\\+\\(MACRO\\|SETUPFILE\\):" nil t) - (let ((element (org-element-at-point))) - (when (eq (org-element-type element) 'keyword) - (let ((val (org-element-property :value element))) - (if (equal (org-element-property :key element) "MACRO") - ;; Install macro in TEMPLATES. - (when (string-match - "^\\(.*?\\)\\(?:\\s-+\\(.*\\)\\)?\\s-*$" val) - (let* ((name (match-string 1 val)) - (template (or (match-string 2 val) "")) - (old-cell (assoc name templates))) - (if old-cell (setcdr old-cell template) - (push (cons name template) templates)))) - ;; Enter setup file. - (let* ((uri (org-strip-quotes (org-trim val))) - (uri-is-url (org-file-url-p uri)) - (uri (if uri-is-url - uri - (expand-file-name uri)))) - ;; Avoid circular dependencies. - (unless (member uri files) - (with-temp-buffer - (unless uri-is-url - (setq default-directory - (file-name-directory uri))) - (org-mode) - (insert (org-file-contents uri 'noerror)) - (setq templates - (funcall collect-macros (cons uri files) - templates))))))))))) - templates)))) - (funcall collect-macros nil nil))) +Return an alist containing all macro templates found. + +FILES is a list of setup files names read so far, used to avoid +circular dependencies. TEMPLATES is the alist collected so far. +The two arguments are used in recursive calls." + (let ((case-fold-search t)) + (org-with-point-at 1 + (while (re-search-forward "^[ \t]*#\\+\\(MACRO\\|SETUPFILE\\):" nil t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'keyword) + (let ((val (org-element-property :value element))) + (if (equal "MACRO" (org-element-property :key element)) + ;; Install macro in TEMPLATES. + (when (string-match "^\\(\\S-+\\)[ \t]*" val) + (let ((name (match-string 1 val)) + (value (substring val (match-end 0)))) + (setq templates + (org-macro--set-template name value templates)))) + ;; Enter setup file. + (let* ((uri (org-strip-quotes val)) + (uri-is-url (org-file-url-p uri)) + (uri (if uri-is-url + uri + (expand-file-name uri)))) + ;; Avoid circular dependencies. + (unless (member uri files) + (with-temp-buffer + (unless uri-is-url + (setq default-directory (file-name-directory uri))) + (org-mode) + (insert (org-file-contents uri 'noerror)) + (setq templates + (org-macro--collect-macros + (cons uri files) templates))))))))))) + (let ((macros `(("author" . ,(org-macro--find-keyword-value "AUTHOR")) + ("email" . ,(org-macro--find-keyword-value "EMAIL")) + ("title" . ,(org-macro--find-keyword-value "TITLE")) + ("date" . ,(org-macro--find-date))))) + (pcase-dolist (`(,name . ,value) macros) + (setq templates (org-macro--set-template name value templates)))) + templates)) (defun org-macro-initialize-templates () "Collect macro templates defined in current buffer. @@ -160,27 +170,12 @@ a file, \"input-file\" and \"modification-time\"." (prin1-to-string (file-attribute-modification-time (file-attributes visited-file)))))))) - ;; Install built-in macros. + ;; Install generic macros. (list '("n" . "(eval (org-macro--counter-increment $1 $2))") - `("author" . ,(org-macro--find-keyword-value "AUTHOR")) - `("email" . ,(org-macro--find-keyword-value "EMAIL")) '("keyword" . "(eval (org-macro--find-keyword-value $1))") '("time" . "(eval (format-time-string $1))") - `("title" . ,(org-macro--find-keyword-value "TITLE")) - '("property" . "(eval (org-macro--get-property $1 $2))") - `("date" . - ,(let* ((value (org-macro--find-keyword-value "DATE")) - (date (org-element-parse-secondary-string - value (org-element-restriction 'keyword)))) - (if (and (consp date) - (not (cdr date)) - (eq 'timestamp (org-element-type (car date)))) - (format "(eval (if (org-string-nw-p $1) %s %S))" - (format "(org-timestamp-format '%S $1)" - (org-element-copy (car date))) - value) - value))))))) + '("property" . "(eval (org-macro--get-property $1 $2))"))))) (defun org-macro-expand (macro templates) "Return expanded MACRO, as a string. @@ -347,6 +342,21 @@ named after NAME, as a string, or nil." (org-element-property :value element)))))) (and result (org-trim result))))) +(defun org-macro--find-date () + "Find value for DATE in current buffer. +Return value as a string." + (let* ((value (org-macro--find-keyword-value "DATE")) + (date (org-element-parse-secondary-string + value (org-element-restriction 'keyword)))) + (if (and (consp date) + (not (cdr date)) + (eq 'timestamp (org-element-type (car date)))) + (format "(eval (if (org-string-nw-p $1) %s %S))" + (format "(org-timestamp-format '%S $1)" + (org-element-copy (car date))) + value) + value))) + (defun org-macro--vc-modified-time (file) (save-window-excursion (when (vc-backend file) diff --git a/testing/lisp/test-org-macro.el b/testing/lisp/test-org-macro.el index 20ffb0f..f5ab633 100644 --- a/testing/lisp/test-org-macro.el +++ b/testing/lisp/test-org-macro.el @@ -103,18 +103,7 @@ "#+MACRO: macro expansion\n* COMMENT H1\n** H2\n<point>{{{macro}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) - (org-with-wide-buffer (buffer-string))))) - ;; User-defined macros take precedence over built-in macros. - (should - (equal - "foo" - (org-test-with-temp-text - "#+MACRO: title foo\n#+TITLE: bar\n<point>{{{title}}}" - (org-macro-initialize-templates) - (org-macro-replace-all org-macro-templates) - (goto-char (point-max)) - (buffer-substring-no-properties (line-beginning-position) - (line-end-position)))))) + (org-with-wide-buffer (buffer-string)))))) (ert-deftest test-org-macro/property () "Test {{{property}}} macro." |