summaryrefslogtreecommitdiff
path: root/lisp/eshell/esh-cmd.el
diff options
context:
space:
mode:
authorJim Porter <jporterbugs@gmail.com>2022-01-20 14:37:54 +0100
committerLars Ingebrigtsen <larsi@gnus.org>2022-01-20 14:37:54 +0100
commit4450c8bdd93d1b2e7f276e26be2cc37372034c22 (patch)
treee45604026d24ea26a1bb7ec342cc6d72edbce505 /lisp/eshell/esh-cmd.el
parent55c1670bc52c924d80c72e55bf3864023749be29 (diff)
downloademacs-4450c8bdd93d1b2e7f276e26be2cc37372034c22.tar.gz
emacs-4450c8bdd93d1b2e7f276e26be2cc37372034c22.tar.bz2
emacs-4450c8bdd93d1b2e7f276e26be2cc37372034c22.zip
Consider subcommands when deciding to invoke Eshell command directly
When an Eshell command contains an asynchronous subcommand (such as calling an external process), it must be evaluated iteratively. See bug#30725. * lisp/eshell/esh-cmd.el (eshell-invoke-command): Move most of the logic from here... (eshell--invoke-command-directly): ... to here. Also add checks for subcommands. * test/lisp/eshell/eshell-tests.el (eshell-test--max-subprocess-time): New variable. (eshell-wait-for-subprocess): New function. (eshell-command-result-p): Use 'eshell-wait-for-subprocess'. (eshell-test/interp-cmd-external): New test (bug#30725).
Diffstat (limited to 'lisp/eshell/esh-cmd.el')
-rw-r--r--lisp/eshell/esh-cmd.el57
1 files changed, 43 insertions, 14 deletions
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index a2d7d9431a9..25e3a5a2054 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -903,21 +903,50 @@ at the moment are:
"Completion for the `debug' command."
(while (pcomplete-here '("errors" "commands"))))
+(defun eshell--invoke-command-directly (command)
+ "Determine whether the given COMMAND can be invoked directly.
+COMMAND should be a non-top-level Eshell command in parsed form.
+
+A command can be invoked directly if all of the following are true:
+
+* The command is of the form
+ \"(eshell-trap-errors (eshell-named-command NAME ARGS))\",
+ where ARGS is optional.
+
+* NAME is a string referring to an alias function and isn't a
+ complex command (see `eshell-complex-commands').
+
+* Any argument in ARGS that calls a subcommand can also be
+ invoked directly."
+ (when (and (eq (car command) 'eshell-trap-errors)
+ (eq (car (cadr command)) 'eshell-named-command))
+ (let ((name (cadr (cadr command)))
+ (args (cdr-safe (nth 2 (cadr command)))))
+ (and name (stringp name)
+ (not (member name eshell-complex-commands))
+ (catch 'simple
+ (dolist (pred eshell-complex-commands t)
+ (when (and (functionp pred)
+ (funcall pred name))
+ (throw 'simple nil))))
+ (eshell-find-alias-function name)
+ (catch 'indirect-subcommand
+ (dolist (arg args t)
+ (pcase arg
+ (`(eshell-escape-arg
+ (let ,_
+ (eshell-convert
+ (eshell-command-to-value
+ (eshell-as-subcommand ,subcommand)))))
+ (unless (eshell--invoke-command-directly subcommand)
+ (throw 'indirect-subcommand nil))))))))))
+
(defun eshell-invoke-directly (command)
- (let ((base (cadr (nth 2 (nth 2 (cadr command))))) name)
- (if (and (eq (car base) 'eshell-trap-errors)
- (eq (car (cadr base)) 'eshell-named-command))
- (setq name (cadr (cadr base))))
- (and name (stringp name)
- (not (member name eshell-complex-commands))
- (catch 'simple
- (progn
- (dolist (pred eshell-complex-commands)
- (if (and (functionp pred)
- (funcall pred name))
- (throw 'simple nil)))
- t))
- (eshell-find-alias-function name))))
+ "Determine whether the given COMMAND can be invoked directly.
+COMMAND should be a top-level Eshell command in parsed form, as
+produced by `eshell-parse-command'."
+ (let ((base (cadr (nth 2 (nth 2 (cadr command))))))
+ (eshell--invoke-command-directly base)))
(defun eshell-eval-command (command &optional input)
"Evaluate the given COMMAND iteratively."