Browse Source

ob: Fix block evaluation in a narrowed buffer

* lisp/ob.el (org-babel-where-is-src-block-result): Insert new results
  keyword in current narrowed part of buffer, if necessary. Small
  refactoring.
(org-babel-insert-result): Do not widen buffer when new results have
to be inserted.  Therefore, results inserted after the last block of
a narrowed buffer still belong to the narrowed part of the buffer.
* testing/lisp/test-ob.el: Add tests.
* testing/lisp/test-ob-exp.el: Move test to test-ob.el
Nicolas Goaziou 12 years ago
parent
commit
2f2a80fe06
3 changed files with 237 additions and 160 deletions
  1. 157 149
      lisp/ob.el
  2. 0 11
      testing/lisp/test-ob-exp.el
  3. 80 0
      testing/lisp/test-ob.el

+ 157 - 149
lisp/ob.el

@@ -1723,63 +1723,58 @@ following the source block."
 	   (head (unless on-lob-line (org-babel-where-is-src-block-head)))
 	   found beg end)
       (when head (goto-char head))
-      (setq
-       found ;; was there a result (before we potentially insert one)
-       (or
-	inlinep
-	(and
-	 ;; named results:
-	 ;; - return t if it is found, else return nil
-	 ;; - if it does not need to be rebuilt, then don't set end
-	 ;; - if it does need to be rebuilt then do set end
-	 name (setq beg (org-babel-find-named-result name))
-	 (prog1 beg
-	   (when (and hash (not (string= hash (match-string 3))))
-	     (goto-char beg) (setq end beg) ;; beginning of result
-	     (forward-line 1)
-	     (delete-region end (org-babel-result-end)) nil)))
-	(and
-	 ;; unnamed results:
-	 ;; - return t if it is found, else return nil
-	 ;; - if it is found, and the hash doesn't match, delete and set end
-	 (or on-lob-line (re-search-forward "^[ \t]*#\\+end_src" nil t))
-	 (progn (end-of-line 1)
-		(if (eobp) (insert "\n") (forward-char 1))
-		(setq end (point))
-		(or (and (not name)
-			 (progn ;; unnamed results line already exists
-			   (re-search-forward "[^ \f\t\n\r\v]" nil t)
-			   (beginning-of-line 1)
-			   (looking-at
-			    (concat org-babel-result-regexp "\n")))
-			 (prog1 (point)
-			   ;; must remove and rebuild if hash!=old-hash
-			   (if (and hash (not (string= hash (match-string 3))))
-			       (prog1 nil
-				 (forward-line 1)
-				 (delete-region
-				  end (org-babel-result-end)))
-			     (setq end nil)))))))))
-      (if (and insert end)
-	  (progn
-	    (goto-char end)
-	    (unless beg
-	      (if (looking-at "[\n\r]") (forward-char 1) (insert "\n")))
-	    (insert (concat
-		     (if indent
-			 (mapconcat
-			  (lambda (el) " ")
-			  (org-number-sequence 1 indent) "")
-		       "")
-		     "#+" org-babel-results-keyword
-		     (when hash (concat "["hash"]"))
-		     ":"
-		     (when name (concat " " name)) "\n"))
-	    (unless beg (insert "\n") (backward-char))
-	    (beginning-of-line 0)
-	    (if hash (org-babel-hide-hash))
-	    (point))
-	found))))
+      (org-with-wide-buffer
+       (setq
+	found ;; was there a result (before we potentially insert one)
+	(or
+	 inlinep
+	 (and
+	  ;; named results:
+	  ;; - return t if it is found, else return nil
+	  ;; - if it does not need to be rebuilt, then don't set end
+	  ;; - if it does need to be rebuilt then do set end
+	  name (setq beg (org-babel-find-named-result name))
+	  (prog1 beg
+	    (when (and hash (not (string= hash (match-string 3))))
+	      (goto-char beg) (setq end beg) ;; beginning of result
+	      (forward-line 1)
+	      (delete-region end (org-babel-result-end)) nil)))
+	 (and
+	  ;; unnamed results:
+	  ;; - return t if it is found, else return nil
+	  ;; - if it is found, and the hash doesn't match, delete and set end
+	  (or on-lob-line (re-search-forward "^[ \t]*#\\+end_src" nil t))
+	  (progn (end-of-line 1)
+		 (if (eobp) (insert "\n") (forward-char 1))
+		 (setq end (point))
+		 (or (and (not name)
+			  (progn ;; unnamed results line already exists
+			    (re-search-forward "[^ \f\t\n\r\v]" nil t)
+			    (beginning-of-line 1)
+			    (looking-at
+			     (concat org-babel-result-regexp "\n")))
+			  (prog1 (point)
+			    ;; must remove and rebuild if hash!=old-hash
+			    (if (and hash (not (string= hash (match-string 3))))
+				(prog1 nil
+				  (forward-line 1)
+				  (delete-region
+				   end (org-babel-result-end)))
+			      (setq end nil))))))))))
+      (if (not (and insert end)) found
+	(goto-char end)
+	(unless beg
+	  (if (looking-at "[\n\r]") (forward-char 1) (insert "\n")))
+	(insert (concat
+		 (when (wholenump indent) (make-string indent ? ))
+		 "#+" org-babel-results-keyword
+		 (when hash (concat "["hash"]"))
+		 ":"
+		 (when name (concat " " name)) "\n"))
+	(unless beg (insert "\n") (backward-char))
+	(beginning-of-line 0)
+	(if hash (org-babel-hide-hash))
+	(point)))))
 
 (defvar org-block-regexp)
 (defun org-babel-read-result ()
@@ -1888,7 +1883,6 @@ code ---- the results are extracted in the syntax of the source
           inside of a #+BEGIN_SRC block with the source-code
           language set appropriately.  Note this relies on the
           optional LANG argument."
-  (save-restriction (widen)
   (if (stringp result)
       (progn
         (setq result (org-no-properties result))
@@ -1915,6 +1909,14 @@ code ---- the results are extracted in the syntax of the source
 				 t info hash indent)))
 	     (results-switches
 	      (cdr (assoc :results_switches (nth 2 info))))
+	     (visible-beg (copy-marker (point-min)))
+	     (visible-end (copy-marker (point-max)))
+	     ;; When results exist outside of the current visible
+	     ;; region of the buffer, be sure to widen buffer to
+	     ;; update them.
+	     (outside-scope-p (and existing-result
+				   (or (> visible-beg existing-result)
+				       (<= visible-end existing-result))))
 	     beg end)
 	(when (and (stringp result)  ; ensure results end in a newline
 		   (not inlinep)
@@ -1922,97 +1924,103 @@ code ---- the results are extracted in the syntax of the source
 		   (not (or (string-equal (substring result -1) "\n")
 			    (string-equal (substring result -1) "\r"))))
 	  (setq result (concat result "\n")))
