lisp/ob-comint.el: Introduce a fallback prompt regexp

* lisp/ob-comint.el (org-babel-comint-prompt-regexp-old): New variable
storing the default value of `comint-prompt-regexp' to be used when
the prompt set by Org mode changes for some reason.
(org-babel-comint-fallback-regexp-threshold): New customization to set
the time Org babel waits for comint command to finish until trying
fallback prompt regexp.
(org-babel-comint--set-fallback-prompt): New internal function that
sets the fallback prompt regexp, if there is any available.
(org-babel-comint-with-output):
(org-babel-comint-wait-for-output): Try fallback prompt regexp when we
cannot find comint command end for too long.
* lisp/ob-haskell.el (org-babel-interpret-haskell):
* lisp/ob-ruby.el (org-babel-ruby-initiate-session):
* lisp/ob-shell.el (org-babel-sh-initiate-session):
* lisp/ob-clojure.el (ob-clojure-eval-with-inf-clojure): Set
`org-babel-comint-prompt-regexp-old' when initializing the inferior
shell.
* etc/ORG-NEWS (New option
~org-babel-comint-fallback-regexp-threshold~): Document the new
customization.

Reported-by: Jack Kamm <jackkamm@tatersworld.org>
Link: https://orgmode.org/list/87sf2q9ubd.fsf@gmail.com
This commit is contained in:
Ihor Radchenko 2024-01-22 12:55:09 +01:00
parent c76d498f44
commit 27d6f8305c
No known key found for this signature in database
GPG Key ID: 6470762A7DA11D8B
6 changed files with 95 additions and 20 deletions

View File

@ -469,6 +469,30 @@ The change is breaking when ~org-use-property-inheritance~ is set to ~t~.
The =TEST= parameter is better served by Emacs debugging tools.
** New and changed options
*** New option ~org-babel-comint-fallback-regexp-threshold~
Org babel is often using Emacs' interactive REPL feature to implement
:session functionality in code blocks. However, Emacs' REPLs use
heuristics to detect which lines in the REPL buffer correspond to
output and which lines are user prompts.
Normally, Org babel changes the default prompt to something unique. It
avoids incorrect detection of code block output.
Sometimes, the Org-configured prompt is changed manually by users or
when running a sub-REPL (for example, when running ssh/python
interpreter inside shell).
The new option controls Org mode's heuristics for catching
user-changed prompt in interactive Org babel sessions. When Org mode
cannot find REPL's prompt for more than
~org-babel-comint-fallback-regexp-threshold~ seconds, imprecise
generic prompt is tried to detect whether the code block output has
arrived.
Users who often work with altering REPL prompts may consider reducing
the default 5 second value of the new option.
*** ~repeated-after-deadline~ value of ~org-agenda-skip-scheduled-repeats-after-deadline~ is moved to a new customization
A new custom option ~org-agenda-skip-scheduled-repeats-after-deadline~

View File

