From 58b42debb18f8988cae7358642cbdc0287e3f2a5 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sat, 6 Oct 2012 10:27:57 +0200 Subject: [PATCH] org-export: Include title, author, date and email macros * contrib/lisp/org-export.el (org-export-expand-macro): New function. (org-export-as): Use new function. * lisp/org.el (org-macro-expand, org-macro-replace-all): Change signature. The function now accepts an alist of templates so it doesn't have to rely only on `org-macro-templates'. (org-macro-initialize-templates): {{{date}}} is not anymore an alias for {{{time}}}. During export, it will provide the value stored in DATE keyword instead. * testing/lisp/test-org-export.el: Add tests. * testing/lisp/test-org.el: Update tests. --- contrib/lisp/org-export.el | 75 ++++++++++++++++++++++++++--------------- lisp/org.el | 33 +++++++++++------- testing/lisp/test-org-export.el | 26 ++++++++++++++ testing/lisp/test-org.el | 8 ++--- 4 files changed, 98 insertions(+), 44 deletions(-) diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index 3ef882fe9..b1b5f9a8d 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -2498,6 +2498,8 @@ Return the updated communication channel." ;; was within an item, the item should contain the headline. That's ;; why file inclusion should be done before any structure can be ;; associated to the file, that is before parsing. +;; +;; Macro are expanded with `org-export-expand-macro'. (defun org-export-as (backend &optional subtreep visible-only body-only ext-plist noexpand) @@ -2542,34 +2544,34 @@ Return code as a string." (narrow-to-region (point) (point-max)))) ;; 1. Get export environment from original buffer. Also install ;; user's and developer's filters. - (let ((info (org-export-install-filters - (org-export-get-environment backend subtreep ext-plist))) - ;; 2. Get parse tree. Buffer isn't parsed directly. - ;; Instead, a temporary copy is created, where macros - ;; and include keywords are expanded and code blocks - ;; are evaluated. - (tree (let ((buf (or (buffer-file-name (buffer-base-buffer)) - (current-buffer)))) - (org-export-with-current-buffer-copy - (unless noexpand - (org-macro-replace-all) - (org-export-expand-include-keyword) - ;; TODO: Setting `org-current-export-file' is - ;; required by Org Babel to properly resolve - ;; noweb references. Once "org-exp.el" is - ;; removed, modify - ;; `org-export-blocks-preprocess' so it accepts - ;; the value as an argument instead. - (let ((org-current-export-file buf)) - (org-export-blocks-preprocess))) - (goto-char (point-min)) - ;; Run hook - ;; `org-export-before-parsing-hook'. with current - ;; back-end as argument. - (run-hook-with-args - 'org-export-before-parsing-hook backend) - ;; Eventually parse buffer. - (org-element-parse-buffer nil visible-only))))) + (let* ((info (org-export-install-filters + (org-export-get-environment backend subtreep ext-plist))) + ;; 2. Get parse tree. Buffer isn't parsed directly. + ;; Instead, a temporary copy is created, where macros + ;; and include keywords are expanded and code blocks + ;; are evaluated. + (tree (let ((buf (or (buffer-file-name (buffer-base-buffer)) + (current-buffer)))) + (org-export-with-current-buffer-copy + (unless noexpand + (org-export-expand-macro info) + (org-export-expand-include-keyword) + ;; TODO: Setting `org-current-export-file' is + ;; required by Org Babel to properly resolve + ;; noweb references. Once "org-exp.el" is + ;; removed, modify + ;; `org-export-blocks-preprocess' so it + ;; accepts the value as an argument instead. + (let ((org-current-export-file buf)) + (org-export-blocks-preprocess))) + (goto-char (point-min)) + ;; Run hook + ;; `org-export-before-parsing-hook'. with current + ;; back-end as argument. + (run-hook-with-args + 'org-export-before-parsing-hook backend) + ;; Eventually parse buffer. + (org-element-parse-buffer nil visible-only))))) ;; 3. Call parse-tree filters to get the final tree. (setq tree (org-export-filter-apply-functions @@ -2721,6 +2723,23 @@ Point is at buffer's beginning when BODY is applied." (progn ,@body)))))) (def-edebug-spec org-export-with-current-buffer-copy (body)) +(defun org-export-expand-macro (info) + "Expand every macro in buffer. +INFO is a plist containing export options and buffer properties." + (org-macro-replace-all + ;; Before expanding macros, install {{{author}}}, {{{date}}}, + ;; {{{email}}} and {{{title}}} templates. + (nconc + (list (cons "author" + (org-element-interpret-data (plist-get info :author))) + (cons "date" + (org-element-interpret-data (plist-get info :date))) + ;; EMAIL is not a parsed keyword: store it as-is. + (cons "email" (or (plist-get info :email) "")) + (cons "title" + (org-element-interpret-data (plist-get info :title)))) + org-macro-templates))) + (defun org-export-expand-include-keyword (&optional included dir) "Expand every include keyword in buffer. Optional argument INCLUDED is a list of included file names along diff --git a/lisp/org.el b/lisp/org.el index cdc9ff590..efbdea2eb 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -20825,10 +20825,18 @@ hierarchy of headlines by UP levels before marking the subtree." ;; Macros are expanded with `org-macro-replace-all', which relies ;; internally on `org-macro-expand'. -;; Templates for expansion are stored in the buffer-local variable -;; `org-macro-templates'. This variable is updated by +;; Default templates for expansion are stored in the buffer-local +;; variable `org-macro-templates'. This variable is updated by ;; `org-macro-initialize-templates'. +;; Along with macros defined through #+MACRO: keyword, default +;; templates include the following hard-coded macros: +;; {{{time(format-string)}}}, {{{property(node-property)}}}, +;; {{{input-file}}} and {{{modification-time(format-string)}}}. + +;; During export, {{{author}}}, {{{date}}}, {{{email}}} and +;; {{{title}}} will also be provided. + (defvar org-macro-templates nil "Alist containing all macro templates in current buffer. @@ -20840,15 +20848,15 @@ directly, use instead: #+MACRO: name template") (make-variable-buffer-local 'org-macro-templates) -(defun org-macro-expand (macro) +(defun org-macro-expand (macro templates) "Return expanded MACRO, as a string. MACRO is an object, obtained, for example, with -`org-element-context'. Return nil if no template was found." +`org-element-context'. TEMPLATES is an alist of templates used +for expansion. See `org-macro-templates' for a buffer-local +default value. Return nil if no template was found." (let ((template - (cdr (assoc-string (org-element-property :key macro) - org-macro-templates - ;; Macro names are case-insensitive. - t)))) + ;; Macro names are case-insensitive. + (cdr (assoc-string (org-element-property :key macro) templates t)))) (when template (let ((value (replace-regexp-in-string "\\$[0-9]+" @@ -20865,14 +20873,16 @@ MACRO is an object, obtained, for example, with ;; Return string. (format "%s" (or value "")))))) -(defun org-macro-replace-all () - "Replace all macros in current buffer by their expansion." +(defun org-macro-replace-all (templates) + "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." (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))) + (let ((value (org-macro-expand object templates))) (when value (delete-region (org-element-property :begin object) @@ -20916,7 +20926,6 @@ function installs the following ones: \"property\", \"date\", (mapc (lambda (cell) (funcall set-template cell)) (list (cons "property" "(eval (org-entry-get nil \"$1\" 'selective))") - (cons "date" "(eval (format-time-string \"$1\"))") (cons "time" "(eval (format-time-string \"$1\"))"))) (let ((visited-file (buffer-file-name (buffer-base-buffer)))) (when (and visited-file (file-exists-p visited-file)) diff --git a/testing/lisp/test-org-export.el b/testing/lisp/test-org-export.el index 0cba76a70..b8463fb4a 100644 --- a/testing/lisp/test-org-export.el +++ b/testing/lisp/test-org-export.el @@ -429,6 +429,32 @@ body\n"))) (should (equal (buffer-string) "#+BEGIN_SRC emacs-lisp\n(+ 2 1)\n#+END_SRC\n")))) +(ert-deftest test-org-export/expand-macro () + "Test macro expansion in an Org buffer." + ;; Standard macro expansion. + (should + (equal "#+MACRO: macro1 value\nvalue" + (org-test-with-temp-text "#+MACRO: macro1 value\n{{{macro1}}}" + (let (info) + (org-macro-initialize-templates) + (org-export-expand-macro info) (buffer-string))))) + ;; Export specific macros. + (should + (equal "me 2012-03-29 me@here Title" + (org-test-with-temp-text + " +#+TITLE: Title +#+DATE: 2012-03-29 +#+AUTHOR: me +#+EMAIL: me@here +{{{author}}} {{{date}}} {{{email}}} {{{title}}}" + (let ((info (org-export-get-environment))) + (org-macro-initialize-templates) + (org-export-expand-macro info) + (goto-char (point-max)) + (buffer-substring (line-beginning-position) + (line-end-position))))))) + (ert-deftest test-org-export/user-ignore-list () "Test if `:ignore-list' accepts user input." (org-test-with-backend test diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index a2f696bd9..cadea6123 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -140,7 +140,7 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/" "#+MACRO: A B\n1 B 3" (org-test-with-temp-text "#+MACRO: A B\n1 {{{A}}} 3" (progn (org-macro-initialize-templates) - (org-macro-replace-all) + (org-macro-replace-all org-macro-templates) (buffer-string))))) ;; Macro with arguments. (should @@ -148,7 +148,7 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/" "#+MACRO: macro $1 $2\nsome text" (org-test-with-temp-text "#+MACRO: macro $1 $2\n{{{macro(some,text)}}}" (progn (org-macro-initialize-templates) - (org-macro-replace-all) + (org-macro-replace-all org-macro-templates) (buffer-string))))) ;; Macro with "eval". (should @@ -156,7 +156,7 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/" "#+MACRO: add (eval (+ $1 $2))\n3" (org-test-with-temp-text "#+MACRO: add (eval (+ $1 $2))\n{{{add(1,2)}}}" (progn (org-macro-initialize-templates) - (org-macro-replace-all) + (org-macro-replace-all org-macro-templates) (buffer-string))))) ;; Nested macros. (should @@ -165,7 +165,7 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/" (org-test-with-temp-text "#+MACRO: in inner\n#+MACRO: out {{{in}}} outer\n{{{out}}}" (progn (org-macro-initialize-templates) - (org-macro-replace-all) + (org-macro-replace-all org-macro-templates) (buffer-string)))))) -- 2.11.4.GIT