-	(if (not existing-result)
-	    (setq beg (or inlinep (point)))
-	  (goto-char existing-result)
-	  (save-excursion
-	    (re-search-forward "#" nil t)
-	    (setq indent (- (current-column) 1)))
-	  (forward-line 1)
-	  (setq beg (point))
-	  (cond
-	   ((member "replace" result-params)
-	    (delete-region (point) (org-babel-result-end)))
-	   ((member "append" result-params)
-	    (goto-char (org-babel-result-end)) (setq beg (point-marker)))
-	   ((member "prepend" result-params)))) ; already there
-	(setq results-switches
-	      (if results-switches (concat " " results-switches) ""))
-	(let ((wrap (lambda (start finish)
-		      (goto-char end) (insert (concat finish "\n"))
-		      (goto-char beg) (insert (concat start "\n"))
-		      (org-escape-code-in-region (point) end)
-		      (goto-char end) (goto-char (point-at-eol))
-		      (setq end (point-marker))))
-	      (proper-list-p (lambda (it) (and (listp it) (null (cdr (last it)))))))
-	  ;; insert results based on type
-	  (cond
-	   ;; do nothing for an empty result
-	   ((null result))
-	   ;; insert a list if preferred
-	   ((member "list" result-params)
-	    (insert
-	     (org-babel-trim
-	      (org-list-to-generic
-	       (cons 'unordered
-		     (mapcar
-		      (lambda (el) (list nil (if (stringp el) el (format "%S" el))))
-		      (if (listp result) result (list result))))
-	       '(:splicep nil :istart "- " :iend "\n")))
-	     "\n"))
-	   ;; assume the result is a table if it's not a string
-	   ((funcall proper-list-p result)
-	    (goto-char beg)
-	    (insert (concat (orgtbl-to-orgtbl
-			     (if (or (eq 'hline (car result))
-				     (and (listp (car result))
-					  (listp (cdr (car result)))))
-				 result (list result))
-			     '(:fmt (lambda (cell) (format "%s" cell)))) "\n"))
-	    (goto-char beg) (when (org-at-table-p) (org-table-align)))
-	   ((and (listp result) (not (funcall proper-list-p result)))
-	    (insert (format "%s\n" result)))
-	   ((member "file" result-params)
-	    (when inlinep (goto-char inlinep))
-	    (insert result))
-	   (t (goto-char beg) (insert result)))
-	  (when (funcall proper-list-p result) (goto-char (org-table-end)))
-	  (setq end (point-marker))
-	  ;; possibly wrap result
-	  (cond
-	   ((assoc :wrap (nth 2 info))
-	    (let ((name (or (cdr (assoc :wrap (nth 2 info))) "RESULTS")))
-	      (funcall wrap (concat "#+BEGIN_" name) (concat "#+END_" name))))
-	   ((member "html" result-params)
-	    (funcall wrap "#+BEGIN_HTML" "#+END_HTML"))
-	   ((member "latex" result-params)
-	    (funcall wrap "#+BEGIN_LaTeX" "#+END_LaTeX"))
-	   ((member "org" result-params)
-	    (funcall wrap "#+BEGIN_SRC org" "#+END_SRC"))
-	   ((member "code" result-params)
-	    (funcall wrap (format "#+BEGIN_SRC %s%s" (or lang "none") results-switches)
-		     "#+END_SRC"))
-	   ((member "raw" result-params)
-	    (goto-char beg) (if (org-at-table-p) (org-cycle)))
-	   ((or (member "drawer" result-params)
-		;; Stay backward compatible with <7.9.2
-		(member "wrap" result-params))
-	    (funcall wrap ":RESULTS:" ":END:"))
-	   ((and (not (funcall proper-list-p result))
-		 (not (member "file" result-params)))
-	    (org-babel-examplize-region beg end results-switches)
-	    (setq end (point)))))
-	;; possibly indent the results to match the #+results line
-	(when (and (not inlinep) (numberp indent) indent (> indent 0)
-		   ;; in this case `table-align' does the work for us
-		   (not (and (listp result)
-			     (member "append" result-params))))
-	  (indent-rigidly beg end indent))))
-    (if (null result)
-	(if (member "value" result-params)
-	    (message "Code block returned no value.")
-	  (message "Code block produced no output."))
-      (message "Code block evaluation complete.")))))
+	(unwind-protect
+	    (progn
+	      (when outside-scope-p (widen))
+	      (if (not existing-result)
+		  (setq beg (or inlinep (point)))
+		(goto-char existing-result)
+		(save-excursion
+		  (re-search-forward "#" nil t)
+		  (setq indent (- (current-column) 1)))
+		(forward-line 1)
+		(setq beg (point))
+		(cond
+		 ((member "replace" result-params)
+		  (delete-region (point) (org-babel-result-end)))
+		 ((member "append" result-params)
+		  (goto-char (org-babel-result-end)) (setq beg (point-marker)))
+		 ((member "prepend" result-params)))) ; already there
+	      (setq results-switches
+		    (if results-switches (concat " " results-switches) ""))
+	      (let ((wrap (lambda (start finish)
+			    (goto-char end) (insert (concat finish "\n"))
+			    (goto-char beg) (insert (concat start "\n"))
+			    (org-escape-code-in-region (point) end)
+			    (goto-char end) (goto-char (point-at-eol))
+			    (setq end (point-marker))))
+		    (proper-list-p (lambda (it) (and (listp it) (null (cdr (last it)))))))
+		;; insert results based on type
+		(cond
+		 ;; do nothing for an empty result
+		 ((null result))
+		 ;; insert a list if preferred
+		 ((member "list" result-params)
+		  (insert
+		   (org-babel-trim
+		    (org-list-to-generic
+		     (cons 'unordered
+			   (mapcar
+			    (lambda (el) (list nil (if (stringp el) el (format "%S" el))))
+			    (if (listp result) result (list result))))
+		     '(:splicep nil :istart "- " :iend "\n")))
+		   "\n"))
+		 ;; assume the result is a table if it's not a string
+		 ((funcall proper-list-p result)
+		  (goto-char beg)
+		  (insert (concat (orgtbl-to-orgtbl
+				   (if (or (eq 'hline (car result))
+					   (and (listp (car result))
+						(listp (cdr (car result)))))
+				       result (list result))
+				   '(:fmt (lambda (cell) (format "%s" cell)))) "\n"))
+		  (goto-char beg) (when (org-at-table-p) (org-table-align)))
+		 ((and (listp result) (not (funcall proper-list-p result)))
+		  (insert (format "%s\n" result)))
+		 ((member "file" result-params)
+		  (when inlinep (goto-char inlinep))
+		  (insert result))
+		 (t (goto-char beg) (insert result)))
+		(when (funcall proper-list-p result) (goto-char (org-table-end)))
+		(setq end (point-marker))
+		;; possibly wrap result
+		(cond
+		 ((assoc :wrap (nth 2 info))
+		  (let ((name (or (cdr (assoc :wrap (nth 2 info))) "RESULTS")))
+		    (funcall wrap (concat "#+BEGIN_" name) (concat "#+END_" name))))
+		 ((member "html" result-params)
+		  (funcall wrap "#+BEGIN_HTML" "#+END_HTML"))
+		 ((member "latex" result-params)
+		  (funcall wrap "#+BEGIN_LaTeX" "#+END_LaTeX"))
+		 ((member "org" result-params)
+		  (funcall wrap "#+BEGIN_SRC org" "#+END_SRC"))
+		 ((member "code" result-params)
+		  (funcall wrap (format "#+BEGIN_SRC %s%s" (or lang "none") results-switches)
+			   "#+END_SRC"))
+		 ((member "raw" result-params)
+		  (goto-char beg) (if (org-at-table-p) (org-cycle)))
+		 ((or (member "drawer" result-params)
+		      ;; Stay backward compatible with <7.9.2
+		      (member "wrap" result-params))
+		  (funcall wrap ":RESULTS:" ":END:"))
+		 ((and (not (funcall proper-list-p result))
+		       (not (member "file" result-params)))
+		  (org-babel-examplize-region beg end results-switches)
+		  (setq end (point)))))
+	      ;; possibly indent the results to match the #+results line
+	      (when (and (not inlinep) (numberp indent) indent (> indent 0)
+			 ;; in this case `table-align' does the work for us
+			 (not (and (listp result)
+				   (member "append" result-params))))
+		(indent-rigidly beg end indent))
+	      (if (null result)
+		  (if (member "value" result-params)
+		      (message "Code block returned no value.")
+		    (message "Code block produced no output."))
+		(message "Code block evaluation complete.")))
+	  (when outside-scope-p (narrow-to-region visible-beg visible-end))
+	  (set-marker visible-beg nil)
+	  (set-marker visible-end nil))))))
 
 (defun org-babel-remove-result (&optional info)
   "Remove the result of the current source block."

+ 0 - 11
testing/lisp/test-ob-exp.el

@@ -273,17 +273,6 @@ elements in the final html."
       (should (string-match (regexp-quote (format nil "%S" '(:foo :bar)))
 			    ascii)))))
 
