aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Porter2024-01-26 10:17:19 -0800
committerJim Porter2024-01-26 10:17:19 -0800
commit047607f6e611709f89f6c93ae0e2fc97b25bf18f (patch)
treebd8f9d4d810fe627bb0b6f1c0956ae7585806bc2
parentde020255a5cef4349d786fceb19481352c49557b (diff)
downloademacs-047607f6e611709f89f6c93ae0e2fc97b25bf18f.tar.gz
emacs-047607f6e611709f89f6c93ae0e2fc97b25bf18f.zip
Fix detection of directly-invokable commands in Eshell
I think this regressed partly due to eef32d13da58, so let's add some regression tests to make sure that doesn't happen again. * lisp/eshell/em-unix.el (eshell-unix-initialize): Add "compile". * lisp/eshell/esh-cmd.el (eshell--find-subcommands): Yield the second element of the subcommand. (eshell--invoke-command-directly-p): Rename and account for 'eshell-with-copied-handles'. (eshell-invoke-directly): Rename to... (eshell-invoke-directly-p): ... this, and use 'pcase' to make the logic clearer. * lisp/eshell/esh-mode.el (eshell-send-input): Always queue input if the process is running; rename some locals to be clearer. * lisp/eshell/esh-var.el (eshell-var-initialize): Add "env" as a complex command. * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test--deftest-invoke-directly): New macro. (no-args, with-args, multiple-cmds, subcmd, complex, complex-subcmd): New test cases.
-rw-r--r--lisp/eshell/em-unix.el6
-rw-r--r--lisp/eshell/esh-cmd.el54
-rw-r--r--lisp/eshell/esh-mode.el17
-rw-r--r--lisp/eshell/esh-var.el2
-rw-r--r--test/lisp/eshell/esh-cmd-tests.el20
5 files changed, 63 insertions, 36 deletions
diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el
index b066e9eeb8e..dad02206759 100644
--- a/lisp/eshell/em-unix.el
+++ b/lisp/eshell/em-unix.el
@@ -166,9 +166,9 @@ Otherwise, Emacs will attempt to use rsh to invoke du on the remote machine."
166 (add-hook 'pcomplete-try-first-hook 166 (add-hook 'pcomplete-try-first-hook
167 'eshell-complete-host-reference nil t)) 167 'eshell-complete-host-reference nil t))
168 (setq-local eshell-complex-commands 168 (setq-local eshell-complex-commands
169 (append '("grep" "egrep" "fgrep" "agrep" "rgrep" 169 (append '("compile" "grep" "egrep" "fgrep" "agrep"
170 "glimpse" "locate" "cat" "time" "cp" "mv" 170 "rgrep" "glimpse" "locate" "cat" "time" "cp"
171 "make" "du" "diff") 171 "mv" "make" "du" "diff")
172 eshell-complex-commands))) 172 eshell-complex-commands)))
173 173
174(defalias 'eshell/date 'current-time-string) 174(defalias 'eshell/date 'current-time-string)
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index 2746800ea78..30494bafb48 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -934,48 +934,52 @@ This yields the SUBCOMMANDs when found in forms like
934 (dolist (elem haystack) 934 (dolist (elem haystack)
935 (cond 935 (cond
936 ((eq (car-safe elem) 'eshell-as-subcommand) 936 ((eq (car-safe elem) 'eshell-as-subcommand)
937 (iter-yield (cdr elem))) 937 (iter-yield (cadr elem)))
938 ((listp elem) 938 ((listp elem)
939 (iter-yield-from (eshell--find-subcommands elem)))))) 939 (iter-yield-from (eshell--find-subcommands elem))))))
940 940
941(defun eshell--invoke-command-directly (command) 941(defun eshell--invoke-command-directly-p (command)
942 "Determine whether the given COMMAND can be invoked directly. 942 "Determine whether the given COMMAND can be invoked directly.
943COMMAND should be a non-top-level Eshell command in parsed form. 943COMMAND should be a non-top-level Eshell command in parsed form.
944 944
945A command can be invoked directly if all of the following are true: 945A command can be invoked directly if all of the following are true:
946 946
947* The command is of the form 947* The command is of the form
948 \"(eshell-trap-errors (eshell-named-command NAME ARGS))\", 948 (eshell-with-copied-handles
949 where ARGS is optional. 949 (eshell-trap-errors (eshell-named-command NAME [ARGS])) _).
950 950
951* NAME is a string referring to an alias function and isn't a 951* NAME is a string referring to an alias function and isn't a
952 complex command (see `eshell-complex-commands'). 952 complex command (see `eshell-complex-commands').
953 953
954* Any subcommands in ARGS can also be invoked directly." 954* Any subcommands in ARGS can also be invoked directly."
955 (when (and (eq (car command) 'eshell-trap-errors) 955 (pcase command
956 (eq (car (cadr command)) 'eshell-named-command)) 956 (`(eshell-with-copied-handles
957 (let ((name (cadr (cadr command))) 957 (eshell-trap-errors (eshell-named-command ,name . ,args))
958 (args (cdr-safe (nth 2 (cadr command))))) 958 ,_)
959 (and name (stringp name) 959 (and name (stringp name)
960 (not (member name eshell-complex-commands)) 960 (not (member name eshell-complex-commands))
961 (catch 'simple 961 (catch 'simple
962 (dolist (pred eshell-complex-commands t) 962 (dolist (pred eshell-complex-commands t)
963 (when (and (functionp pred) 963 (when (and (functionp pred)
964 (funcall pred name)) 964 (funcall pred name))
965 (throw 'simple nil)))) 965 (throw 'simple nil))))
966 (eshell-find-alias-function name) 966 (eshell-find-alias-function name)
967 (catch 'indirect-subcommand 967 (catch 'indirect-subcommand
968 (iter-do (subcommand (eshell--find-subcommands args)) 968 (iter-do (subcommand (eshell--find-subcommands (car args)))
969 (unless (eshell--invoke-command-directly subcommand) 969 (unless (eshell--invoke-command-directly-p subcommand)
970 (throw 'indirect-subcommand nil))) 970 (throw 'indirect-subcommand nil)))
971 t))))) 971 t)))))
972 972
973(defun eshell-invoke-directly (command) 973(defun eshell-invoke-directly-p (command)
974 "Determine whether the given COMMAND can be invoked directly. 974 "Determine whether the given COMMAND can be invoked directly.
975COMMAND should be a top-level Eshell command in parsed form, as 975COMMAND should be a top-level Eshell command in parsed form, as
976produced by `eshell-parse-command'." 976produced by `eshell-parse-command'."
977 (let ((base (cadr (nth 2 (nth 2 (cadr command)))))) 977 (pcase command
978 (eshell--invoke-command-directly base))) 978 (`(eshell-commands (progn ,_ (unwind-protect (progn ,base) . ,_)))
979 (eshell--invoke-command-directly-p base))))
980
981(define-obsolete-function-alias 'eshell-invoke-directly
982 'eshell-invoke-directly-p "30.1")
979 983
980(defun eshell-eval-argument (argument) 984(defun eshell-eval-argument (argument)
981 "Evaluate a single Eshell ARGUMENT and return the result." 985 "Evaluate a single Eshell ARGUMENT and return the result."
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el
index 21e3f00086f..fd279f61673 100644
--- a/lisp/eshell/esh-mode.el
+++ b/lisp/eshell/esh-mode.el
@@ -619,14 +619,14 @@ If NO-NEWLINE is non-nil, the input is sent without an implied final
619newline." 619newline."
620 (interactive "P") 620 (interactive "P")
621 ;; Note that the input string does not include its terminal newline. 621 ;; Note that the input string does not include its terminal newline.
622 (let ((proc-running-p (and (eshell-head-process) 622 (let* ((proc-running-p (eshell-head-process))
623 (not queue-p))) 623 (send-to-process-p (and proc-running-p (not queue-p)))
624 (inhibit-modification-hooks t)) 624 (inhibit-modification-hooks t))
625 (unless (and proc-running-p 625 (unless (and send-to-process-p
626 (not (eq (process-status 626 (not (eq (process-status
627 (eshell-head-process)) 627 (eshell-head-process))
628 'run))) 628 'run)))
629 (if (or proc-running-p 629 (if (or send-to-process-p
630 (>= (point) eshell-last-output-end)) 630 (>= (point) eshell-last-output-end))
631 (goto-char (point-max)) 631 (goto-char (point-max))
632 (let ((copy (eshell-get-old-input use-region))) 632 (let ((copy (eshell-get-old-input use-region)))
@@ -634,7 +634,7 @@ newline."
634 (insert-and-inherit copy))) 634 (insert-and-inherit copy)))
635 (unless (or no-newline 635 (unless (or no-newline
636 (and eshell-send-direct-to-subprocesses 636 (and eshell-send-direct-to-subprocesses
637 proc-running-p)) 637 send-to-process-p))
638 (insert-before-markers-and-inherit ?\n)) 638 (insert-before-markers-and-inherit ?\n))
639 ;; Delete and reinsert input. This seems like a no-op, except 639 ;; Delete and reinsert input. This seems like a no-op, except
640 ;; for the resulting entries in the undo list: undoing this 640 ;; for the resulting entries in the undo list: undoing this
@@ -644,7 +644,7 @@ newline."
644 (inhibit-read-only t)) 644 (inhibit-read-only t))
645 (delete-region eshell-last-output-end (point)) 645 (delete-region eshell-last-output-end (point))
646 (insert text)) 646 (insert text))
647 (if proc-running-p 647 (if send-to-process-p
648 (progn 648 (progn
649 (eshell-update-markers eshell-last-output-end) 649 (eshell-update-markers eshell-last-output-end)
650 (if (or eshell-send-direct-to-subprocesses 650 (if (or eshell-send-direct-to-subprocesses
@@ -673,7 +673,8 @@ newline."
673 (run-hooks 'eshell-input-filter-functions) 673 (run-hooks 'eshell-input-filter-functions)
674 (and (catch 'eshell-terminal 674 (and (catch 'eshell-terminal
675 (ignore 675 (ignore
676 (if (eshell-invoke-directly cmd) 676 (if (and (not proc-running-p)
677 (eshell-invoke-directly-p cmd))
677 (eval cmd) 678 (eval cmd)
678 (eshell-eval-command cmd input)))) 679 (eshell-eval-command cmd input))))
679 (eshell-life-is-too-much))))) 680 (eshell-life-is-too-much)))))
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index 627cbb17797..537bc4b0641 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -287,6 +287,8 @@ This is set to t in `eshell-local-variable-bindings' (which see).")
287 (setq-local eshell-subcommand-bindings 287 (setq-local eshell-subcommand-bindings
288 (append eshell-local-variable-bindings 288 (append eshell-local-variable-bindings
289 eshell-subcommand-bindings)) 289 eshell-subcommand-bindings))
290 (setq-local eshell-complex-commands
291 (append '("env") eshell-complex-commands))
290 292
291 (setq-local eshell-special-chars-inside-quoting 293 (setq-local eshell-special-chars-inside-quoting
292 (append eshell-special-chars-inside-quoting '(?$))) 294 (append eshell-special-chars-inside-quoting '(?$)))
diff --git a/test/lisp/eshell/esh-cmd-tests.el b/test/lisp/eshell/esh-cmd-tests.el
index be31681267b..c37e6d14187 100644
--- a/test/lisp/eshell/esh-cmd-tests.el
+++ b/test/lisp/eshell/esh-cmd-tests.el
@@ -469,6 +469,26 @@ This tests when `eshell-lisp-form-nil-is-failure' is nil."
469 "no")) 469 "no"))
470 470
471 471
472;; Direct invocation
473
474(defmacro esh-cmd-test--deftest-invoke-directly (name command expected)
475 "FIXME"
476 (declare (indent 2))
477 `(ert-deftest ,(intern (concat "esh-cmd-test/invoke-directly/"
478 (symbol-name name))) ()
479 (with-temp-eshell
480 (should (equal (eshell-invoke-directly
481 (eshell-parse-command ,command nil t))
482 ,expected)))))
483
484(esh-cmd-test--deftest-invoke-directly no-args "echo" t)
485(esh-cmd-test--deftest-invoke-directly with-args "echo hi" t)
486(esh-cmd-test--deftest-invoke-directly multiple-cmds "echo hi; echo bye" nil)
487(esh-cmd-test--deftest-invoke-directly subcmd "echo ${echo hi}" t)
488(esh-cmd-test--deftest-invoke-directly complex "ls ." nil)
489(esh-cmd-test--deftest-invoke-directly complex-subcmd "echo {ls .}" nil)
490
491
472;; Error handling 492;; Error handling
473 493
474(ert-deftest esh-cmd-test/throw () 494(ert-deftest esh-cmd-test/throw ()