diff options
author | Jim Porter <jporterbugs@gmail.com> | 2022-08-28 11:19:30 -0700 |
---|---|---|
committer | Jim Porter <jporterbugs@gmail.com> | 2022-09-04 15:15:01 -0700 |
commit | a87c7aff554213651678e9390dd7500b11419012 (patch) | |
tree | 50a130c8a4e8374e81dc0c8a4f53b8afd629effc /lisp/eshell | |
parent | ab7e94fb1d9b794c9d199435d72f569fba6ab017 (diff) | |
download | emacs-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.el | 144 |
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)))))) |