From 83f716950a60ee64c49654033430f8e09d373f24 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 * doc/misc/eshell.texi (Invocation): Document change. * etc/NEWS: Announce change. * lisp/eshell/em-prompt.el (eshell-prompt-function): Insert the exit code if last command failed. * lisp/eshell/esh-io.el (eshell-last-command-status): Make it buffer-local. * test/lisp/eshell/em-prompt-tests.el (em-prompt-test/after-failure) (em-prompt-test/next-previous-prompt-with) (em-prompt-test/forward-backward-matching-input-with): New tests. --- 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 | 37 +++++++++++++++++++++++++---- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 7a563bc794c..677bd0f3e3e 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -234,6 +234,9 @@ Invocation can be controlled the same way as any other background process in Emacs. +If a command exits abnormally, Eshell displays the command's exit code +in the 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..7a9010a2a9a 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, Eshell now displays the command's exit code in the prompt. + ** Pcomplete --- diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el index 42f8f273b52..692b579d02d 100644 --- a/lisp/eshell/em-prompt.el +++ b/lisp/eshell/em-prompt.el @@ -50,6 +50,8 @@ eshell-prompt-load-hook (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. Make sure to update `eshell-prompt-regexp' so that it will match your 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 @@ eshell-redirection-operators-alist (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 93bf9d84ab3..c90f417cefd 100644 --- a/test/lisp/eshell/em-prompt-tests.el +++ b/test/lisp/eshell/em-prompt-tests.el @@ -34,6 +34,25 @@ ;;; Tests: +(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))))))) + (ert-deftest em-prompt-test/field-properties () "Check that field properties are properly set on Eshell output/prompts." (with-temp-eshell @@ -88,6 +107,8 @@ em-prompt-test--with-multiline (defun em-prompt-test/next-previous-prompt-with () "Helper for checking forward/backward navigation of old prompts." (with-temp-eshell + (let ((debug-on-error nil)) + (eshell-insert-command "(zerop \"foo\")")) ; A failed command. (eshell-insert-command "echo one") (eshell-insert-command "echo two") (eshell-insert-command "echo three") @@ -99,8 +120,11 @@ em-prompt-test/next-previous-prompt-with (end-of-line) (eshell-previous-prompt 2) (should (equal (eshell-get-old-input) "echo one")) - ;; Go forward three prompts. - (eshell-next-prompt 3) + ;; Go back one prompt. + (eshell-previous-prompt 1) + (should (equal (eshell-get-old-input) "(zerop \"foo\")")) + ;; Go forward four prompts. + (eshell-next-prompt 4) (should (equal (eshell-get-old-input) "echo fou")))) (ert-deftest em-prompt-test/next-previous-prompt () @@ -115,6 +139,8 @@ em-prompt-test/next-previous-prompt-multiline (defun em-prompt-test/forward-backward-matching-input-with () "Helper for checking forward/backward navigation via regexps." (with-temp-eshell + (let ((debug-on-error nil)) + (eshell-insert-command "(zerop \"foo\")")) ; A failed command. (eshell-insert-command "echo one") (eshell-insert-command "printnl something else") (eshell-insert-command "echo two") @@ -127,8 +153,11 @@ em-prompt-test/forward-backward-matching-input-with (end-of-line) (eshell-backward-matching-input "echo" 2) (should (equal (eshell-get-old-input) "echo one")) - ;; Go forward three prompts. - (eshell-forward-matching-input "echo" 3) + ;; Go back one prompt. + (eshell-previous-prompt 1) + (should (equal (eshell-get-old-input) "(zerop \"foo\")")) + ;; Go forward four prompts. + (eshell-forward-matching-input "echo" 4) (should (equal (eshell-get-old-input) "echo fou")))) (ert-deftest em-prompt-test/forward-backward-matching-input () -- 2.42.0