From 4b428820432752117c60b79da0a79fd4e50e4ba1 Mon Sep 17 00:00:00 2001 From: Aaron Ecay Date: Tue, 22 Apr 2014 15:13:48 -0400 Subject: [PATCH] ob-core.el: allow the auto-generation of output file names for src blocks. * lisp/ob-core.el (org-babel-generate-file-param): New function. (org-babel-get-src-block-info): Use it. * testing/lisp/test-ob.el (test-org-babel/file-ext-and-output-dir): New test. * doc/org.texi (Specific header arguments): Add doc for :file-ext and :output-dir header args. --- doc/org.texi | 27 +++++++++++++++++++++++++++ lisp/ob-core.el | 34 ++++++++++++++++++++++++++++++++++ testing/examples/babel.org | 34 ++++++++++++++++++++++++++++++++++ testing/lisp/test-ob.el | 14 ++++++++++++++ 4 files changed, 109 insertions(+) diff --git a/doc/org.texi b/doc/org.texi index 2546be1..79cc044 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -14406,6 +14406,8 @@ argument in lowercase letters. The following header arguments are defined: be collected and handled * file:: Specify a path for file output * file-desc:: Specify a description for file results +* file-ext:: Specify an extension for file output +* output-dir:: Specify a directory to write file output to * dir:: Specify the default (possibly remote) directory for code block execution * exports:: Export code and/or results @@ -14840,6 +14842,31 @@ description for file code block results which are inserted as Org mode links with no value the link path will be placed in both the ``link'' and the ``description'' portion of the Org mode link. +@node file-ext +@subsubsection @code{:file-ext} +@cindex @code{:file-ext}, src header argument + +The value of the @code{:file-ext} header argument is used to provide an +extension to write the file output to. It is combined with the +@code{#+NAME:} of the source block and the value of the @ref{output-dir} +header argument to generate a complete file name. + +This header arg will be overridden by @code{:file}, and thus has no effect +when the latter is specified. + +@node output-dir +@subsubsection @code{:output-dir} +@cindex @code{:output-dir}, src header argument + +The value of the @code{:output-dir} header argument is used to provide a +directory to write the file output to. It may specify an absolute directory +(beginning with @code{/}) or a relative directory (without @code{/}). It is +combined with the @code{#+NAME:} of the source block and the value of the +@ref{output-dir} header argument to generate a complete file name. + +This header arg will be overridden by @code{:file}, and thus has no effect +when the latter is specified. + @node dir @subsubsection @code{:dir} and remote execution @cindex @code{:dir}, src header argument diff --git a/lisp/ob-core.el b/lisp/ob-core.el index 1348f04..4a3683d 100644 --- a/lisp/ob-core.el +++ b/lisp/ob-core.el @@ -283,6 +283,8 @@ Returns a list ;; resolve variable references and add summary parameters (when (and info (not light)) (setf (nth 2 info) (org-babel-process-params (nth 2 info)))) + (when info + (setf (nth 2 info) (org-babel-generate-file-param name (nth 2 info)))) (when info (append info (list name indent head))))) (defvar org-babel-exp-reference-buffer nil @@ -2890,6 +2892,38 @@ For the format of SAFE-LIST, see `org-babel-safe-header-args'." (member (cdr pair) (cdr entry))) (t nil))))))) +(defun org-babel-generate-file-param (src-name params) + "Calculate the filename for source block results. + +The directory is calculated from the :output-dir property of the +source block; if not specified, use the current directory. + +If the source block has a #+NAME and the :file parameter does not +contain any period characters, then the :file parameter is +treated as an extension, and the output file name is the +concatenation of the directory (as calculated above), the block +name, a period, and the parameter value as a file extension. +Otherwise, the :file parameter is treated as a full file name, +and the output file name is the directory (as calculated above) +plus the parameter value." + (let* ((file-cons (assq :file params)) + (file-ext-cons (assq :file-ext params)) + (file-ext (cdr-safe file-ext-cons)) + (dir (or (cdr-safe (assq :output-dir params)) ""))) + ;; Only operate if: + ;; 1. :file is not given + ;; 2. :file-ext is given + ;; 3. the source block has a #+name + (if (and (not file-cons) file-ext src-name) + (progn + ;; Create the :output-dir if it does not exist + (when (and file-ext (not (string= dir ""))) + (make-directory dir t) + (unless (string-match "/\\'" dir) + (setq dir (concat dir "/")))) + (cons (cons :file (concat dir src-name "." file-ext)) params)) + ;; Otherwise return params unmodified + params))) ;;; Used by backends: R, Maxima, Octave. (defun org-babel-graphical-output-file (params) diff --git a/testing/examples/babel.org b/testing/examples/babel.org index 449824f..68ba65a 100644 --- a/testing/examples/babel.org +++ b/testing/examples/babel.org @@ -458,3 +458,37 @@ function definition "0")))) (format "elisp a:%d, b:%d, c:%d, d:%d, e:%d" a b c d e) #+END_SRC + +* =:file-ext= and =:output-dir= header args + :PROPERTIES: + :ID: 93573e1d-6486-442e-b6d0-3fedbdc37c9b + :END: +#+name: file-ext-basic +#+BEGIN_SRC emacs-lisp :file-ext txt +nil +#+END_SRC + +#+name: file-ext-dir-relative +#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir foo +nil +#+END_SRC + +#+name: file-ext-dir-relative-slash +#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir foo/ +nil +#+END_SRC + +#+name: file-ext-dir-absolute +#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir /tmp +nil +#+END_SRC + +#+name: file-ext-file-wins +#+BEGIN_SRC emacs-lisp :file-ext txt :file foo.bar +nil +#+END_SRC + +#+name: file-ext-file-wins2 +#+BEGIN_SRC emacs-lisp :output-dir xxx :file foo.bar +nil +#+END_SRC diff --git a/testing/lisp/test-ob.el b/testing/lisp/test-ob.el index c0ca493..fb791d6 100644 --- a/testing/lisp/test-ob.el +++ b/testing/lisp/test-ob.el @@ -1237,6 +1237,20 @@ echo \"$data\" (org-babel-execute-src-block))) (should (= noweb-expansions-in-cache-var 2))))) +(ert-deftest test-org-babel/file-ext-and-output-dir () + (org-test-at-id "93573e1d-6486-442e-b6d0-3fedbdc37c9b" + (macrolet ((at-next (&rest body) + `(progn + (org-babel-next-src-block) + (save-match-data ,@body)))) + (at-next (should (equal "file-ext-basic.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t))))))) + (at-next (should (equal "foo/file-ext-dir-relative.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t))))))) + (at-next (should (equal "foo/file-ext-dir-relative-slash.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t))))))) + (at-next (should (equal "/tmp/file-ext-dir-absolute.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t))))))) + (at-next (should (equal "foo.bar" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t))))))) + (at-next (should (equal "foo.bar" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t))))))) + ))) + (provide 'test-ob) ;;; test-ob ends here -- 1.9.2