emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master fada04cfc78 3/3: Add support for chaining conditionals in Eshell


From: Jim Porter
Subject: master fada04cfc78 3/3: Add support for chaining conditionals in Eshell
Date: Thu, 17 Oct 2024 00:54:15 -0400 (EDT)

branch: master
commit fada04cfc788a486265c9da6636611986b48ae49
Author: Jim Porter <jporterbugs@gmail.com>
Commit: Jim Porter <jporterbugs@gmail.com>

    Add support for chaining conditionals in Eshell
    
    * lisp/eshell/esh-cmd.el (eshell-structure-basic-command): Check for the
    presence of the conditional.  Allow any number of BODY forms.
    (eshell-rewrite-if-command): Add support for 'else' keyword and chained
    conditionals.
    
    * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/if-else-statement):
    Test 'else' keyword.
    (esh-cmd-test/if-else-statement-chain): New test.
    
    * doc/misc/eshell.texi (Control Flow): Document this change.
    
    * etc/NEWS: Announce this change.
---
 doc/misc/eshell.texi              | 19 +++++++++++++++---
 etc/NEWS                          | 14 +++++++++++++
 lisp/eshell/esh-cmd.el            | 42 +++++++++++++++++++++++----------------
 test/lisp/eshell/esh-cmd-tests.el | 22 ++++++++++++++++----
 4 files changed, 73 insertions(+), 24 deletions(-)

diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index bbb6b2e6aac..9a2714b14fb 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -1698,16 +1698,29 @@ satisfied if the subcommand's exit status is 0.
 @table @code
 
 @item if @var{conditional} @var{true-subcommand}
-@itemx if @var{conditional} @var{true-subcommand} @var{false-subcommand}
+@itemx if @var{conditional} @var{true-subcommand} else @var{false-subcommand}
 Evaluate @var{true-subcommand} if @var{conditional} is satisfied;
 otherwise, evaluate @var{false-subcommand}.  Both @var{true-subcommand}
 and @var{false-subcommand} should be subcommands, as with
 @var{conditional}.
 
+You can also chain together @code{if}/@code{else} forms, for example:
+
+@example
+if @{[ -f file.txt ]@} @{
+  echo found file
+@} else if @{[ -f alternate.txt ]@} @{
+  echo found alternate
+@} else @{
+  echo not found!
+@}
+@end example
+
 @item unless @var{conditional} @var{false-subcommand}
-@itemx unless @var{conditional} @var{false-subcommand} @var{true-subcommand}
+@itemx unless @var{conditional} @var{false-subcommand} else 
@var{true-subcommand}
 Evaluate @var{false-subcommand} if @var{conditional} is not satisfied;
-otherwise, evaluate @var{true-subcommand}.
+otherwise, evaluate @var{true-subcommand}.  Like above, you can also
+chain together @code{unless}/@code{else} forms.
 
 @item while @var{conditional} @var{subcommand}
 Repeatedly evaluate @var{subcommand} so long as @var{conditional} is
diff --git a/etc/NEWS b/etc/NEWS
index 4346fb4aedd..f9ba659ed86 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -257,6 +257,20 @@ These functions now take an optional ERROR-TARGET argument 
to control
 where to send the standard error output.  See the "(eshell) Entry
 Points" node in the Eshell manual for more details.
 
++++
+*** Conditional statements in Eshell now use an 'else' keyword.
+Eshell now prefers the following form when writing conditionals:
+
+    if {conditional} {true-subcommand} else {false-subcommand}
+
+The old form (without the 'else' keyword) is retained for compatibility.
+
++++
+*** You can now chain conditional statements in Eshell.
+When using the newly-preferred conditional form in Eshell, you can now
+chain together multiple 'if'/'else' statements.  For more information,
+see "(eshell) Control Flow" in the Eshell manual.
+
 +++
 *** Eshell's built-in 'wait' command now accepts a timeout.
 By passing '-t' or '--timeout', you can specify a maximum time to wait
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index 65f997e5b88..c9096b0d159 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -551,12 +551,14 @@ implemented via rewriting, rather than as a function."
               ,body)
              (setq ,for-items (cdr ,for-items)))))))
 
-(defun eshell-structure-basic-command (func names keyword test body
-                                           &optional else)
+(defun eshell-structure-basic-command (func names keyword test &rest body)
   "With TERMS, KEYWORD, and two NAMES, structure a basic command.
 The first of NAMES should be the positive form, and the second the
 negative.  It's not likely that users should ever need to call this
 function."
+  (unless test
+    (error "Missing test for `%s' command" keyword))
+
   ;; If the test form is a subcommand, wrap it in `eshell-commands' to
   ;; silence the output.
   (when (memq (car test) '(eshell-as-subcommand eshell-lisp-command))
@@ -582,33 +584,39 @@ function."
       (setq test `(not ,test)))
 
   ;; Finally, create the form that represents this structured command.
