From 5f5d82ed516b7b385a9258271becbfa247e94af3 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Tue, 21 Nov 2017 22:25:17 +0100 Subject: [PATCH] Remove second pass for macro expansion * lisp/org-macro.el (org-macro-initialize-templates): Initialize all macros, including {{{title}}} and al. (org-macro-replace-all): Change signature. (org-macro--find-keyword-value): New function. * lisp/ox.el (org-export-as): Remove second macro expansion --- lisp/org-macro.el | 53 ++++++++++++++++++++++++++++++++++++++++------- lisp/ox.el | 29 +++----------------------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/lisp/org-macro.el b/lisp/org-macro.el index 1d2823ea0..f78eaf006 100644 --- a/lisp/org-macro.el +++ b/lisp/org-macro.el @@ -136,6 +136,25 @@ function installs the following ones: \"property\", (let ((old-template (assoc (car cell) templates))) (if old-template (setcdr old-template (cdr cell)) (push cell templates)))))) + ;; Install "author, "date, "email", "title" and "results" macros. + (mapc update-templates + (list + (cons "author" (org-macro--find-keyword-value "AUTHOR")) + (cons "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 (org-element-type (car date)) 'timestamp)) + (format "(eval (if (org-string-nw-p \"$1\") %s %S))" + (format "(org-timestamp-format '%S \"$1\")" + (org-element-copy (car date))) + value) + value))) + (cons "email" (org-macro--find-keyword-value "EMAIL")) + (cons "results" "$1") + (cons "title" (org-macro--find-keyword-value "TITLE")))) ;; Install "property", "time" macros. (mapc update-templates (list (cons "property" @@ -156,7 +175,11 @@ function installs the following ones: \"property\", (mapc update-templates (list (cons "input-file" (file-name-nondirectory visited-file)) (cons "modification-time" - (format "(eval (format-time-string \"$1\" (or (and (org-string-nw-p \"$2\") (org-macro--vc-modified-time %s)) '%s)))" + (format "(eval +\(format-time-string \"$1\" + (or (and (org-string-nw-p \"$2\") + (org-macro--vc-modified-time %s)) + '%s)))" (prin1-to-string visited-file) (prin1-to-string (nth 5 (file-attributes visited-file))))))))) @@ -190,17 +213,17 @@ default value. Return nil if no template was found." ;; Return string. (format "%s" (or value "")))))) -(defun org-macro-replace-all (templates &optional finalize keywords) +(defun org-macro-replace-all (templates &optional keywords) "Replace all macros in current buffer by their expansion. TEMPLATES is an alist of templates used for expansion. See `org-macro-templates' for a buffer-local default value. -If optional arg FINALIZE is non-nil, raise an error if a macro is -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." +as strings, where macro expansion is allowed. + +Return an error if a macro in the buffer cannot be associated to +a definition in TEMPLATES." (org-with-wide-buffer (goto-char (point-min)) (let ((properties-regexp (format "\\`EXPORT_%s\\+?\\'" @@ -246,7 +269,7 @@ as strings, where macro expansion is allowed." ;; Leave point before replacement in case of ;; recursive expansions. (save-excursion (insert value))) - (finalize + (t (error "Undefined Org macro: %s; aborting" (org-element-property :key macro)))))))))))) @@ -294,6 +317,22 @@ Return a list of arguments, as strings. This is the opposite of ;;; Helper functions and variables for internal macros +(defun org-macro--find-keyword-value (name) + "Find value for keyword NAME in current buffer. +KEYWORD is a string. Return value associated to the keywords +named after NAME, as a string, or nil." + (org-with-point-at 1 + (let ((regexp (format "^[ \t]*#\\+%s:" (regexp-quote name))) + (case-fold-search t) + (result nil)) + (while (re-search-forward regexp nil t) + (let ((element (org-element-at-point))) + (when (eq 'keyword (org-element-type element)) + (setq result (concat result + " " + (org-element-property :value element)))))) + (and result (org-trim result))))) + (defun org-macro--vc-modified-time (file) (save-window-excursion (when (vc-backend file) diff --git a/lisp/ox.el b/lisp/ox.el index 7ab41286a..db5594fc0 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -3038,9 +3038,9 @@ Return code as a string." (org-export-expand-include-keyword) (org-export--delete-comment-trees) (org-macro-initialize-templates) - (org-macro-replace-all - (append org-macro-templates org-export-global-macros) - nil parsed-keywords) + (org-macro-replace-all (append org-macro-templates + org-export-global-macros) + parsed-keywords) ;; Refresh buffer properties and radio targets after ;; potentially invasive previous changes. Likewise, do it ;; again after executing Babel code. @@ -3082,29 +3082,6 @@ Return code as a string." (dolist (filter (plist-get info :filter-options)) (let ((result (funcall filter info backend-name))) (when result (setq info result))))) - ;; Expand export-specific set of macros: {{{author}}}, - ;; {{{date(FORMAT)}}}, {{{email}}} and {{{title}}}. It must - ;; be done once regular macros have been expanded, since - ;; parsed keywords may contain one of them. - (org-macro-replace-all - (list - (cons "author" (org-element-interpret-data (plist-get info :author))) - (cons "date" - (let* ((date (plist-get info :date)) - (value (or (org-element-interpret-data date) ""))) - (if (and (consp date) - (not (cdr date)) - (eq (org-element-type (car date)) 'timestamp)) - (format "(eval (if (org-string-nw-p \"$1\") %s %S))" - (format "(org-timestamp-format '%S \"$1\")" - (org-element-copy (car date))) - value) - value))) - (cons "email" (org-element-interpret-data (plist-get info :email))) - (cons "title" (org-element-interpret-data (plist-get info :title))) - (cons "results" "$1")) - 'finalize - parsed-keywords) ;; Parse buffer. (setq tree (org-element-parse-buffer nil visible-only)) ;; Prune tree from non-exported elements and transform