-(ert-deftest ob-exp/blocks-with-spaces ()
-  "Test expansion of blocks followed by blank lines."
-  (should
-   (equal "#+RESULTS:\n: 3\n\n\n"
-	  (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp :exports results
-\(+ 1 2)
-#+END_SRC\n\n\n"
-	    (let ((org-current-export-file (current-buffer)))
-	      (org-export-blocks-preprocess)
-	      (buffer-string))))))
-
 
 (provide 'test-ob-exp)
 

+ 80 - 0
testing/lisp/test-ob.el

@@ -1033,6 +1033,86 @@ Line 3\"
       (move-beginning-of-line 0)
       (should (looking-at (format ": %d" num))))))
 
+(ert-deftest test-ob/blocks-with-spaces ()
+  "Test expansion of blocks followed by blank lines."
+  (should
+   (equal "#+BEGIN_SRC emacs-lisp
+\(+ 1 2)
+#+END_SRC
+
+#+RESULTS:
+: 3\n\n\n"
+	  (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp
+\(+ 1 2)
+#+END_SRC\n\n\n"
+	    (progn (org-babel-execute-src-block)
+		   (buffer-string))))))
+
+(ert-deftest test-ob/results-in-narrowed-buffer ()
+  "Test block execution in a narrowed buffer."
+  ;; If results don't exist, they should be inserted in visible part
+  ;; of the buffer.
+  (should
+   (equal
+    "#+BEGIN_SRC emacs-lisp\n(+ 1 2)\n#+END_SRC\n\n#+RESULTS:\n: 3"
+    (org-test-with-temp-text
+	"#+BEGIN_SRC emacs-lisp\n(+ 1 2)\n#+END_SRC\n\nParagraph"
+      (progn
+	(narrow-to-region (point) (save-excursion (forward-line 3) (point)))
+	(org-babel-execute-src-block)
+	(org-trim (buffer-string))))))
+  (should
+   (equal
+    "#+NAME: test\n#+BEGIN_SRC emacs-lisp\n(+ 1 2)\n#+END_SRC\n\n#+RESULTS: test\n: 3"
+    (org-test-with-temp-text
+	"#+NAME: test\n#+BEGIN_SRC emacs-lisp\n(+ 1 2)\n#+END_SRC\n\nParagraph"
+      (progn
+	(narrow-to-region (point) (save-excursion (forward-line 4) (point)))
+	(org-babel-execute-src-block)
+	(org-trim (buffer-string))))))
+  ;; Results in visible part of buffer, should be updated here.
+  (should
+   (equal
+    "#+NAME: test
+#+BEGIN_SRC emacs-lisp
+\(+ 1 2)
+#+END_SRC
+
+#+RESULTS: test
+: 3"
+    (org-test-with-temp-text
+	"#+NAME: test
+#+BEGIN_SRC emacs-lisp
+\(+ 1 2)
+#+END_SRC
+
+#+RESULTS: test
+: 4
+
+Paragraph"
+      (progn
+	(narrow-to-region (point) (save-excursion (forward-line 7) (point)))
+	(org-babel-execute-src-block)
+	(org-trim (buffer-string))))))
+  ;; Results in invisible part of buffer, should be updated there.
+  (org-test-with-temp-text
+      "#+NAME: test
+#+BEGIN_SRC emacs-lisp
+\(+ 1 2)
+#+END_SRC
+
+#+RESULTS: test
+: 4
+
+Paragraph"
+    (progn
+      (narrow-to-region (point) (save-excursion (forward-line 4) (point)))
+      (org-babel-execute-src-block)
+      (should-not (re-search-forward "^#\\+RESULTS:" nil t))
+      (widen)
+      (should (should (re-search-forward "^: 3" nil t))))))
+
+
 (provide 'test-ob)
 
 ;;; test-ob ends here