* 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)
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
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:
"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)
(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