summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <n.goaziou@gmail.com>2014-03-14 15:27:32 +0100
committerNicolas Goaziou <n.goaziou@gmail.com>2014-03-19 21:34:56 +0100
commitdf10309489a6f6e1a001866be42480ba8cf9f5af (patch)
treed5112e6d295941c3b566446172325b225e667bc1
parent7527e3a80c157d59cf456153810dc20d16642317 (diff)
downloadorg-mode-df10309489a6f6e1a001866be42480ba8cf9f5af.tar.gz
ob-ref: Properly resolve references in ":post" arguments
* lisp/ob-core.el (org-babel-exp-reference-buffer): New variable, as a replacement for `org-current-export-file'. (org-babel-check-confirm-evaluate): Use new variable. * lisp/ob-exp.el (org-babel-exp-in-export-file): Use new variable. (org-babel-exp-get-export-buffer): Remove function. (org-babel-exp-process-buffer): Change signature. * lisp/ob-ref.el (org-babel-ref-resolve): Use new variable during export in order to properly resolve references. * lisp/ox.el (org-export-execute-babel-code): Use new variable. * contrib/lisp/org-wikinodes.el (org-wikinodes-process-links-for-export): Remove a cond branch as it is always false (`org-current-export-file' couldn't be a string). * testing/lisp/test-ob-lob.el (test-ob-lob/export-lob-lines): Update test. * testing/lisp/test-ob.el (test-ob/eval-header-argument): Update test. * testing/lisp/test-ob-exp.el (ob-export/reference-in-post-header): New test. During export, Babel executes sequentially all blocks in the buffer being exported. This can lead to modifications preventing some references from being resolved. As a workaround, Babel stores a pristine copy of the buffer in a variable so it can always find needed references. Before this patch, the variable storing this copy was `org-current-export-file' and was dynamically bound in "ox.el". It was used to resolve noweb references (`org-babel-expand-noweb-references') but not regular references (`org-babel-ref-resolve'). Now, the variable is `org-babel-exp-reference-buffer' and it is bound from `org-babel-exp-process-buffer'. It is used to resolve all references. In particular, this allows to use references in :post header. Thanks to Jarmo Hurri for reporting it.
-rw-r--r--contrib/lisp/org-wikinodes.el7
-rw-r--r--lisp/ob-core.el8
-rw-r--r--lisp/ob-exp.el37
-rw-r--r--lisp/ob-ref.el185
-rw-r--r--lisp/ox.el4
-rw-r--r--testing/lisp/test-ob-exp.el17
-rw-r--r--testing/lisp/test-ob-lob.el3
-rw-r--r--testing/lisp/test-ob.el2
8 files changed, 134 insertions, 129 deletions
diff --git a/contrib/lisp/org-wikinodes.el b/contrib/lisp/org-wikinodes.el
index 7d136c2..7f3e2e3 100644
--- a/contrib/lisp/org-wikinodes.el
+++ b/contrib/lisp/org-wikinodes.el
@@ -268,7 +268,6 @@ If there is no such wiki target, return nil."
(car org-export-target-aliases))))
(push (caar target-alist) (cdr a)))))
-(defvar org-current-export-file)
(defun org-wikinodes-process-links-for-export ()
"Process Wiki links in the export preprocess buffer.
@@ -294,12 +293,6 @@ with working links."
((eq org-wikinodes-scope 'file)
;; No match in file, and other files are not allowed
(insert (format "%s" link)))
- ((setq file
- (and (org-string-nw-p org-current-export-file)
- (org-wikinodes-which-file
- link (file-name-directory org-current-export-file))))
- ;; Match in another file in the current directory
- (insert (format "[[file:%s::%s][%s]]" file link link)))
(t ;; No match for this link
(insert (format "%s" link)))))))))
diff --git a/lisp/ob-core.el b/lisp/ob-core.el
index 8c55a18..0adfc33 100644
--- a/lisp/ob-core.el
+++ b/lisp/ob-core.el
@@ -285,7 +285,11 @@ Returns a list
(setf (nth 2 info) (org-babel-process-params (nth 2 info))))
(when info (append info (list name indent head)))))
-(defvar org-current-export-file) ; dynamically bound
+(defvar org-babel-exp-reference-buffer nil
+ "Buffer containing original contents of the exported buffer.
+This is used by Babel to resolve references in source blocks.
+Its value is dynamically bound during export.")
+
(defmacro org-babel-check-confirm-evaluate (info &rest body)
"Evaluate BODY with special execution confirmation variables set.
@@ -305,7 +309,7 @@ name of the code block."
(when (assoc :noeval ,headers) "no")))
(,eval-no (or (equal ,eval "no")
(equal ,eval "never")))
- (,export (org-bound-and-true-p org-current-export-file))
+ (,export org-babel-exp-reference-buffer)
(,eval-no-export (and ,export (or (equal ,eval "no-export")
(equal ,eval "never-export"))))
(noeval (or ,eval-no ,eval-no-export))
diff --git a/lisp/ob-exp.el b/lisp/ob-exp.el
index 38618ee..0ddb4dc 100644
--- a/lisp/ob-exp.el
+++ b/lisp/ob-exp.el
@@ -27,7 +27,6 @@
(eval-when-compile
(require 'cl))
-(defvar org-current-export-file)
(defvar org-babel-lob-one-liner-regexp)
(defvar org-babel-ref-split-regexp)
(defvar org-list-forbidden-blocks)
@@ -61,27 +60,18 @@ be executed."
(const :tag "Always" t)))
(put 'org-export-babel-evaluate 'safe-local-variable (lambda (x) (eq x nil)))
-(defun org-babel-exp-get-export-buffer ()
- "Return the current export buffer if possible."
- (cond
- ((bufferp org-current-export-file) org-current-export-file)
- (org-current-export-file (get-file-buffer org-current-export-file))
- ('otherwise
- (error "Requested export buffer when `org-current-export-file' is nil"))))
-
(defvar org-link-search-inhibit-query)
-
(defmacro org-babel-exp-in-export-file (lang &rest body)
(declare (indent 1))
`(let* ((lang-headers (intern (concat "org-babel-default-header-args:" ,lang)))
(heading (nth 4 (ignore-errors (org-heading-components))))
(export-buffer (current-buffer))
- (original-buffer (org-babel-exp-get-export-buffer)) results)
- (when original-buffer
- ;; resolve parameters in the original file so that
- ;; headline and file-wide parameters are included, attempt
- ;; to go to the same heading in the original file
- (set-buffer original-buffer)
+ results)
+ (when org-babel-exp-reference-buffer
+ ;; Resolve parameters in the original file so that headline and
+ ;; file-wide parameters are included, attempt to go to the same
+ ;; heading in the original file
+ (set-buffer org-babel-exp-reference-buffer)
(save-restriction
(when heading
(condition-case nil
@@ -152,12 +142,17 @@ this template."
:type 'string)
(defvar org-babel-default-lob-header-args)
-(defun org-babel-exp-process-buffer ()
- "Execute all Babel blocks in current buffer."
+(defun org-babel-exp-process-buffer (reference-buffer)
+ "Execute all Babel blocks in current buffer.
+REFERENCE-BUFFER is the buffer containing a pristine copy of the
+buffer being processed. It is used to properly resolve
+references in source blocks, as modifications in current buffer
+may make them unreachable."
(interactive)
(save-window-excursion
(save-excursion
(let ((case-fold-search t)
+ (org-babel-exp-reference-buffer reference-buffer)
(regexp (concat org-babel-inline-src-block-regexp "\\|"
org-babel-lob-one-liner-regexp "\\|"
"^[ \t]*#\\+BEGIN_SRC")))
@@ -186,7 +181,7 @@ this template."
(if (and (cdr (assoc :noweb params))
(string= "yes" (cdr (assoc :noweb params))))
(org-babel-expand-noweb-references
- info (org-babel-exp-get-export-buffer))
+ info org-babel-exp-reference-buffer)
(nth 1 info)))
(goto-char begin)
(let ((replacement (org-babel-exp-do-export info 'inline)))
@@ -342,7 +337,7 @@ replaced with its value."
(org-babel-noweb-wrap) "" (nth 1 info))
(if (org-babel-noweb-p (nth 2 info) :export)
(org-babel-expand-noweb-references
- info (org-babel-exp-get-export-buffer))
+ info org-babel-exp-reference-buffer)
(nth 1 info))))
(org-fill-template
org-babel-exp-code-template
@@ -371,7 +366,7 @@ inhibit insertion of results into the buffer."
(let ((lang (nth 0 info))
(body (if (org-babel-noweb-p (nth 2 info) :eval)
(org-babel-expand-noweb-references
- info (org-babel-exp-get-export-buffer))
+ info org-babel-exp-reference-buffer)
(nth 1 info)))
(info (copy-sequence info))
(org-babel-current-src-block-location (point-marker)))
diff --git a/lisp/ob-ref.el b/lisp/ob-ref.el
index f50ca72..6172895 100644
--- a/lisp/ob-ref.el
+++ b/lisp/ob-ref.el
@@ -129,98 +129,99 @@ the variable."
(defun org-babel-ref-resolve (ref)
"Resolve the reference REF and return its value."
(save-window-excursion
- (save-excursion
- (let ((case-fold-search t)
- type args new-refere new-header-args new-referent result
- lob-info split-file split-ref index index-row index-col id)
- ;; if ref is indexed grab the indices -- beware nested indices
- (when (and (string-match "\\[\\([^\\[]+\\)\\]$" ref)
- (let ((str (substring ref 0 (match-beginning 0))))
- (= (org-count ?( str) (org-count ?) str))))
- (setq index (match-string 1 ref))
- (setq ref (substring ref 0 (match-beginning 0))))
- ;; assign any arguments to pass to source block
- (when (string-match
- "^\\(.+?\\)\\(\\[\\(.*\\)\\]\\|\\(\\)\\)\(\\(.*\\)\)$" ref)
- (setq new-refere (match-string 1 ref))
- (setq new-header-args (match-string 3 ref))
- (setq new-referent (match-string 5 ref))
- (when (> (length new-refere) 0)
- (when (> (length new-referent) 0)
- (setq args (mapcar (lambda (ref) (cons :var ref))
- (org-babel-ref-split-args new-referent))))
- (when (> (length new-header-args) 0)
- (setq args (append (org-babel-parse-header-arguments
- new-header-args) args)))
- (setq ref new-refere)))
- (when (string-match "^\\(.+\\):\\(.+\\)$" ref)
- (setq split-file (match-string 1 ref))
- (setq split-ref (match-string 2 ref))
- (find-file split-file) (setq ref split-ref))
- (save-restriction
- (widen)
- (goto-char (point-min))
- (if (let ((src-rx (org-babel-named-src-block-regexp-for-name ref))
- (res-rx (org-babel-named-data-regexp-for-name ref)))
- ;; goto ref in the current buffer
- (or
- ;; check for code blocks
- (re-search-forward src-rx nil t)
- ;; check for named data
- (re-search-forward res-rx nil t)
- ;; check for local or global headlines by id
- (setq id (org-babel-ref-goto-headline-id ref))
- ;; check the Library of Babel
- (setq lob-info (cdr (assoc (intern ref)
- org-babel-library-of-babel)))))
- (unless (or lob-info id) (goto-char (match-beginning 0)))
- ;; ;; TODO: allow searching for names in other buffers
- ;; (setq id-loc (org-id-find ref 'marker)
- ;; buffer (marker-buffer id-loc)
- ;; loc (marker-position id-loc))
- ;; (move-marker id-loc nil)
- (error "Reference '%s' not found in this buffer" ref))
- (cond
- (lob-info (setq type 'lob))
- (id (setq type 'id))
- ((and (looking-at org-babel-src-name-regexp)
- (save-excursion
- (forward-line 1)
- (or (looking-at org-babel-src-block-regexp)
- (looking-at org-babel-multi-line-header-regexp))))
- (setq type 'source-block))
- ((and (looking-at org-babel-src-name-regexp)
- (save-excursion
- (forward-line 1)
- (looking-at org-babel-lob-one-liner-regexp)))
- (setq type 'call-line))
- (t (while (not (setq type (org-babel-ref-at-ref-p)))
- (forward-line 1)
- (beginning-of-line)
- (if (or (= (point) (point-min)) (= (point) (point-max)))
- (error "Reference not found")))))
- (let ((params (append args '((:results . "silent")))))
- (setq result
- (case type
- (results-line (org-babel-read-result))
- (table (org-babel-read-table))
- (list (org-babel-read-list))
- (file (org-babel-read-link))
- (source-block (org-babel-execute-src-block
- nil nil (if org-babel-update-intermediate
- nil params)))
- (call-line (save-excursion
- (forward-line 1)
- (org-babel-lob-execute
- (org-babel-lob-get-info))))
- (lob (org-babel-execute-src-block
- nil lob-info params))
- (id (org-babel-ref-headline-body)))))
- (if (symbolp result)
- (format "%S" result)
- (if (and index (listp result))
- (org-babel-ref-index-list index result)
- result)))))))
+ (with-current-buffer (or org-babel-exp-reference-buffer (current-buffer))
+ (save-excursion
+ (let ((case-fold-search t)
+ type args new-refere new-header-args new-referent result
+ lob-info split-file split-ref index index-row index-col id)
+ ;; if ref is indexed grab the indices -- beware nested indices
+ (when (and (string-match "\\[\\([^\\[]+\\)\\]$" ref)
+ (let ((str (substring ref 0 (match-beginning 0))))
+ (= (org-count ?( str) (org-count ?) str))))
+ (setq index (match-string 1 ref))
+ (setq ref (substring ref 0 (match-beginning 0))))
+ ;; assign any arguments to pass to source block
+ (when (string-match
+ "^\\(.+?\\)\\(\\[\\(.*\\)\\]\\|\\(\\)\\)\(\\(.*\\)\)$" ref)
+ (setq new-refere (match-string 1 ref))
+ (setq new-header-args (match-string 3 ref))
+ (setq new-referent (match-string 5 ref))
+ (when (> (length new-refere) 0)
+ (when (> (length new-referent) 0)
+ (setq args (mapcar (lambda (ref) (cons :var ref))
+ (org-babel-ref-split-args new-referent))))
+ (when (> (length new-header-args) 0)
+ (setq args (append (org-babel-parse-header-arguments
+ new-header-args) args)))
+ (setq ref new-refere)))
+ (when (string-match "^\\(.+\\):\\(.+\\)$" ref)
+ (setq split-file (match-string 1 ref))
+ (setq split-ref (match-string 2 ref))
+ (find-file split-file) (setq ref split-ref))
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (if (let ((src-rx (org-babel-named-src-block-regexp-for-name ref))
+ (res-rx (org-babel-named-data-regexp-for-name ref)))
+ ;; goto ref in the current buffer
+ (or
+ ;; check for code blocks
+ (re-search-forward src-rx nil t)
+ ;; check for named data
+ (re-search-forward res-rx nil t)
+ ;; check for local or global headlines by id
+ (setq id (org-babel-ref-goto-headline-id ref))
+ ;; check the Library of Babel
+ (setq lob-info (cdr (assoc (intern ref)
+ org-babel-library-of-babel)))))
+ (unless (or lob-info id) (goto-char (match-beginning 0)))
+ ;; ;; TODO: allow searching for names in other buffers
+ ;; (setq id-loc (org-id-find ref 'marker)
+ ;; buffer (marker-buffer id-loc)
+ ;; loc (marker-position id-loc))
+ ;; (move-marker id-loc nil)
+ (error "Reference '%s' not found in this buffer" ref))
+ (cond
+ (lob-info (setq type 'lob))
+ (id (setq type 'id))
+ ((and (looking-at org-babel-src-name-regexp)
+ (save-excursion
+ (forward-line 1)
+ (or (looking-at org-babel-src-block-regexp)
+ (looking-at org-babel-multi-line-header-regexp))))
+ (setq type 'source-block))
+ ((and (looking-at org-babel-src-name-regexp)
+ (save-excursion
+ (forward-line 1)
+ (looking-at org-babel-lob-one-liner-regexp)))
+ (setq type 'call-line))
+ (t (while (not (setq type (org-babel-ref-at-ref-p)))
+ (forward-line 1)
+ (beginning-of-line)
+ (if (or (= (point) (point-min)) (= (point) (point-max)))
+ (error "Reference not found")))))
+ (let ((params (append args '((:results . "silent")))))
+ (setq result
+ (case type
+ (results-line (org-babel-read-result))
+ (table (org-babel-read-table))
+ (list (org-babel-read-list))
+ (file (org-babel-read-link))
+ (source-block (org-babel-execute-src-block
+ nil nil (if org-babel-update-intermediate
+ nil params)))
+ (call-line (save-excursion
+ (forward-line 1)
+ (org-babel-lob-execute
+ (org-babel-lob-get-info))))
+ (lob (org-babel-execute-src-block
+ nil lob-info params))
+ (id (org-babel-ref-headline-body)))))
+ (if (symbolp result)
+ (format "%S" result)
+ (if (and index (listp result))
+ (org-babel-ref-index-list index result)
+ result))))))))
(defun org-babel-ref-index-list (index lis)
"Return the subset of LIS indexed by INDEX.
diff --git a/lisp/ox.el b/lisp/ox.el
index c437038..9f77af4 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -318,6 +318,7 @@ there is no export process in progress.
It can be used to teach Babel blocks how to act differently
according to the back-end used.")
+
;;; User-configurable Variables
;;
@@ -3434,8 +3435,7 @@ file should have."
;; Get a pristine copy of current buffer so Babel references can be
;; properly resolved.
(let ((reference (org-export-copy-buffer)))
- (unwind-protect (let ((org-current-export-file reference))
- (org-babel-exp-process-buffer))
+ (unwind-protect (org-babel-exp-process-buffer reference)
(kill-buffer reference))))
(defun org-export--copy-to-kill-ring-p ()
diff --git a/testing/lisp/test-ob-exp.el b/testing/lisp/test-ob-exp.el
index d5db187..1fe810b 100644
--- a/testing/lisp/test-ob-exp.el
+++ b/testing/lisp/test-ob-exp.el
@@ -32,8 +32,7 @@ Current buffer is a copy of the original buffer."
(with-temp-buffer
(org-mode)
(insert string)
- (let ((org-current-export-file buf))
- (org-babel-exp-process-buffer))
+ (org-babel-exp-process-buffer buf)
(goto-char (point-min))
(progn ,@body))))
@@ -405,6 +404,20 @@ src_emacs-lisp{(+ 1 1)}"
(org-export-execute-babel-code)
(buffer-string)))))
+(ert-deftest ob-export/reference-in-post-header ()
+ "Test references in :post header during export."
+ (should
+ (org-test-with-temp-text "
+#+NAME: foo
+#+BEGIN_SRC emacs-lisp :exports none :var bar=\"baz\"
+ (concat \"bar\" bar)
+#+END_SRC
+
+#+NAME: nofun
+#+BEGIN_SRC emacs-lisp :exports results :post foo(\"nofun\")
+#+END_SRC"
+ (org-export-execute-babel-code) t)))
+
(provide 'test-ob-exp)
diff --git a/testing/lisp/test-ob-lob.el b/testing/lisp/test-ob-lob.el
index b0d079a..b85022b 100644
--- a/testing/lisp/test-ob-lob.el
+++ b/testing/lisp/test-ob-lob.el
@@ -83,8 +83,7 @@
(with-temp-buffer
(org-mode)
(insert string)
- (let ((org-current-export-file buf))
- (org-babel-exp-process-buffer))
+ (org-babel-exp-process-buffer buf)
(message (buffer-string))
(goto-char (point-min))
(should (re-search-forward "^: 0" nil t))
diff --git a/testing/lisp/test-ob.el b/testing/lisp/test-ob.el
index d7ef41a..c0ca493 100644
--- a/testing/lisp/test-ob.el
+++ b/testing/lisp/test-ob.el
@@ -645,7 +645,7 @@ on two lines
(check-eval "no" nil)
(check-eval "never-export" t)
(check-eval "no-export" t)
- (let ((org-current-export-file "something"))
+ (let ((org-babel-exp-reference-buffer (current-buffer)))
(check-eval "never" nil)
(check-eval "no" nil)
(check-eval "never-export" nil)