diff --git a/lisp/ob-R.el b/lisp/ob-R.el index 0bb5862b1..3214bf72c 100644 --- a/lisp/ob-R.el +++ b/lisp/ob-R.el @@ -492,7 +492,8 @@ by `org-babel-comint-async-filter'." session (current-buffer) "^\\(?:[>.+] \\)*\\[1\\] \"ob_comint_async_R_\\(start\\|end\\|file\\)_\\(.+\\)\"$" 'org-babel-chomp - 'ob-session-async-R-value-callback) + 'ob-session-async-R-value-callback + 'disable-prompt-filtering) (cl-case result-type (value (let ((tmp-file (org-babel-temp-file "R-"))) diff --git a/lisp/ob-comint.el b/lisp/ob-comint.el index b88ac445a..65f18fa80 100644 --- a/lisp/ob-comint.el +++ b/lisp/ob-comint.el @@ -240,6 +240,9 @@ Its single argument is a string consisting of output from the comint process. It should return a string that will be passed to `org-babel-insert-result'.") +(defvar-local org-babel-comint-async-remove-prompts-p t + "Whether prompts should be detected and removed from async output.") + (defvar-local org-babel-comint-async-dangling nil "Dangling piece of the last process output, as a string. Used when `org-babel-comint-async-indicator' is spread across multiple @@ -327,10 +330,16 @@ STRING contains the output originally inserted into the comint buffer." until (and (equal (match-string 1) "start") (equal (match-string 2) uuid)) finally return (+ 1 (match-end 0))))) - ;; Remove prompt - (res-promptless (org-trim (string-join (mapcar #'org-trim (org-babel-comint--prompt-filter res-str-raw)) "\n") "\n")) ;; Apply user callback - (res-str (funcall org-babel-comint-async-chunk-callback res-promptless))) + (res-str (funcall org-babel-comint-async-chunk-callback + (if org-babel-comint-async-remove-prompts-p + (org-trim (string-join + (mapcar #'org-trim + (org-babel-comint--prompt-filter + res-str-raw)) + "\n") + t) + res-str-raw)))) ;; Search for uuid in associated org-buffers to insert results (cl-loop for buf in org-buffers until (with-current-buffer buf @@ -351,18 +360,31 @@ STRING contains the output originally inserted into the comint buffer." (defun org-babel-comint-async-register (session-buffer org-buffer indicator-regexp - chunk-callback file-callback) + chunk-callback file-callback + &optional prompt-handling) "Set local org-babel-comint-async variables in SESSION-BUFFER. ORG-BUFFER is added to `org-babel-comint-async-buffers' if not present. `org-babel-comint-async-indicator', `org-babel-comint-async-chunk-callback', and `org-babel-comint-async-file-callback' are set to -INDICATOR-REGEXP, CHUNK-CALLBACK, and FILE-CALLBACK -respectively." +INDICATOR-REGEXP, CHUNK-CALLBACK, and FILE-CALLBACK respectively. +PROMPT-HANDLING may be either of the symbols `filter-prompts', in +which case prompts matching `comint-prompt-regexp' are filtered +from output before it is passed to CHUNK-CALLBACK, or +`disable-prompt-filtering', in which case this behavior is +disabled. For backward-compatibility, the default value of `nil' +is equivalent to `filter-prompts'." (org-babel-comint-in-buffer session-buffer (setq org-babel-comint-async-indicator indicator-regexp org-babel-comint-async-chunk-callback chunk-callback org-babel-comint-async-file-callback file-callback) + (setq org-babel-comint-async-remove-prompts-p + (cond + ((eq prompt-handling 'disable-prompt-filtering) nil) + ((eq prompt-handling 'filter-prompts) t) + ((eq prompt-handling nil) t) + (t (error (format "Unrecognized prompt handling behavior %s" + prompt-handling))))) (unless (memq org-buffer org-babel-comint-async-buffers) (setq org-babel-comint-async-buffers (cons org-buffer org-babel-comint-async-buffers))) diff --git a/lisp/ob-python.el b/lisp/ob-python.el index f881918c7..9975e83be 100644 --- a/lisp/ob-python.el +++ b/lisp/ob-python.el @@ -538,7 +538,8 @@ by `org-babel-comint-async-filter'." (org-babel-comint-async-register session (current-buffer) "ob_comint_async_python_\\(start\\|end\\|file\\)_\\(.+\\)" - 'org-babel-chomp 'org-babel-python-async-value-callback) + 'org-babel-chomp 'org-babel-python-async-value-callback + 'disable-prompt-filtering) (pcase result-type (`output (let ((uuid (org-id-uuid))) diff --git a/lisp/org-macs.el b/lisp/org-macs.el index 6765363ec..051c5c0a1 100644 --- a/lisp/org-macs.el +++ b/lisp/org-macs.el @@ -33,6 +33,7 @@ (require 'cl-lib) (require 'format-spec) +(eval-when-compile (require 'subr-x)) ; For `when-let*', Emacs < 29 ;;; Org version verification. diff --git a/lisp/org.el b/lisp/org.el index 819a82eb9..7bc87c25c 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -8279,9 +8279,12 @@ See the docstring of `org-open-file' for details." ;; link abbreviations. So, suppressing parser complains about ;; non-Org buffer to keep the feature working at least to the extent ;; it did before. + (defvar warning-suppress-types) ; warnings.el (let ((warning-suppress-types (cons '(org-element org-element-parser) warning-suppress-types))) + ;; FIXME: Suppress warning in Emacs <30 + ;; (ignore warning-suppress-types) (org-open-at-point))) (defvar org-window-config-before-follow-link nil diff --git a/testing/lisp/test-ob-R.el b/testing/lisp/test-ob-R.el index 0d291bf54..2e171b2eb 100644 --- a/testing/lisp/test-ob-R.el +++ b/testing/lisp/test-ob-R.el @@ -316,6 +316,34 @@ x (string= (concat text result) (buffer-string))))))) +(ert-deftest test-ob-R/async-prompt-filter () + "Test that async evaluation doesn't remove spurious prompts and leading indentation." + (let* (ess-ask-for-ess-directory + ess-history-file + org-confirm-babel-evaluate + (session-name "*R:test-ob-R/session-async-results*") + (kill-buffer-query-functions nil) + (start-time (current-time)) + (wait-time (time-add start-time 3)) + uuid-placeholder) + (org-test-with-temp-text + (concat "#+begin_src R :session " session-name " :async t :results output +table(c('ab','ab','c',NA,NA), useNA='always') +#+end_src") + (setq uuid-placeholder (org-trim (org-babel-execute-src-block))) + (catch 'too-long + (while (string-match uuid-placeholder (buffer-string)) + (progn + (sleep-for 0.01) + (when (time-less-p wait-time (current-time)) + (throw 'too-long (ert-fail "Took too long to get result from callback")))))) + (search-forward "#+results") + (beginning-of-line 2) + (when (should (re-search-forward "\ +:\\([ ]+ab\\)[ ]+c[ ]+[ ]* +:\\([ ]+2\\)[ ]+1[ ]+2")) + (should (equal (length (match-string 1)) (length (match-string 2)))) + (kill-buffer session-name))))) (provide 'test-ob-R)