scratch/eshell-lisp-pipe bc9d1ff27a9: Allow sending interactive input to Eshell workers
Jim Porter <[email protected]> Fri, 31 Jul 2026 13:45:40 -0400 (EDT)
| Newsgroups | gmane.emacs.diffs |
|---|---|
| Message-ID | <[email protected]> |
branch: scratch/eshell-lisp-pipe commit bc9d1ff27a9070341e679b20a63c70d3e1ff055f Author: Jim Porter <[email protected]> Commit: Jim Porter <[email protected]> Allow sending interactive input to Eshell workers * lisp/eshell/esh-io.el (eshell-send-eof-to-target) (eshell-output-region-to-target): New generic methods. * lisp/eshell/esh-mode.el (eshell-send-input): Call 'eshell-output-region-to-target'. (eshell-send-eof-to-process): Call 'eshell-send-eof-to-target'. * lisp/eshell/esh-worker.el (eshell-worker-initialize): New function. * test/lisp/eshell/esh-worker-tests.el (esh-worker-test/map-lines/interactive) (esh-worker-test/apply-lines/interactive): New tests. --- lisp/eshell/esh-io.el | 24 ++++++++++++++++++++++++ lisp/eshell/esh-mode.el | 10 +++++----- lisp/eshell/esh-worker.el | 6 ++++++ test/lisp/eshell/esh-worker-tests.el | 31 +++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index 395a641aed6..39e456ff9d3 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -702,6 +702,15 @@ If status is nil, prompt before killing." (when-let* ((close-function (eshell-function-target-close-function target))) (funcall close-function status))) +(cl-defgeneric eshell-send-eof-to-target (target) + "Send end-of-file to TARGET. +By default, this is equivalent to `eshell-close-target' with nil status." + (eshell-close-target target nil)) + +(cl-defmethod eshell-send-eof-to-target ((target process)) + "Send end-of-file to a TARGET process." + (process-send-eof target)) + (cl-defgeneric eshell-output-object-to-target (object target) "Output OBJECT to TARGET. Returns what was actually sent, or nil if nothing was sent.") @@ -766,6 +775,21 @@ command output when `eshell-ensure-newline-p' is non-nil." "Return non-nil to indicate that the display is line-oriented." t) +(cl-defgeneric eshell-output-region-to-target (start end target) + "Output the region from START to END to TARGET." + (eshell-output-object-to-target (buffer-substring start end) target)) + +(cl-defmethod eshell-output-region-to-target (start end (target process)) + "Output the region from START to END to the process TARGET." + (condition-case _ + (process-send-region target start end) + (error + ;; NOTE: When running Emacs in batch mode (e.g. during regression + ;; tests), Emacs can abort due to SIGPIPE here. Maybe + ;; `process-send-region' should handle SIGPIPE even in batch mode + ;; (bug#66186). + (signal 'eshell-pipe-broken (list target))))) + (defun eshell-output-object (object &optional handle-index handles) "Insert OBJECT, using HANDLE-INDEX specifically. If HANDLE-INDEX is nil, output to `eshell-output-handle'. diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index 3a6c9a2a7f4..057a1e91773 100644 --- a/lisp/eshell/esh-mode.el +++ b/lisp/eshell/esh-mode.el @@ -645,10 +645,10 @@ newline." (if (or eshell-send-direct-to-subprocesses (= eshell-last-input-start eshell-last-input-end)) (unless no-newline - (process-send-string (eshell-head-process) "\n")) - (process-send-region (eshell-head-process) - eshell-last-input-start - eshell-last-input-end))) + (eshell-output-object-to-target "\n" (eshell-head-process))) + (eshell-output-region-to-target eshell-last-input-start + eshell-last-input-end + (eshell-head-process)))) (if (= eshell-last-output-end (point)) (run-hooks 'eshell-post-command-hook) (let (input) @@ -685,7 +685,7 @@ newline." (interactive) (eshell-send-input nil nil t) (when (eshell-head-process) - (process-send-eof (eshell-head-process)))) + (eshell-send-eof-to-target (eshell-head-process)))) (defsubst eshell-kill-new () "Add the last input text to the kill ring." diff --git a/lisp/eshell/esh-worker.el b/lisp/eshell/esh-worker.el index cdbde95d41d..6f86ad02825 100644 --- a/lisp/eshell/esh-worker.el +++ b/lisp/eshell/esh-worker.el @@ -66,6 +66,12 @@ commands' output with ordinary Lisp." :tag "Worker support" :group 'eshell) +(defun eshell-worker-initialize () ;Called from `eshell-mode' via intern-soft! + "Initialize the Eshell worker code." + (setq-local eshell-complex-commands + (append '("accumulate" "apply-lines" "map-lines") + eshell-complex-commands))) + (cl-defstruct (eshell-worker (:constructor eshell-worker-create) (:copier nil)) diff --git a/test/lisp/eshell/esh-worker-tests.el b/test/lisp/eshell/esh-worker-tests.el index b9b1ea3c256..d51f5319a59 100644 --- a/test/lisp/eshell/esh-worker-tests.el +++ b/test/lisp/eshell/esh-worker-tests.el @@ -159,6 +159,21 @@ It should call the mapped function once per line." "{echo '10\n1'; echo '5\n20'} | map-lines #'1+" "\\`11\n16\n21\n\\'"))) +(ert-deftest esh-worker-test/map-lines/interactive () + "Test that `map-lines' works interactively." + (with-temp-eshell + (let (output-begin) + (eshell-insert-command "map-lines #'upcase") + (insert "hello") + (setq output-begin (1+ (point))) ; Add one to skip the next newline. + (eshell-send-input) + (should (string= (buffer-substring output-begin (point)) "HELLO\n")) + (insert "goodbye") + (setq output-begin (1+ (point))) + (eshell-send-input) + (should (string= (buffer-substring output-begin (point)) "GOODBYE\n")) + (eshell-send-eof-to-process)))) + (ert-deftest esh-worker-test/map-lines/error-handling () "Test that `map-lines' catches errors." (with-temp-eshell @@ -221,4 +236,20 @@ It should pass each line as an argument to the applied function." "\"hi\"\n\\'")) (should (= eshell-last-command-status 1)))) +(ert-deftest esh-worker-test/apply-lines/interactive () + "Test that `apply-lines' works interactively." + (with-temp-eshell + (let (output-begin) + (eshell-insert-command "apply-lines #'+") + (insert "5") + (eshell-send-input) + (insert "8") + (eshell-send-input) + (insert "13") + (eshell-send-input) + (setq output-begin (point)) + (eshell-send-eof-to-process) + (should (string= (buffer-substring output-begin (eshell-end-of-output)) + "26\n"))))) + ;;; esh-io-tests.el ends here