-  `(,func ,test ,body ,else))
+  `(,func ,test ,@body))
 
 (defun eshell-rewrite-while-command (terms)
   "Rewrite a `while' command into its equivalent Eshell command form.
 Because the implementation of `while' relies upon conditional
 evaluation of its argument (i.e., use of a Lisp special form), it
 must be implemented via rewriting, rather than as a function."
-  (if (and (stringp (car terms))
-          (member (car terms) '("while" "until")))
-      (eshell-structure-basic-command
-       'while '("while" "until") (car terms)
-       (cadr terms)
-       (car (last terms)))))
+  (when (and (stringp (car terms))
+             (member (car terms) '("while" "until")))
+    (eshell-structure-basic-command
+     'while '("while" "until") (car terms)
+     (cadr terms)
+     (caddr terms))))
 
 (defun eshell-rewrite-if-command (terms)
   "Rewrite an `if' command into its equivalent Eshell command form.
 Because the implementation of `if' relies upon conditional
 evaluation of its argument (i.e., use of a Lisp special form), it
 must be implemented via rewriting, rather than as a function."
-  (if (and (stringp (car terms))
-          (member (car terms) '("if" "unless")))
-      (eshell-structure-basic-command
-       'if '("if" "unless") (car terms)
-       (cadr terms)
-       (car (last terms (if (= (length terms) 4) 2)))
-       (when (= (length terms) 4)
-         (car (last terms))))))
+  (when (and (stringp (car terms))
+             (member (car terms) '("if" "unless")))
+    (eshell-structure-basic-command
+     'if '("if" "unless") (car terms)
+     (cadr terms)
+     (caddr terms)
+     (if (equal (nth 3 terms) "else")
+         ;; If there's an "else" keyword, allow chaining together
+         ;; multiple "if" forms...
+         (or (eshell-rewrite-if-command (nthcdr 4 terms))
+             (nth 4 terms))
+       ;; ... otherwise, only allow a single "else" block (without the
+       ;; keyword) as before for compatibility.
+       (nth 3 terms)))))
 
 (defun eshell-set-exit-info (status &optional result)
   "Set the exit status and result for the last command.
diff --git a/test/lisp/eshell/esh-cmd-tests.el 
b/test/lisp/eshell/esh-cmd-tests.el
index 9e4cbc58201..0f388a9eba4 100644
--- a/test/lisp/eshell/esh-cmd-tests.el
+++ b/test/lisp/eshell/esh-cmd-tests.el
@@ -427,11 +427,15 @@ processes correctly."
 (ert-deftest esh-cmd-test/if-else-statement ()
   "Test invocation of an if/else statement."
   (let ((eshell-test-value t))
-    (eshell-command-result-equal "if $eshell-test-value {echo yes} {echo no}"
-                                 "yes"))
+    (eshell-command-result-equal
+     "if $eshell-test-value {echo yes} {echo no}" "yes")
+    (eshell-command-result-equal
+     "if $eshell-test-value {echo yes} else {echo no}" "yes"))
   (let ((eshell-test-value nil))
-    (eshell-command-result-equal "if $eshell-test-value {echo yes} {echo no}"
-                                 "no")))
+    (eshell-command-result-equal
+     "if $eshell-test-value {echo yes} {echo no}" "no")
+    (eshell-command-result-equal
+     "if $eshell-test-value {echo yes} else {echo no}" "no")))
 
 (ert-deftest esh-cmd-test/if-else-statement-lisp-form ()
   "Test invocation of an if/else statement using a Lisp form."
@@ -474,6 +478,16 @@ This tests when `eshell-lisp-form-nil-is-failure' is nil."
   (eshell-command-result-equal "if {[ foo = bar ]} {echo yes} {echo no}"
                                "no"))
 
+(ert-deftest esh-cmd-test/if-else-statement-chain ()
+  "Test invocation of a chained if/else statement."
+  (dolist (case '((1 . "one") (2 . "two") (3 . "other")))
+    (let ((eshell-test-value (car case)))
+      (eshell-command-result-equal
+       (concat "if (= eshell-test-value 1) {echo one} "
+               "else if (= eshell-test-value 2) {echo two} "
+               "else {echo other}")
+       (cdr case)))))
+
 (ert-deftest esh-cmd-test/if-statement-pipe ()
   "Test invocation of an if statement piped to another command."
   (skip-unless (executable-find "rev"))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]