From 3d08d0dd80a02419f8301b63bb5663710f4f8ee2 Mon Sep 17 00:00:00 2001 From: Davide Masserut Date: Tue, 29 Aug 2023 22:33:48 +0200 Subject: [PATCH] Display the exit code if the last command failed in Eshell * lisp/eshell/esh-io.el (eshell-last-command-status): Make buffer-local. * lisp/eshell/em-prompt.el (eshell-prompt-function): Insert the exit code if last command failed. * test/lisp/eshell/em-prompt-tests.el (em-prompt-test/after-failure): New test. (em-prompt-test/next-previous-prompt-1) (em-prompt-test/forward-backward-matching-input-1): Add a failing command to tests. * doc/misc/eshell.texi (Invocation): Document change. * etc/NEWS: Announce change (bug#65604). --- doc/misc/eshell.texi | 3 ++ etc/NEWS | 3 ++ lisp/eshell/em-prompt.el | 2 ++ lisp/eshell/esh-io.el | 2 +- test/lisp/eshell/em-prompt-tests.el | 44 ++++++++++++++++++++++------- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 7a563bc794c..0ec90d0c159 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -234,6 +234,9 @@ the foreground. That said, background processes invoked from Eshell can be controlled the same way as any other background process in Emacs. +If a command exits abnormally, Eshell will display its exit code +in the next prompt. + @subsection Command form Command form looks much the same as in other shells. A command consists of arguments separated by spaces; the first argument is the diff --git a/etc/NEWS b/etc/NEWS index 5c11b6b9ac7..81ef0d6cedb 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -370,6 +370,9 @@ to load the edited aliases. Running 'rgrep' in Eshell now uses the Emacs grep facility instead of calling external rgrep. ++++ +*** If a command exits abnormally, the Eshell prompt now shows its exit code. + ** Pcomplete --- diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el index a5abb75bccc..15b849a4d37 100644 --- a/lisp/eshell/em-prompt.el +++ b/lisp/eshell/em-prompt.el @@ -50,6 +50,8 @@ as is common with most shells." (defcustom eshell-prompt-function (lambda () (concat (abbreviate-file-name (eshell/pwd)) + (unless (eshell-exit-success-p) + (format " [%d]" eshell-last-command-status)) (if (= (file-user-uid) 0) " # " " $ "))) "A function that returns the Eshell prompt string." :type 'function diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index c07f871dd37..cd0cee6e21d 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -170,7 +170,7 @@ describing the mode, e.g. for using with `eshell-get-target'.") (defvar eshell-current-handles nil) -(defvar eshell-last-command-status 0 +(defvar-local eshell-last-command-status 0 "The exit code from the last command. 0 if successful.") (defvar eshell-last-command-result nil diff --git a/test/lisp/eshell/em-prompt-tests.el b/test/lisp/eshell/em-prompt-tests.el index f6a63ac0db5..46e74e64983 100644 --- a/test/lisp/eshell/em-prompt-tests.el +++ b/test/lisp/eshell/em-prompt-tests.el @@ -85,20 +85,41 @@ This tests the case when `eshell-highlight-prompt' is nil." (apply #'propertize "hello\n" eshell-command-output-properties))))))) +(ert-deftest em-prompt-test/after-failure () + "Check that current prompt shows the exit code of the last failed command." + (with-temp-eshell + (let ((debug-on-error nil)) + (eshell-insert-command "(zerop \"foo\")")) + (let ((current-prompt (field-string (1- (point))))) + (should (equal-including-properties + current-prompt + (propertize + (concat (directory-file-name default-directory) + (unless (eshell-exit-success-p) + (format " [%d]" eshell-last-command-status)) + (if (= (file-user-uid) 0) " # " " $ ")) + 'read-only t + 'field 'prompt + 'font-lock-face 'eshell-prompt + 'front-sticky '(read-only field font-lock-face) + 'rear-nonsticky '(read-only field font-lock-face))))))) + (defun em-prompt-test/next-previous-prompt-1 () "Helper for checking forward/backward navigation of old prompts." (with-temp-eshell (eshell-insert-command "echo one") (eshell-insert-command "echo two") (eshell-insert-command "echo three") + (let ((debug-on-error nil)) ; A failed command. + (eshell-insert-command "(zerop \"foo\")")) (insert "echo fou") ; A partially-entered command. (ert-info ("Go back one prompt") (eshell-previous-prompt) (should (equal (point) (field-beginning))) - (should (equal (field-string) "echo three\n"))) - (ert-info ("Go back two prompts, starting from the end of the input") + (should (equal (field-string) "(zerop \"foo\")\n"))) + (ert-info ("Go back three prompts, starting from the end of the input") (end-of-line) - (eshell-previous-prompt 2) + (eshell-previous-prompt 3) (should (equal (point) (field-beginning))) (should (equal (field-string) "echo one\n"))) (ert-info ("Go to the current prompt, starting from the end of the input") @@ -110,20 +131,20 @@ This tests the case when `eshell-highlight-prompt' is nil." (eshell-next-prompt) (should (equal (point) (field-beginning))) (should (equal (field-string) "echo two\n"))) - (ert-info ("Go forward two prompts") - (eshell-next-prompt 2) + (ert-info ("Go forward three prompts") + (eshell-next-prompt 3) (should (equal (point) (field-beginning))) (should (equal (field-string) "echo fou"))) (ert-info ("Go back one prompt, starting from the beginning of the line") (forward-line 0) (eshell-previous-prompt 1) (should (equal (point) (field-beginning))) - (should (equal (field-string) "echo three\n"))) + (should (equal (field-string) "(zerop \"foo\")\n"))) (ert-info ("Go back one prompt, starting from the previous prompt's output") (forward-line -1) (eshell-previous-prompt 1) (should (equal (point) (field-beginning))) - (should (equal (field-string) "echo two\n"))))) + (should (equal (field-string) "echo three\n"))))) (ert-deftest em-prompt-test/next-previous-prompt () "Check that navigating forward/backward through old prompts works correctly." @@ -141,17 +162,20 @@ This tests the case when `eshell-highlight-prompt' is nil." (eshell-insert-command "printnl something else") (eshell-insert-command "echo two") (eshell-insert-command "echo three") + (let ((debug-on-error nil)) ; A failed command. + (eshell-insert-command "(zerop \"foo\")")) (insert "echo fou") ; A partially-entered command. - (ert-info ("Go back one prompt") + (ert-info ("Search for \"echo\", back one prompt") (eshell-backward-matching-input "echo" 1) (should (equal (point) (field-beginning))) (should (equal (field-string) "echo three\n"))) - (ert-info ("Go back two prompts, starting from the end of this line") + (ert-info ((concat "Search for \"echo\", back two prompts, " + "starting from the end of this line")) (end-of-line) (eshell-backward-matching-input "echo" 2) (should (equal (point) (field-beginning))) (should (equal (field-string) "echo one\n"))) - (ert-info ("Go forward three prompts") + (ert-info ("Search for \"echo\", forward three prompts") (eshell-forward-matching-input "echo" 3) (should (equal (point) (field-beginning))) (should (equal (field-string) "echo fou"))))) -- 2.39.2