forked from mirrors/org-mode
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.
This commit is contained in:
parent
7527e3a80c
commit
df10309489
|
@ -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)))))))))
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)))
|
||||
|
|
185
lisp/ob-ref.el
185
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.
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue