diff options
| author | Jim Porter | 2023-04-02 22:41:29 -0700 |
|---|---|---|
| committer | Jim Porter | 2023-09-10 10:38:25 -0700 |
| commit | f9667836c4b193d02a375350f4a16e0fe8d8b4f2 (patch) | |
| tree | 2765a4ae2ba4933e565255a464683cc560ac76e9 | |
| parent | dd2438eeaa6a2420383a5e783c7a2d178c64c590 (diff) | |
| download | emacs-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.el | 109 | ||||
| -rw-r--r-- | lisp/eshell/esh-util.el | 23 |
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). |
| 278 | When executing a pipeline, this is a cons cell whose CAR is the | 278 | When executing a pipeline, this is a list of all the pipeline's |
| 279 | first process (usually reading from stdin) and whose CDR is the | 279 | processes, with the first usually reading from stdin and last |
| 280 | last process (usually writing to stdout). Otherwise, the CAR and | 280 | usually writing to stdout.") |
| 281 | CDR are the same process. | ||
| 282 | |||
| 283 | When 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. |
| 304 | This only returns external (non-Lisp) processes." | 301 | This 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. |
| 309 | This only returns external (non-Lisp) processes." | 306 | This 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. |
| 806 | Returns a list of the processes in the pipeline. | ||
| 807 | |||
| 809 | This macro calls itself recursively, with NOTFIRST non-nil." | 808 | This 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. |
| 835 | Output of each command is passed as input to the next one in the pipeline. | 833 | This collects the output of each command in turn, passing it as |
| 836 | This is used on systems where async subprocesses are not supported." | 834 | input to the next one in the pipeline. Returns the result of the |
| 835 | first command invocation in the pipeline (usually t or nil). | ||
| 836 | |||
| 837 | This is used on systems where async subprocesses are not | ||
| 838 | supported." | ||
| 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. | 740 | PROCS can be a single process or a list thereof. If PROCS is |
| 741 | This represents the head and tail of a pipeline of processes, | 741 | anything else, return nil instead." |
| 742 | where 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) |