branch: externals/org
commit 10ffea6b63fd3993558614789d9ab380f67b3c89
Author: Jack Kamm <jackk...@gmail.com>
Commit: Jack Kamm <jackk...@gmail.com>

    ob-comint: Fix async session evaluation for inline src blocks
    
    * lisp/ob-comint.el: Imports for functions and variables from
    org-element and org-element-ast.  Note org-element.el cannot be
    directly imported due to recursive requires, so use declare-function
    and defvar instead.
    (org-babel-comint-async-filter): Use new function
    `org-babel-comint-async--find-src' instead of
    `org-babel-previous-src-block'.
    (org-babel-comint-async--find-src): New helper function to find the
    source block or inline source associated with an async session result.
    * lisp/org-element.el (org-element-inline-src-block-regexp): New
    constant regexp to match inline source blocks.
    (org-element-inline-src-block-parser): Use
    `org-element-inline-src-block-regexp'.
    *
    testing/lisp/test-ob-python.el (test-ob-python/inline-session-output):
    New test for inline python session blocks.
    (test-ob-python/async-inline-session-output): New test for async inline 
python
    session blocks.
---
 lisp/ob-comint.el              | 46 ++++++++++++++++++++++++++++++++++++++----
 lisp/org-element.el            |  5 ++++-
 testing/lisp/test-ob-python.el | 31 ++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+), 5 deletions(-)

diff --git a/lisp/ob-comint.el b/lisp/ob-comint.el
index f0a2c0f580..fb3808b304 100644
--- a/lisp/ob-comint.el
+++ b/lisp/ob-comint.el
@@ -36,8 +36,13 @@
 
 (require 'ob-core)
 (require 'org-compat)
+(require 'org-element-ast)
+
 (require 'comint)
 
+(declare-function org-element-context "org-element" (&optional element))
+(defvar org-element-inline-src-block-regexp)
+
 (defun org-babel-comint-buffer-livep (buffer)
   "Check if BUFFER is a comint buffer with a live process."
   (let ((buffer (when buffer (get-buffer buffer))))
@@ -312,8 +317,7 @@ STRING contains the output originally inserted into the 
comint buffer."
                          (with-current-buffer buf
                            (save-excursion
                              (goto-char (point-min))
-                             (when (search-forward tmp-file nil t)
-                               (org-babel-previous-src-block)
+                             (when (org-babel-comint-async--find-src tmp-file)
                                 (let* ((info (org-babel-get-src-block-info))
                                        (params (nth 2 info))
                                        (result-params
@@ -363,8 +367,7 @@ STRING contains the output originally inserted into the 
comint buffer."
                       until (with-current-buffer buf
                               (save-excursion
                                 (goto-char (point-min))
-                                (when (search-forward uuid nil t)
-                                  (org-babel-previous-src-block)
+                                (when (org-babel-comint-async--find-src uuid)
                                    (let* ((info (org-babel-get-src-block-info))
                                           (params (nth 2 info))
                                           (result-params
@@ -376,6 +379,41 @@ STRING contains the output originally inserted into the 
comint buffer."
              ;; Remove uuid from the list to search for
              (setq uuid-list (delete uuid uuid-list)))))))))
 
+(defun org-babel-comint-async--find-src (uuid-or-tmpfile)
+  "Find source block associated with an async comint result.
+UUID-OR-TMPFILE is the uuid or tmpfile associated with the result.
+Returns non-nil if the source block is succesfully found, and moves
+point there.
+
+This function assumes that UUID-OR-TMPFILE was previously inserted as
+the source block's result, as a placeholder until the true result
+becomes ready.  It may fail to find the source block if the buffer was
+modified so that UUID-OR-TMPFILE is no longer the result of the source
+block, or if it has been copied elsewhere into the buffer (this is a
+limitation of the current async implementation)."
+  (goto-char (point-min))
+  (when (search-forward uuid-or-tmpfile nil t)
+    (let ((uuid-pos (point)))
+      (and (re-search-backward
+            ;; find the nearest preceding src or inline-src block
+            (rx (or (regexp org-babel-src-block-regexp)
+                    (regexp org-element-inline-src-block-regexp)))
+            nil t)
+           ;; check it's actually a src block and not verbatim text
+           (org-element-type-p (org-element-context)
+                               '(inline-src-block src-block))
+           ;; Check result contains the uuid. There isn't a simple way
+           ;; to extract the result value that works in all cases
+           ;; (e.g. inline blocks or results drawers), so instead
+           ;; check the result region contains the found uuid position
+           (let ((result-where (org-babel-where-is-src-block-result)))
+             (when result-where
+               (save-excursion
+                 (goto-char result-where)
+                 (and
+                  (>= uuid-pos (org-element-property :begin 
(org-element-context)))
+                  (< uuid-pos (org-element-property :end 
(org-element-context)))))))))))
+
 (defun org-babel-comint-async-register
     (session-buffer org-buffer indicator-regexp
                    chunk-callback file-callback
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 8e17af8cf6..fec90c45fa 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -3697,6 +3697,9 @@ Assume point is at the beginning of the babel call."
 
 ;;;; Inline Src Block
 
+(defconst org-element-inline-src-block-regexp "\\<src_\\([^ \t\n[{]+\\)[{[]"
+  "Regexp matching inline source blocks.")
+
 (defun org-element-inline-src-block-parser ()
   "Parse inline source block at point, if any.
 
@@ -3709,7 +3712,7 @@ Assume point is at the beginning of the inline source 
block."
   (save-excursion
     (catch :no-object
       (when (let ((case-fold-search nil))
-             (looking-at "\\<src_\\([^ \t\n[{]+\\)[{[]"))
+             (looking-at org-element-inline-src-block-regexp))
        (goto-char (match-end 1))
        (let ((begin (match-beginning 0))
              (language (org-element--get-cached-string
diff --git a/testing/lisp/test-ob-python.el b/testing/lisp/test-ob-python.el
index a435457c4b..415f877aca 100644
--- a/testing/lisp/test-ob-python.el
+++ b/testing/lisp/test-ob-python.el
@@ -246,6 +246,37 @@ print('Yep!')
                                (goto-char 
(org-babel-where-is-src-block-result))
                                (org-babel-read-result)))))))))
 
+(ert-deftest test-ob-python/inline-session-output ()
+  ;; Disable the test on older Emacs as built-in python.el sometimes
+  ;; fail to initialize session.
+  (skip-unless (version<= "28" emacs-version))
+  (let ((org-babel-temporary-directory temporary-file-directory)
+        (org-confirm-babel-evaluate nil)
+        (org-babel-inline-result-wrap "=%s="))
+    (org-test-with-temp-text
+     "src_python[:session :results output]{print(1+1)}"
+     (should (string= "2" (org-babel-execute-src-block))))))
+
+(ert-deftest test-ob-python/async-inline-session-output ()
+  ;; Disable the test on older Emacs as built-in python.el sometimes
+  ;; fail to initialize session.
+  (skip-unless (version<= "28" emacs-version))
+  (let ((org-babel-temporary-directory temporary-file-directory)
+        (org-confirm-babel-evaluate nil)
+        (org-babel-inline-result-wrap "=%s=")
+        (test-line "src_python[:session :async yes :results 
output]{print(1+1)}"))
+    (org-test-with-temp-text
+     test-line
+     (goto-char (point-min)) (org-babel-execute-maybe)
+     (should (let* ((expected-result "2")
+                    (expected-full (format "%s {{{results(=%s=)}}}"
+                                           test-line expected-result)))
+              (and (not (string= expected-result 
(org-babel-execute-src-block)))
+                   (string= expected-full
+                            (progn
+                              (sleep-for 0.200)
+                               (buffer-substring-no-properties (point-at-bol) 
(point-at-eol))))))))))
+
 (ert-deftest test-ob-python/async-named-output ()
   ;; Disable the test on older Emacs as built-in python.el sometimes
   ;; fail to initialize session.

Reply via email to