]> git.eshelyaron.com Git - emacs.git/commitdiff
Add support for range objects in Eshell "for" loops
authorJim Porter <jporterbugs@gmail.com>
Sun, 3 Nov 2024 19:56:15 +0000 (11:56 -0800)
committerEshel Yaron <me@eshelyaron.com>
Fri, 8 Nov 2024 13:30:43 +0000 (14:30 +0100)
* lisp/eshell/esh-cmd.el (eshell-for-iterate): Add support for
'eshell-range' objects.

* test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/for-loop-range): New
test.

* doc/misc/eshell.texi (Control Flow): Update documentation.

* etc/NEWS: Announce this change.

(cherry picked from commit ee87af4f1603d2042afa641e74df0403a49af1c5)

doc/misc/eshell.texi
etc/NEWS
lisp/eshell/esh-cmd.el
test/lisp/eshell/esh-cmd-tests.el

index 5470d519687ea33fc512568103d4365a767593c3..146fed6c8f8f49cce8139e776c9b9218dbb8d303 100644 (file)
@@ -1761,11 +1761,15 @@ satisfied.
 Repeatedly evaluate @var{subcommand} until @var{conditional} is
 satisfied.
 
-@item for @var{var} in @var{list}@dots{} @var{subcommand}
-Iterate over each element of @var{list}, storing the element in
-@var{var} and evaluating @var{subcommand}.  If @var{list} is not a list,
-treat it as a list of one element.  If you specify multiple @var{lists},
-this will iterate over each of them in turn.
+@item for @var{var} in @var{sequence}@dots{} @var{subcommand}
+Iterate over each element of @var{sequence}, storing the element in
+@var{var} and evaluating @var{subcommand}.  If @var{sequence} is a
+range of the form @code{@var{begin}..@var{end}}, iterate over each
+integer between @var{begin} and @var{end}, not including @var{end}.  If
+@var{sequence} is not a sequence, treat it as a list of one element.
+
+If you specify multiple @var{sequences}, this will iterate over each of
+them in turn.
 
 @end table
 
index d841bfd21351642269386c3899593c9e7cf04efd..af23b71d0cc69dc19f2c65de5ea52b896bfd6659 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -226,6 +226,12 @@ 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.
 
++++
+*** You can now loop over ranges of integers with the Eshell 'for' command.
+When passing a range like 'BEGIN..END' to the Eshell 'for' command,
+Eshell will now iterate over each integer between BEGIN and END, not
+including END.
+
 +++
 *** Conditional statements in Eshell now use an 'else' keyword.
 Eshell now prefers the following form when writing conditionals:
index 441a39ac86ab17ee0d65d1c2f3f3b1d8de5b54de..4b7061c0e01c44c152b31fa3424a3e6fef0e9329 100644 (file)
@@ -530,7 +530,19 @@ the second is ignored."
   "Iterate over the elements of each sequence in ARGS.
 If ARGS is not a sequence, treat it as a list of one element."
   (dolist (arg args)
+    (when (eshell--range-string-p arg)
+      (setq arg (eshell--string-to-range arg)))
     (cond
+     ((eshell-range-p arg)
+      (let ((i (eshell-range-begin arg))
+            (end (eshell-range-end arg)))
+        ;; NOTE: We could support unbounded ranges here, but those
+        ;; aren't very easy to use in Eshell yet.  (We'd need something
+        ;; like the "break" statement for "for" loops.)
+        (cl-assert (and i end))
+        (while (< i end)
+          (iter-yield i)
+          (cl-incf i))))
      ((stringp arg)
       (iter-yield arg))
      ((listp arg)
index ae1aed59b45ec0c9e15f96736404d4b290c2c254..64ac3a8c2f6c69f35aebcdeb6be56faa663d0683 100644 (file)
@@ -341,6 +341,21 @@ processes correctly."
     (eshell-match-command-output "for i in `[1 2 3] { echo $i }"
                                  "1\n2\n3\n")))
 
+(ert-deftest esh-cmd-test/for-loop-range ()
+  "Test invocation of a for loop iterating over a range."
+  (with-temp-eshell
+    (eshell-match-command-output "for i in 1..5 { echo $i }"
+                                 "1\n2\n3\n4\n")
+    (let ((eshell-test-value 2))
+      (eshell-match-command-output "for i in $eshell-test-value..5 { echo $i }"
+                                   "2\n3\n4\n"))
+    ;; Make sure range syntax only work when it's part of the literal
+    ;; syntax; a variable expanding to something that looks like a range
+    ;; doesn't count.
+    (let ((eshell-test-value "1..5"))
+      (eshell-match-command-output "for i in $eshell-test-value { echo $i }"
+                                   "1..5\n")))))
+
 (ert-deftest esh-cmd-test/for-loop-mixed-args ()
   "Test invocation of a for loop iterating over multiple arguments."
   (with-temp-eshell