@ -237,7 +237,9 @@ or set the `:backend' header argument"))))
"clojure" (format "clojure -A%s" alias)
cmd0)
cmd0)))
(setq comint-prompt-regexp inf-clojure-comint-prompt-regexp)
(setq
org-babel-comint-prompt-regexp-old comint-prompt-regexp
comint-prompt-regexp inf-clojure-comint-prompt-regexp)
(funcall-interactively #'inf-clojure cmd)
(goto-char (point-max))))
(sit-for 1))

View File

@ -58,6 +58,22 @@ executed inside the protection of `save-excursion' and
(let ((comint-input-filter (lambda (_input) nil)))
,@body))))))
(defvar-local org-babel-comint-prompt-regexp-old nil
"Fallback regexp used to detect prompt.")
(defcustom org-babel-comint-fallback-regexp-threshold 5.0
"Waiting time until trying to use fallback regexp to detect prompt.
This is useful when prompt unexpectedly changes."
:type 'float
:group 'org-babel)
(defun org-babel-comint--set-fallback-prompt ()
"Swap `comint-prompt-regexp' and `org-babel-comint-prompt-regexp-old'."
(when org-babel-comint-prompt-regexp-old
(let ((tmp comint-prompt-regexp))
(setq comint-prompt-regexp org-babel-comint-prompt-regexp-old
org-babel-comint-prompt-regexp-old tmp))))
(defmacro org-babel-comint-with-output (meta &rest body)
"Evaluate BODY in BUFFER and return process output.
Will wait until EOE-INDICATOR appears in the output, then return
@ -96,14 +112,29 @@ or user `keyboard-quit' during execution of body."
;; pass FULL-BODY to process
,@body
;; wait for end-of-evaluation indicator
(while (progn
(goto-char comint-last-input-end)
(not (save-excursion
(and (re-search-forward
(regexp-quote ,eoe-indicator) nil t)
(re-search-forward
comint-prompt-regexp nil t)))))
(accept-process-output (get-buffer-process (current-buffer))))
(let ((start-time (current-time)))
(while (progn
(goto-char comint-last-input-end)
(not (save-excursion
(and (re-search-forward
(regexp-quote ,eoe-indicator) nil t)
(re-search-forward
comint-prompt-regexp nil t)))))
(accept-process-output
(get-buffer-process (current-buffer))
org-babel-comint-fallback-regexp-threshold)
(when (and org-babel-comint-prompt-regexp-old
(> (float-time (time-since start-time))
org-babel-comint-fallback-regexp-threshold)
(progn
(goto-char comint-last-input-end)
(save-excursion
(and
(re-search-forward
(regexp-quote ,eoe-indicator) nil t)
(re-search-forward
org-babel-comint-prompt-regexp-old nil t)))))
(org-babel-comint--set-fallback-prompt))))
;; replace cut dangling text
(goto-char (process-mark (get-buffer-process (current-buffer))))
(insert dangling-text)
@ -148,11 +179,23 @@ The input will not be echoed."
Note: this is only safe when waiting for the result of a single
statement (not large blocks of code)."
(org-babel-comint-in-buffer buffer
(while (progn
(goto-char comint-last-input-end)
(not (and (re-search-forward comint-prompt-regexp nil t)
(goto-char (match-beginning 0)))))
(accept-process-output (get-buffer-process buffer)))))
(let ((start-time (current-time)))
(while (progn
(goto-char comint-last-input-end)
(not (and (re-search-forward comint-prompt-regexp nil t)
(goto-char (match-beginning 0)))))
(accept-process-output
(get-buffer-process buffer)
org-babel-comint-fallback-regexp-threshold)
(when (and org-babel-comint-prompt-regexp-old
(> (float-time (time-since start-time))
org-babel-comint-fallback-regexp-threshold)
(progn
(goto-char comint-last-input-end)
(save-excursion
(re-search-forward
org-babel-comint-prompt-regexp-old nil t))))
(org-babel-comint--set-fallback-prompt))))))
(defun org-babel-comint-eval-invisibly-and-wait-for-file
(buffer file string &optional period)

View File

@ -152,8 +152,10 @@ This function should only be called by `org-babel-execute:haskell'."
(org-require-package 'inf-haskell "haskell-mode")
(add-hook 'inferior-haskell-hook
(lambda ()
(setq-local comint-prompt-regexp
(concat haskell-prompt-regexp "\\|^λ?> "))))
(setq-local
org-babel-comint-prompt-regexp-old comint-prompt-regexp
comint-prompt-regexp
(concat haskell-prompt-regexp "\\|^λ?> "))))
(org-babel-haskell-with-session session params
(cl-labels
((send-txt-to-ghci (txt)

View File

@ -191,7 +191,9 @@ Session settings (`:ruby' header arg value) are taken from PARAMS."
;; uniquely by regexp.
(when new-session?
(with-current-buffer session-buffer
(setq-local comint-prompt-regexp (concat "^" org-babel-ruby-prompt))
(setq-local
org-babel-comint-prompt-regexp-old comint-prompt-regexp
comint-prompt-regexp (concat "^" org-babel-ruby-prompt))
(insert org-babel-ruby-define-prompt ";")
(insert "_org_prompt_mode=conf.prompt_mode;conf.prompt_mode=:CUSTOM;")
(insert "conf.echo=false\n")

View File

@ -273,9 +273,11 @@ var of the same value."
org-babel-shell-set-prompt-commands))
(alist-get t org-babel-shell-set-prompt-commands))
org-babel-sh-prompt))
(setq-local comint-prompt-regexp
(concat "^" (regexp-quote org-babel-sh-prompt)
" *"))
(setq-local
org-babel-comint-prompt-regexp-old comint-prompt-regexp
comint-prompt-regexp
(concat "^" (regexp-quote org-babel-sh-prompt)
" *"))
;; Needed for Emacs 23 since the marker is initially
;; undefined and the filter functions try to use it without
;; checking.