Ihor Radchenko <yanta...@posteo.net> writes: > 1. Probably better make it internal function for more flexibility in > future > 2. You need to consult `org-element-context' to make sure that we are > really at inline src block, but not inside some kind of verbatim > markup. > 3. Rather than copying regexps around, please factor it out and create a > constant in org-element.el
I'm attaching an updated patch to fix the inline async session issue that incorporates this feedback along with some other changes. Rather than creating a general function in ob-core to find the previous src or inline src block, I decided to create a private function in ob-comint that is specialized for finding the block of an async result (`org-babel-comint-async--find-src'). In addition to using `org-element-context' to make sure we really are at a source block, I also added a check that the block's result contains the hash (`uuid-or-tmpfile') that the async result replaces. I also created a constant for the regexp in org-element.el, and added unit tests in test-ob-python.el for inline and async inline session blocks.
>From 29fc7ae73b2d9968a39b36eae787d03ff004f56f Mon Sep 17 00:00:00 2001 From: Jack Kamm <jackk...@gmail.com> Date: Sun, 16 Mar 2025 21:12:34 -0700 Subject: [PATCH] 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 f0a2c0f58..fb3808b30 100644 --- a/lisp/ob-comint.el +++ b/lisp/ob-comint.el @@ -36,8 +36,13 @@ (org-assert-version) (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 @@ (defun org-babel-comint-async-filter (string) (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 @@ (defun org-babel-comint-async-filter (string) 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 @@ (defun org-babel-comint-async-filter (string) ;; 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 8e17af8cf..fec90c45f 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -3697,6 +3697,9 @@ (defun org-element-inline-babel-call-interpreter (inline-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 @@ (defun org-element-inline-src-block-parser () (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 a435457c4..415f877ac 100644 --- a/testing/lisp/test-ob-python.el +++ b/testing/lisp/test-ob-python.el @@ -246,6 +246,37 @@ (ert-deftest test-ob-python/async-simple-session-output () (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. -- 2.49.0