Jelajahi Sumber

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.
Nicolas Goaziou 11 tahun lalu
induk
melakukan
df10309489

+ 0 - 7
contrib/lisp/org-wikinodes.el

@@ -268,7 +268,6 @@ If there is no such wiki target, return nil."
 		    (car org-export-target-aliases))))
 		    (car org-export-target-aliases))))
       (push (caar target-alist) (cdr a)))))
       (push (caar target-alist) (cdr a)))))
 
 
-(defvar org-current-export-file)
 (defun org-wikinodes-process-links-for-export ()
 (defun org-wikinodes-process-links-for-export ()
   "Process Wiki links in the export preprocess buffer.
   "Process Wiki links in the export preprocess buffer.
 
 
@@ -294,12 +293,6 @@ with working links."
 	   ((eq org-wikinodes-scope 'file)
 	   ((eq org-wikinodes-scope 'file)
 	    ;; No match in file, and other files are not allowed
 	    ;; No match in file, and other files are not allowed
 	    (insert (format "%s" link)))
 	    (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
 	   (t ;; No match for this link
 	    (insert (format "%s" link)))))))))
 	    (insert (format "%s" link)))))))))
 
 

+ 6 - 2
lisp/ob-core.el

@@ -285,7 +285,11 @@ Returns a list
       (setf (nth 2 info) (org-babel-process-params (nth 2 info))))
       (setf (nth 2 info) (org-babel-process-params (nth 2 info))))
     (when info (append info (list name indent head)))))
     (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)
 (defmacro org-babel-check-confirm-evaluate (info &rest body)
   "Evaluate BODY with special execution confirmation variables set.
   "Evaluate BODY with special execution confirmation variables set.
 
 
@@ -305,7 +309,7 @@ name of the code block."
 				 (when (assoc :noeval ,headers) "no")))
 				 (when (assoc :noeval ,headers) "no")))
 	    (,eval-no        (or (equal ,eval "no")
 	    (,eval-no        (or (equal ,eval "no")
 				 (equal ,eval "never")))
 				 (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")
 	    (,eval-no-export (and ,export (or (equal ,eval "no-export")
 					      (equal ,eval "never-export"))))
 					      (equal ,eval "never-export"))))
 	    (noeval          (or ,eval-no ,eval-no-export))
 	    (noeval          (or ,eval-no ,eval-no-export))

+ 16 - 21
lisp/ob-exp.el

@@ -27,7 +27,6 @@
 (eval-when-compile
 (eval-when-compile
   (require 'cl))
   (require 'cl))
 
 
-(defvar org-current-export-file)
 (defvar org-babel-lob-one-liner-regexp)
 (defvar org-babel-lob-one-liner-regexp)
 (defvar org-babel-ref-split-regexp)
 (defvar org-babel-ref-split-regexp)
 (defvar org-list-forbidden-blocks)
 (defvar org-list-forbidden-blocks)
@@ -61,27 +60,18 @@ be executed."
 		 (const :tag "Always" t)))
 		 (const :tag "Always" t)))
 (put 'org-export-babel-evaluate 'safe-local-variable (lambda (x) (eq x nil)))
 (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)
 (defvar org-link-search-inhibit-query)
