From bed83773f46c5fc72e9694139ddbc20ba0990c62 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Thu, 30 Mar 2023 19:31:30 -0700 Subject: [PATCH 1/2] Fix using background commands in 'eshell-command' This regressed due to the patch for bug#53715, which changed how Eshell pipelines return the processes in the pipeline. * lisp/eshell/esh-cmd.el (eshell-parse-command): When creating background commands, wrap the process(es) in a cons cell whose CAR is ':eshell-background'. This lets us use fewer heuristics... (eshell-eval-command): ... here. Additionally, keep the result and the incomplete delimiter separate. * lisp/eshell/eshell.el (eshell-command): Check ':eshell-background' and use a more-robust method for setting the output target. * test/lisp/eshell/eshell-tests.el (eshell-test/eshell-command/simple) (eshell-test/eshell-command/pipeline) (eshell-test/eshell-command/background) (eshell-test/eshell-command/background-pipeline): New tests. --- lisp/eshell/esh-cmd.el | 30 ++++++++++------------ lisp/eshell/eshell.el | 21 +++++---------- test/lisp/eshell/eshell-tests.el | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 30 deletions(-) diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index d5237ee1f04..600a2c3af0b 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -421,7 +421,8 @@ eshell-parse-command (string= (car eshell--sep-terms) ";")) (eshell-parse-pipeline cmd) `(eshell-do-subjob - (list ,(eshell-parse-pipeline cmd))))) + (cons :eshell-background + ,(eshell-parse-pipeline cmd))))) (setq eshell--sep-terms (cdr eshell--sep-terms)) (if eshell-in-pipeline-p cmd @@ -1030,7 +1031,12 @@ eshell-eval-argument (cadr result))) (defun eshell-eval-command (command &optional input) - "Evaluate the given COMMAND iteratively." + "Evaluate the given COMMAND iteratively. +Return the process (or head and tail processes) created by +COMMAND, if any. If COMMAND is a background command, return the +process(es) in a cons cell like: + + (:eshell-background . PROCESS)" (if eshell-current-command ;; We can just stick the new command at the end of the current ;; one, and everything will happen as it should. @@ -1046,20 +1052,12 @@ eshell-eval-command (erase-buffer) (insert "command: \"" input "\"\n"))) (setq eshell-current-command command) - (let* ((delim (catch 'eshell-incomplete - (eshell-resume-eval))) - (val (car-safe delim))) - ;; If the return value of `eshell-resume-eval' is wrapped in a - ;; list, it indicates that the command was run asynchronously. - ;; In that case, unwrap the value before checking the delimiter - ;; value. - (if (and val - (not (eshell-processp val)) - (not (eq val t))) - (error "Unmatched delimiter: %S" val) - ;; Eshell-command expect a list like () to know if the - ;; command should be async or not. - (or (and (eshell-processp val) delim) val))))) + (let* (result + (delim (catch 'eshell-incomplete + (ignore (setq result (eshell-resume-eval)))))) + (when delim + (error "Unmatched delimiter: %S" delim)) + result))) (defun eshell-resume-command (proc status) "Resume the current command when a process ends." diff --git a/lisp/eshell/eshell.el b/lisp/eshell/eshell.el index 7d2c0335db2..b71f283bf9f 100644 --- a/lisp/eshell/eshell.el +++ b/lisp/eshell/eshell.el @@ -290,25 +290,18 @@ eshell-command (eshell-add-input-to-history command))))) (unless command (error "No command specified!")) - ;; redirection into the current buffer is achieved by adding an - ;; output redirection to the end of the command, of the form - ;; 'COMMAND >>> #'. This will not interfere with - ;; other redirections, since multiple redirections merely cause the - ;; output to be copied to multiple target locations - (if arg - (setq command - (concat command - (format " >>> #" - (buffer-name (current-buffer)))))) (save-excursion - (let ((buf (set-buffer (generate-new-buffer " *eshell cmd*"))) + (let ((stdout (if arg (current-buffer) t)) + (buf (set-buffer (generate-new-buffer " *eshell cmd*"))) (eshell-non-interactive-p t)) (eshell-mode) (let* ((proc (eshell-eval-command - (list 'eshell-commands - (eshell-parse-command command)))) + `(let ((eshell-current-handles + (eshell-create-handles ,stdout 'insert)) + (eshell-current-subjob-p)) + ,(eshell-parse-command command)))) intr - (bufname (if (and proc (listp proc)) + (bufname (if (eq (car-safe proc) :eshell-background) "*Eshell Async Command Output*" (setq intr t) "*Eshell Command Output*"))) diff --git a/test/lisp/eshell/eshell-tests.el b/test/lisp/eshell/eshell-tests.el index 743cc28b9b5..390f75cfbb9 100644 --- a/test/lisp/eshell/eshell-tests.el +++ b/test/lisp/eshell/eshell-tests.el @@ -107,6 +107,50 @@ eshell-test/lisp-reset-in-pipeline (format template "format \"%s\" eshell-in-pipeline-p") "nil"))) +(ert-deftest eshell-test/eshell-command/simple () + "Test that the `eshell-command' function writes to the current buffer." + (skip-unless (executable-find "echo")) + (ert-with-temp-directory eshell-directory-name + (let ((eshell-history-file-name nil)) + (with-temp-buffer + (eshell-command "*echo hi" t) + (should (equal (buffer-string) "hi\n")))))) + +(ert-deftest eshell-test/eshell-command/pipeline () + "Test that the `eshell-command' function writes to the current buffer. +This test uses a pipeline for the command." + (skip-unless (and (executable-find "echo") + (executable-find "cat"))) + (ert-with-temp-directory eshell-directory-name + (let ((eshell-history-file-name nil)) + (with-temp-buffer + (eshell-command "*echo hi | *cat" t) + (should (equal (buffer-string) "hi\n")))))) + +(ert-deftest eshell-test/eshell-command/background () + "Test that `eshell-command' works for background commands." + (skip-unless (executable-find "echo")) + (ert-with-temp-directory eshell-directory-name + (let ((orig-processes (process-list)) + (eshell-history-file-name nil)) + (with-temp-buffer + (eshell-command "*echo hi &" t) + (eshell-wait-for (lambda () (equal (process-list) orig-processes))) + (should (equal (buffer-string) "hi\n")))))) + +(ert-deftest eshell-test/eshell-command/background-pipeline () + "Test that `eshell-command' works for background commands. +This test uses a pipeline for the command." + (skip-unless (and (executable-find "echo") + (executable-find "cat"))) + (ert-with-temp-directory eshell-directory-name + (let ((orig-processes (copy-tree (process-list))) + (eshell-history-file-name nil)) + (with-temp-buffer + (eshell-command "*echo hi | *cat &" t) + (eshell-wait-for (lambda () (equal (process-list) orig-processes))) + (should (equal (buffer-string) "hi\n")))))) + (ert-deftest eshell-test/command-running-p () "Modeline should show no command running" (with-temp-eshell -- 2.25.1