aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Porter2023-04-02 22:41:29 -0700
committerJim Porter2023-09-10 10:38:25 -0700
commitf9667836c4b193d02a375350f4a16e0fe8d8b4f2 (patch)
tree2765a4ae2ba4933e565255a464683cc560ac76e9
parentdd2438eeaa6a2420383a5e783c7a2d178c64c590 (diff)
downloademacs-f9667836c4b193d02a375350f4a16e0fe8d8b4f2.tar.gz
emacs-f9667836c4b193d02a375350f4a16e0fe8d8b4f2.zip
Collect all processes in an Eshell pipeline, not just the head and tail
This has the extra benefit that Eshell now only considers a pipeline to be done when *all* of its processes are done (previously, it checked only the last one in the pipeline). * lisp/eshell/esh-util.el (eshell-process-pair-p) (eshell-make-process-pair): Rename to... (eshell-process-list-p, eshell-make-process-list): ... these, and handle lists of processes. Update callers. * lisp/eshell/esh-cmd.el (eshell-head-process): Use 'car'. (eshell-tail-process): Get the last element of the list. (eshell-do-pipelines): Return a list of all processes in the pipeline. (eshell-do-pipelines-synchronously): Return the result of the first command (usually t or nil). (eshell-execute-pipeline): Simplify. (eshell-do-eval): Pass all processes to 'eshell/wait'.
-rw-r--r--lisp/eshell/esh-cmd.el109
-rw-r--r--lisp/eshell/esh-util.el23
2 files changed, 62 insertions, 70 deletions
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index 45176b332d8..a67b8abcc67 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -275,12 +275,9 @@ otherwise t.")
275(defvar eshell-last-command-name nil) 275(defvar eshell-last-command-name nil)
276(defvar eshell-last-async-procs nil 276(defvar eshell-last-async-procs nil
277 "The currently-running foreground process(es). 277 "The currently-running foreground process(es).
278When executing a pipeline, this is a cons cell whose CAR is the 278When executing a pipeline, this is a list of all the pipeline's
279first process (usually reading from stdin) and whose CDR is the 279processes, with the first usually reading from stdin and last
280last process (usually writing to stdout). Otherwise, the CAR and 280usually writing to stdout.")
281CDR are the same process.
282
283When the process in the CDR completes, resume command evaluation.")
284 281
285(defvar eshell-allow-commands t 282(defvar eshell-allow-commands t
286 "If non-nil, allow evaluating command forms (including Lisp forms). 283 "If non-nil, allow evaluating command forms (including Lisp forms).
@@ -302,12 +299,12 @@ also `eshell-complete-parse-arguments'.")
302(defsubst eshell-head-process () 299(defsubst eshell-head-process ()
303 "Return the currently running process at the head of any pipeline. 300 "Return the currently running process at the head of any pipeline.
304This only returns external (non-Lisp) processes." 301This only returns external (non-Lisp) processes."
305 (car-safe eshell-last-async-procs)) 302 (car eshell-last-async-procs))
306 303
307(defsubst eshell-tail-process () 304(defsubst eshell-tail-process ()
308 "Return the currently running process at the tail of any pipeline. 305 "Return the currently running process at the tail of any pipeline.
309This only returns external (non-Lisp) processes." 306This only returns external (non-Lisp) processes."
310 (cdr-safe eshell-last-async-procs)) 307 (car (last eshell-last-async-procs)))
311 308
312(define-obsolete-function-alias 'eshell-interactive-process 309(define-obsolete-function-alias 'eshell-interactive-process
313 'eshell-tail-process "29.1") 310 'eshell-tail-process "29.1")
@@ -806,54 +803,56 @@ that Eshell doesn't erroneously allow deferring it. For example,
806 803
807(defmacro eshell-do-pipelines (pipeline &optional notfirst) 804(defmacro eshell-do-pipelines (pipeline &optional notfirst)
808 "Execute the commands in PIPELINE, connecting each to one another. 805 "Execute the commands in PIPELINE, connecting each to one another.
806Returns a list of the processes in the pipeline.
807
809This macro calls itself recursively, with NOTFIRST non-nil." 808This macro calls itself recursively, with NOTFIRST non-nil."
810 (when (setq pipeline (cadr pipeline)) 809 (when (setq pipeline (cadr pipeline))
811 (eshell--unmark-deferrable (car pipeline)) 810 (eshell--unmark-deferrable (car pipeline))
812 `(eshell-with-copied-handles 811 `(eshell-with-copied-handles
813 (progn 812 (let ((next-procs
814 ,(when (cdr pipeline) 813 ,(when (cdr pipeline)
815 `(let ((nextproc 814 `(eshell-do-pipelines (quote ,(cdr pipeline)) t)))
816 (eshell-do-pipelines (quote ,(cdr pipeline)) t))) 815 ;; First and last elements in a pipeline may need special
817 (eshell-set-output-handle ,eshell-output-handle 816 ;; treatment (currently only `eshell-ls-files' uses
818 'append nextproc))) 817 ;; `last'). Affects `process-connection-type' in
819 ;; First and last elements in a pipeline may need special treatment. 818 ;; `eshell-gather-process-output'.
820 ;; (Currently only eshell-ls-files uses 'last.) 819 (eshell-in-pipeline-p
821 ;; Affects process-connection-type in eshell-gather-process-output. 820 ,(cond ((not notfirst) (quote 'first))
822 (let ((eshell-in-pipeline-p 821 ((cdr pipeline) t)
823 ,(cond ((not notfirst) (quote 'first)) 822 (t (quote 'last)))))
824 ((cdr pipeline) t) 823 ,(when (cdr pipeline)
825 (t (quote 'last))))) 824 `(eshell-set-output-handle ,eshell-output-handle
826 (let ((proc ,(car pipeline))) 825 'append (car next-procs)))
827 (set headproc (or proc (symbol-value headproc))) 826 (let ((proc ,(car pipeline)))
828 (set tailproc (or (symbol-value tailproc) proc)) 827 (cons proc next-procs)))
829 proc)))
830 ;; Steal handles if this is the last item in the pipeline. 828 ;; Steal handles if this is the last item in the pipeline.
831 ,(null (cdr pipeline))))) 829 ,(null (cdr pipeline)))))
832 830
833(defmacro eshell-do-pipelines-synchronously (pipeline) 831(defmacro eshell-do-pipelines-synchronously (pipeline)
834 "Execute the commands in PIPELINE in sequence synchronously. 832 "Execute the commands in PIPELINE in sequence synchronously.
835Output of each command is passed as input to the next one in the pipeline. 833This collects the output of each command in turn, passing it as
836This is used on systems where async subprocesses are not supported." 834input to the next one in the pipeline. Returns the result of the
835first command invocation in the pipeline (usually t or nil).
836
837This is used on systems where async subprocesses are not
838supported."
837 (when (setq pipeline (cadr pipeline)) 839 (when (setq pipeline (cadr pipeline))
838 ;; FIXME: is deferrable significant here? 840 ;; FIXME: is deferrable significant here?
839 (eshell--unmark-deferrable (car pipeline)) 841 (eshell--unmark-deferrable (car pipeline))
840 `(progn 842 `(prog1
841 (eshell-with-copied-handles 843 (eshell-with-copied-handles
842 (progn 844 (progn
843 ,(when (cdr pipeline) 845 ,(when (cdr pipeline)
844 `(let ((output-marker ,(point-marker))) 846 `(let ((output-marker ,(point-marker)))
845 (eshell-set-output-handle ,eshell-output-handle 847 (eshell-set-output-handle ,eshell-output-handle
846 'append output-marker))) 848 'append output-marker)))
847 (let (;; XXX: `eshell-in-pipeline-p' has a different meaning 849 (let (;; XXX: `eshell-in-pipeline-p' has a different
848 ;; for synchronous processes: it's non-nil only when 850 ;; meaning for synchronous processes: it's non-nil
849 ;; piping *to* a process. 851 ;; only when piping *to* a process.
850 (eshell-in-pipeline-p ,(and (cdr pipeline) t))) 852 (eshell-in-pipeline-p ,(and (cdr pipeline) t)))
851 (let ((result ,(car pipeline))) 853 ,(car pipeline)))
852 ;; `tailproc' gets the result of the last successful 854 ;; Steal handles if this is the last item in the pipeline.
853 ;; process in the pipeline. 855 ,(null (cdr pipeline)))
854 (set tailproc (or result (symbol-value tailproc))))))
855 ;; Steal handles if this is the last item in the pipeline.
856 ,(null (cdr pipeline)))
857 ,(when (cdr pipeline) 856 ,(when (cdr pipeline)
858 `(eshell-do-pipelines-synchronously (quote ,(cdr pipeline))))))) 857 `(eshell-do-pipelines-synchronously (quote ,(cdr pipeline)))))))
859 858
@@ -861,16 +860,10 @@ This is used on systems where async subprocesses are not supported."
861 860
862(defmacro eshell-execute-pipeline (pipeline) 861(defmacro eshell-execute-pipeline (pipeline)
863 "Execute the commands in PIPELINE, connecting each to one another." 862 "Execute the commands in PIPELINE, connecting each to one another."
864 `(let ((headproc (make-symbol "headproc")) 863 `(eshell-process-identity
865 (tailproc (make-symbol "tailproc"))) 864 ,(if eshell-supports-asynchronous-processes
866 (set headproc nil) 865 `(remove nil (eshell-do-pipelines ,pipeline))
867 (set tailproc nil) 866 `(eshell-do-pipelines-synchronously ,pipeline))))
868 (progn
869 ,(if eshell-supports-asynchronous-processes
870 `(eshell-do-pipelines ,pipeline)
871 `(eshell-do-pipelines-synchronously ,pipeline))
872 (eshell-process-identity (cons (symbol-value headproc)
873 (symbol-value tailproc))))))
874 867
875(defmacro eshell-as-subcommand (command) 868(defmacro eshell-as-subcommand (command)
876 "Execute COMMAND as a subcommand. 869 "Execute COMMAND as a subcommand.
@@ -1021,7 +1014,7 @@ process(es) in a cons cell like:
1021 (setq retval 1014 (setq retval
1022 (eshell-do-eval 1015 (eshell-do-eval
1023 eshell-current-command)))))) 1016 eshell-current-command))))))
1024 (if (eshell-process-pair-p procs) 1017 (if (eshell-process-list-p procs)
1025 (ignore (setq eshell-last-async-procs procs)) 1018 (ignore (setq eshell-last-async-procs procs))
1026 (cadr retval))))) 1019 (cadr retval)))))
1027 (error 1020 (error
@@ -1228,10 +1221,10 @@ have been replaced by constants."
1228 (eshell-do-eval form synchronous-p)) 1221 (eshell-do-eval form synchronous-p))
1229 (if-let (((memq (car form) eshell-deferrable-commands)) 1222 (if-let (((memq (car form) eshell-deferrable-commands))
1230 ((not eshell-current-subjob-p)) 1223 ((not eshell-current-subjob-p))
1231 (procs (eshell-make-process-pair result))) 1224 (procs (eshell-make-process-list result)))
1232 (if synchronous-p 1225 (if synchronous-p
1233 (eshell/wait (cdr procs)) 1226 (apply #'eshell/wait procs)
1234 (eshell-manipulate form "inserting ignore form" 1227 (eshell-manipulate form "inserting ignore form"
1235 (setcar form 'ignore) 1228 (setcar form 'ignore)
1236 (setcdr form nil)) 1229 (setcdr form nil))
1237 (throw 'eshell-defer procs)) 1230 (throw 'eshell-defer procs))
diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el
index d5a75b0d715..5134adeb7fb 100644
--- a/lisp/eshell/esh-util.el
+++ b/lisp/eshell/esh-util.el
@@ -730,19 +730,18 @@ gid format. Valid values are `string' and `integer', defaulting to
730 "If the `processp' function does not exist, PROC is not a process." 730 "If the `processp' function does not exist, PROC is not a process."
731 (and (fboundp 'processp) (processp proc))) 731 (and (fboundp 'processp) (processp proc)))
732 732
733(defun eshell-process-pair-p (procs) 733(defun eshell-process-list-p (procs)
734 "Return non-nil if PROCS is a pair of process objects." 734 "Return non-nil if PROCS is a list of process objects."
735 (and (consp procs) 735 (and (listp procs)
736 (eshell-processp (car procs)) 736 (seq-every-p #'eshell-processp procs)))
737 (eshell-processp (cdr procs)))) 737
738 738(defun eshell-make-process-list (procs)
739(defun eshell-make-process-pair (procs) 739 "Make a list of process objects from PROCS if possible.
740 "Make a pair of process objects from PROCS if possible. 740PROCS can be a single process or a list thereof. If PROCS is
741This represents the head and tail of a pipeline of processes, 741anything else, return nil instead."
742where the head and tail may be the same process."
743 (pcase procs 742 (pcase procs
744 ((pred eshell-processp) (cons procs procs)) 743 ((pred eshell-processp) (list procs))
745 ((pred eshell-process-pair-p) procs))) 744 ((pred eshell-process-list-p) procs)))
746 745
747;; (defun eshell-copy-file 746;; (defun eshell-copy-file
748;; (file newname &optional ok-if-already-exists keep-date) 747;; (file newname &optional ok-if-already-exists keep-date)