-
 (defmacro org-babel-exp-in-export-file (lang &rest body)
 (defmacro org-babel-exp-in-export-file (lang &rest body)
   (declare (indent 1))
   (declare (indent 1))
   `(let* ((lang-headers (intern (concat "org-babel-default-header-args:" ,lang)))
   `(let* ((lang-headers (intern (concat "org-babel-default-header-args:" ,lang)))
 	  (heading (nth 4 (ignore-errors (org-heading-components))))
 	  (heading (nth 4 (ignore-errors (org-heading-components))))
 	  (export-buffer (current-buffer))
 	  (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
        (save-restriction
 	 (when heading
 	 (when heading
 	   (condition-case nil
 	   (condition-case nil
@@ -152,12 +142,17 @@ this template."
   :type 'string)
   :type 'string)
 
 
 (defvar org-babel-default-lob-header-args)
 (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)
   (interactive)
   (save-window-excursion
   (save-window-excursion
     (save-excursion
     (save-excursion
       (let ((case-fold-search t)
       (let ((case-fold-search t)
+	    (org-babel-exp-reference-buffer reference-buffer)
 	    (regexp (concat org-babel-inline-src-block-regexp "\\|"
 	    (regexp (concat org-babel-inline-src-block-regexp "\\|"
 			    org-babel-lob-one-liner-regexp "\\|"
 			    org-babel-lob-one-liner-regexp "\\|"
 			    "^[ \t]*#\\+BEGIN_SRC")))
 			    "^[ \t]*#\\+BEGIN_SRC")))
@@ -186,7 +181,7 @@ this template."
 			 (if (and (cdr (assoc :noweb params))
 			 (if (and (cdr (assoc :noweb params))
 				  (string= "yes" (cdr (assoc :noweb params))))
 				  (string= "yes" (cdr (assoc :noweb params))))
 			     (org-babel-expand-noweb-references
 			     (org-babel-expand-noweb-references
-			      info (org-babel-exp-get-export-buffer))
+			      info org-babel-exp-reference-buffer)
 			   (nth 1 info)))
 			   (nth 1 info)))
 		   (goto-char begin)
 		   (goto-char begin)
 		   (let ((replacement (org-babel-exp-do-export info 'inline)))
 		   (let ((replacement (org-babel-exp-do-export info 'inline)))
@@ -342,7 +337,7 @@ replaced with its value."
 	     (org-babel-noweb-wrap) "" (nth 1 info))
 	     (org-babel-noweb-wrap) "" (nth 1 info))
 	  (if (org-babel-noweb-p (nth 2 info) :export)
 	  (if (org-babel-noweb-p (nth 2 info) :export)
 	      (org-babel-expand-noweb-references
 	      (org-babel-expand-noweb-references
-	       info (org-babel-exp-get-export-buffer))
+	       info org-babel-exp-reference-buffer)
 	    (nth 1 info))))
 	    (nth 1 info))))
   (org-fill-template
   (org-fill-template
    org-babel-exp-code-template
    org-babel-exp-code-template
@@ -371,7 +366,7 @@ inhibit insertion of results into the buffer."
     (let ((lang (nth 0 info))
     (let ((lang (nth 0 info))
 	  (body (if (org-babel-noweb-p (nth 2 info) :eval)
 	  (body (if (org-babel-noweb-p (nth 2 info) :eval)
 		    (org-babel-expand-noweb-references
 		    (org-babel-expand-noweb-references
-		     info (org-babel-exp-get-export-buffer))
+		     info org-babel-exp-reference-buffer)
 		  (nth 1 info)))
 		  (nth 1 info)))
 	  (info (copy-sequence info))
 	  (info (copy-sequence info))
 	  (org-babel-current-src-block-location (point-marker)))
 	  (org-babel-current-src-block-location (point-marker)))

+ 93 - 92
lisp/ob-ref.el

@@ -129,98 +129,99 @@ the variable."
 (defun org-babel-ref-resolve (ref)
 (defun org-babel-ref-resolve (ref)
   "Resolve the reference REF and return its value."
   "Resolve the reference REF and return its value."
   (save-window-excursion
   (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)
 (defun org-babel-ref-index-list (index lis)
   "Return the subset of LIS indexed by INDEX.
   "Return the subset of LIS indexed by INDEX.

+ 2 - 2
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
 It can be used to teach Babel blocks how to act differently
 according to the back-end used.")
 according to the back-end used.")
 
 
+
 
 
 ;;; User-configurable Variables
 ;;; User-configurable Variables
 ;;
 ;;
@@ -3434,8 +3435,7 @@ file should have."
   ;; Get a pristine copy of current buffer so Babel references can be
   ;; Get a pristine copy of current buffer so Babel references can be
   ;; properly resolved.
   ;; properly resolved.
   (let ((reference (org-export-copy-buffer)))
   (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))))
       (kill-buffer reference))))
 
 
 (defun org-export--copy-to-kill-ring-p ()
 (defun org-export--copy-to-kill-ring-p ()

+ 15 - 2
testing/lisp/test-ob-exp.el

@@ -32,8 +32,7 @@ Current buffer is a copy of the original buffer."
      (with-temp-buffer
      (with-temp-buffer
        (org-mode)
        (org-mode)
        (insert string)
        (insert string)
-       (let ((org-current-export-file buf))
-	 (org-babel-exp-process-buffer))
+       (org-babel-exp-process-buffer buf)
        (goto-char (point-min))
        (goto-char (point-min))
        (progn ,@body))))
        (progn ,@body))))
 
 
@@ -405,6 +404,20 @@ src_emacs-lisp{(+ 1 1)}"
       (org-export-execute-babel-code)
       (org-export-execute-babel-code)
       (buffer-string)))))
       (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)
 (provide 'test-ob-exp)
 
 

+ 1 - 2
testing/lisp/test-ob-lob.el

@@ -83,8 +83,7 @@
       (with-temp-buffer
       (with-temp-buffer
 	(org-mode)
 	(org-mode)
 	(insert string)
 	(insert string)
-	(let ((org-current-export-file buf))
-	  (org-babel-exp-process-buffer))
+	(org-babel-exp-process-buffer buf)
 	(message (buffer-string))
 	(message (buffer-string))
 	(goto-char (point-min))
 	(goto-char (point-min))
 	(should (re-search-forward "^: 0" nil t))
 	(should (re-search-forward "^: 0" nil t))

+ 1 - 1
testing/lisp/test-ob.el

@@ -645,7 +645,7 @@ on two lines
     (check-eval "no" nil)
     (check-eval "no" nil)
     (check-eval "never-export" t)
     (check-eval "never-export" t)
     (check-eval "no-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 "never" nil)
       (check-eval "no" nil)
       (check-eval "no" nil)
       (check-eval "never-export" nil)
       (check-eval "never-export" nil)