diff options
| author | Jim Porter | 2024-01-26 10:17:19 -0800 |
|---|---|---|
| committer | Jim Porter | 2024-01-26 10:17:19 -0800 |
| commit | 047607f6e611709f89f6c93ae0e2fc97b25bf18f (patch) | |
| tree | bd8f9d4d810fe627bb0b6f1c0956ae7585806bc2 | |
| parent | de020255a5cef4349d786fceb19481352c49557b (diff) | |
| download | emacs-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.el | 6 | ||||
| -rw-r--r-- | lisp/eshell/esh-cmd.el | 54 | ||||
| -rw-r--r-- | lisp/eshell/esh-mode.el | 17 | ||||
| -rw-r--r-- | lisp/eshell/esh-var.el | 2 | ||||
| -rw-r--r-- | test/lisp/eshell/esh-cmd-tests.el | 20 |
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. |
| 943 | COMMAND should be a non-top-level Eshell command in parsed form. | 943 | COMMAND should be a non-top-level Eshell command in parsed form. |
| 944 | 944 | ||
| 945 | A command can be invoked directly if all of the following are true: | 945 | A 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. |
| 975 | COMMAND should be a top-level Eshell command in parsed form, as | 975 | COMMAND should be a top-level Eshell command in parsed form, as |
| 976 | produced by `eshell-parse-command'." | 976 | produced 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 | |||
| 619 | newline." | 619 | newline." |
| 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 () |