summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2019-04-29 20:35:23 +0200
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2019-04-29 20:35:23 +0200
commit14132a356a241dbea95e4d63364bf9dbb90b669e (patch)
tree7028b81dd461cd6d436bc5c39b25830768533eec
parent42abf5c6954dee8410e33d0c5140d3b36c9d1c15 (diff)
downloadorg-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.el132
-rw-r--r--testing/lisp/test-org-macro.el13
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."