summaryrefslogtreecommitdiff
path: root/lisp/eshell
diff options
context:
space:
mode:
authorJim Porter <jporterbugs@gmail.com>2022-08-28 11:19:30 -0700
committerJim Porter <jporterbugs@gmail.com>2022-09-04 15:15:01 -0700
commita87c7aff554213651678e9390dd7500b11419012 (patch)
tree50a130c8a4e8374e81dc0c8a4f53b8afd629effc /lisp/eshell
parentab7e94fb1d9b794c9d199435d72f569fba6ab017 (diff)
downloademacs-a87c7aff554213651678e9390dd7500b11419012.tar.gz
Put Eshell's bookkeeping data for external processes on the process object
This allows tracking this information for process objects not recorded in 'eshell-process-list', which will be useful for pipe processes for stderr output. * lisp/eshell/esh-proc.el (eshell-process-list): Add docstring. (eshell-record-process-object): Only record the process object and whether it's a subjob. (eshell-remove-process-entry): Adapt to changes in 'eshell-record-process-object'. (eshell-record-process-properties): New function... (eshell-gather-process-output): ... call it. (eshell-insertion-filter, eshell-sentinel): Use new process properties, don't require process to be in 'eshell-process-list'. * test/lisp/eshell/esh-proc-tests.el (esh-proc-test--output-cmd): New variable. (esh-proc-test--detect-pty-cmd): Add docstring. (esh-proc-test/output/to-screen) (esh-proc-test/output/stdout-and-stderr-to-buffer) (esh-proc-test/exit-status/success, esh-proc-test/exit-status/failure) (esh-proc-test/kill-process/foreground-only): New tests. (esh-proc-test/kill-background-process): Rename to... (esh-proc-test/kill-process/background-prompt): ... this, and use 'eshell-wait-for-subprocess' instead of 'sit-for'.
Diffstat (limited to 'lisp/eshell')
-rw-r--r--lisp/eshell/esh-proc.el144
1 files changed, 76 insertions, 68 deletions
diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el
index c367b5cd643..5ca35b71dbd 100644
--- a/lisp/eshell/esh-proc.el
+++ b/lisp/eshell/esh-proc.el
@@ -99,7 +99,13 @@ information, for example."
(defvar eshell-current-subjob-p nil)
(defvar eshell-process-list nil
- "A list of the current status of subprocesses.")
+ "A list of the current status of subprocesses.
+Each element has the form (PROC . SUBJOB-P), where PROC is the
+process object and SUBJOB-P is non-nil if the process is a
+subjob.
+
+To add or remove elements of this list, see
+`eshell-record-process-object' and `eshell-remove-process-entry'.")
(declare-function eshell-send-eof-to-process "esh-mode")
(declare-function eshell-tail-process "esh-cmd")
@@ -229,21 +235,26 @@ The prompt will be set to PROMPT."
(declare-function eshell-interactive-print "esh-mode" (string))
(eshell-interactive-print
(format "[%s] %d\n" (process-name object) (process-id object))))
- (setq eshell-process-list
- (cons (list object eshell-current-handles
- eshell-current-subjob-p nil nil)
- eshell-process-list)))
+ (push (cons object eshell-current-subjob-p) eshell-process-list))
(defun eshell-remove-process-entry (entry)
"Record the process ENTRY as fully completed."
(if (and (eshell-processp (car entry))
- (nth 2 entry)
+ (cdr entry)
eshell-done-messages-in-minibuffer)
(message "[%s]+ Done %s" (process-name (car entry))
(process-command (car entry))))
(setq eshell-process-list
(delq entry eshell-process-list)))
+(defun eshell-record-process-properties (process)
+ "Record Eshell bookkeeping properties for PROCESS.
+`eshell-insertion-filter' and `eshell-sentinel' will use these to
+do their jobs."
+ (process-put process :eshell-handles eshell-current-handles)
+ (process-put process :eshell-pending nil)
+ (process-put process :eshell-busy nil))
+
(defvar eshell-scratch-buffer " *eshell-scratch*"
"Scratch buffer for holding Eshell's input/output.")
(defvar eshell-last-sync-output-start nil
@@ -283,6 +294,7 @@ Used only on systems which do not support async subprocesses.")
:connection-type conn-type
:file-handler t)))
(eshell-record-process-object proc)
+ (eshell-record-process-properties proc)
(run-hook-with-args 'eshell-exec-hook proc)
(when (fboundp 'process-coding-system)
(let ((coding-systems (process-coding-system proc)))
@@ -363,36 +375,35 @@ PROC is the process for which we're inserting output. STRING is the
output."
(when (buffer-live-p (process-buffer proc))
(with-current-buffer (process-buffer proc)
- (let ((entry (assq proc eshell-process-list)))
- (when entry
- (setcar (nthcdr 3 entry)
- (concat (nth 3 entry) string))
- (unless (nth 4 entry) ; already being handled?
- (while (nth 3 entry)
- (let ((data (nth 3 entry)))
- (setcar (nthcdr 3 entry) nil)
- (setcar (nthcdr 4 entry) t)
- (unwind-protect
- (condition-case nil
- (eshell-output-object data nil (cadr entry))
- ;; FIXME: We want to send SIGPIPE to the process
- ;; here. However, remote processes don't
- ;; currently support that, and not all systems
- ;; have SIGPIPE in the first place (e.g. MS
- ;; Windows). In these cases, just delete the
- ;; process; this is reasonably close to the
- ;; right behavior, since the default action for
- ;; SIGPIPE is to terminate the process. For use
- ;; cases where SIGPIPE is truly needed, using an
- ;; external pipe operator (`*|') may work
- ;; instead (e.g. when working with remote
- ;; processes).
- (eshell-pipe-broken
- (if (or (process-get proc 'remote-pid)
- (eq system-type 'windows-nt))
- (delete-process proc)
- (signal-process proc 'SIGPIPE))))
- (setcar (nthcdr 4 entry) nil))))))))))
+ (process-put proc :eshell-pending
+ (concat (process-get proc :eshell-pending)
+ string))
+ (unless (process-get proc :eshell-busy) ; Already being handled?
+ (while (process-get proc :eshell-pending)
+ (let ((handles (process-get proc :eshell-handles))
+ (data (process-get proc :eshell-pending)))
+ (process-put proc :eshell-pending nil)
+ (process-put proc :eshell-busy t)
+ (unwind-protect
+ (condition-case nil
+ (eshell-output-object data nil handles)
+ ;; FIXME: We want to send SIGPIPE to the process
+ ;; here. However, remote processes don't currently
+ ;; support that, and not all systems have SIGPIPE in
+ ;; the first place (e.g. MS Windows). In these
+ ;; cases, just delete the process; this is
+ ;; reasonably close to the right behavior, since the
+ ;; default action for SIGPIPE is to terminate the
+ ;; process. For use cases where SIGPIPE is truly
+ ;; needed, using an external pipe operator (`*|')
+ ;; may work instead (e.g. when working with remote
+ ;; processes).
+ (eshell-pipe-broken
+ (if (or (process-get proc 'remote-pid)
+ (eq system-type 'windows-nt))
+ (delete-process proc)
+ (signal-process proc 'SIGPIPE))))
+ (process-put proc :eshell-busy nil))))))))
(defun eshell-sentinel (proc string)
"Generic sentinel for command processes. Reports only signals.
@@ -400,37 +411,34 @@ PROC is the process that's exiting. STRING is the exit message."
(when (buffer-live-p (process-buffer proc))
(with-current-buffer (process-buffer proc)
(unwind-protect
- (when-let ((entry (assq proc eshell-process-list)))
- (unwind-protect
- (unless (string= string "run")
- ;; Write the exit message if the status is
- ;; abnormal and the process is already writing
- ;; to the terminal.
- (when (and (eq proc (eshell-tail-process))
- (not (string-match "^\\(finished\\|exited\\)"
- string)))
- (funcall (process-filter proc) proc string))
- (let ((handles (nth 1 entry))
- (str (prog1 (nth 3 entry)
- (setf (nth 3 entry) nil)))
- (status (process-exit-status proc)))
- ;; If we're in the middle of handling output
- ;; from this process then schedule the EOF for
- ;; later.
- (letrec ((finish-io
- (lambda ()
- (if (nth 4 entry)
- (run-at-time 0 nil finish-io)
- (when str
- (ignore-error 'eshell-pipe-broken
- (eshell-output-object
- str nil handles)))
- (eshell-close-handles
- status (list 'quote (= status 0))
- handles)))))
- (funcall finish-io))))
- (eshell-remove-process-entry entry)))
- (eshell-kill-process-function proc string)))))
+ (unless (string= string "run")
+ ;; Write the exit message if the status is abnormal and
+ ;; the process is already writing to the terminal.
+ (when (and (eq proc (eshell-tail-process))
+ (not (string-match "^\\(finished\\|exited\\)"
+ string)))
+ (funcall (process-filter proc) proc string))
+ (let ((handles (process-get proc :eshell-handles))
+ (data (process-get proc :eshell-pending))
+ (status (process-exit-status proc)))
+ (process-put proc :eshell-pending nil)
+ ;; If we're in the middle of handling output from this
+ ;; process then schedule the EOF for later.
+ (letrec ((finish-io
+ (lambda ()
+ (if (process-get proc :eshell-busy)
+ (run-at-time 0 nil finish-io)
+ (when data
+ (ignore-error 'eshell-pipe-broken
+ (eshell-output-object
+ data nil handles)))
+ (eshell-close-handles
+ status (list 'quote (= status 0))
+ handles)))))
+ (funcall finish-io))))
+ (when-let ((entry (assq proc eshell-process-list)))
+ (eshell-remove-process-entry entry))
+ (eshell-kill-process-function proc string)))))
(defun eshell-process-interact (func &optional all query)
"Interact with a process, using PROMPT if more than one, via FUNC.
@@ -441,7 +449,7 @@ If QUERY is non-nil, query the user with QUERY before calling FUNC."
(if (and (memq (process-status (car entry))
'(run stop open closed))
(or all
- (not (nth 2 entry)))
+ (not (cdr entry)))
(or (not query)
(y-or-n-p (format-message query
(process-name (car entry))))))