Improve handling of Eshell "for" loops
This fixes some errors with more-complex string forms, and also allows iterating over sequences other than just lists. * lisp/eshell/esh-cmd.el (eshell-for-iterate): New function... (eshell-rewrite-for-command): ... use it. * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/for-loop): Test multiple values. (esh-cmd-test/for-loop-string, esh-cmd-test/for-loop-vector): New tests. (esh-cmd-test/for-loop-mixed-args): Rename. * test/lisp/eshell/esh-proc-tests.el (esh-proc-test/sentinel/change-buffer): Make sure all the processes get cleaned up.
This commit is contained in:
parent
b3c82f939c
commit
08d5994b43
3 changed files with 49 additions and 35 deletions
|
@ -526,6 +526,20 @@ the second is ignored."
|
|||
(defvar eshell--local-vars nil
|
||||
"List of locally bound vars that should take precedence over env-vars.")
|
||||
|
||||
(iter-defun eshell-for-iterate (&rest args)
|
||||
"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)
|
||||
(cond
|
||||
((stringp arg)
|
||||
(iter-yield arg))
|
||||
((listp arg)
|
||||
(dolist (i arg) (iter-yield i)))
|
||||
((arrayp arg)
|
||||
(dotimes (i (length arg)) (iter-yield (aref arg i))))
|
||||
(t
|
||||
(iter-yield arg)))))
|
||||
|
||||
(defun eshell-rewrite-for-command (terms)
|
||||
"Rewrite a `for' command into its equivalent Eshell command form.
|
||||
Because the implementation of `for' relies upon conditional evaluation
|
||||
|
@ -533,23 +547,14 @@ of its argument (i.e., use of a Lisp special form), it must be
|
|||
implemented via rewriting, rather than as a function."
|
||||
(if (and (equal (car terms) "for")
|
||||
(equal (nth 2 terms) "in"))
|
||||
(let ((for-items (make-symbol "for-items"))
|
||||
(let ((iter-symbol (intern (nth 1 terms)))
|
||||
(body (car (last terms))))
|
||||
(setcdr (last terms 2) nil)
|
||||
`(let ((,for-items
|
||||
(append
|
||||
,@(mapcar
|
||||
(lambda (elem)
|
||||
(if (listp elem)
|
||||
(eshell-term-as-value elem)
|
||||
`(list ,elem)))
|
||||
(nthcdr 3 terms)))))
|
||||
(while ,for-items
|
||||
(let ((,(intern (cadr terms)) (car ,for-items))
|
||||
(eshell--local-vars (cons ',(intern (cadr terms))
|
||||
eshell--local-vars)))
|
||||
,body)
|
||||
(setq ,for-items (cdr ,for-items)))))))
|
||||
`(let ((eshell--local-vars (cons ',iter-symbol eshell--local-vars)))
|
||||
(iter-do (,iter-symbol (eshell-for-iterate
|
||||
,@(mapcar #'eshell-term-as-value
|
||||
(nthcdr 3 terms))))
|
||||
,body)))))
|
||||
|
||||
(defun eshell-structure-basic-command (func names keyword test &rest body)
|
||||
"With TERMS, KEYWORD, and two NAMES, structure a basic command.
|
||||
|
|
|
@ -319,8 +319,15 @@ processes correctly."
|
|||
(ert-deftest esh-cmd-test/for-loop ()
|
||||
"Test invocation of a for loop."
|
||||
(with-temp-eshell
|
||||
(eshell-match-command-output "for i in 5 { echo $i }"
|
||||
"5\n")))
|
||||
(eshell-match-command-output "for i in 1 2 { echo $i }"
|
||||
"1\n2\n")))
|
||||
|
||||
(ert-deftest esh-cmd-test/for-loop-string ()
|
||||
"Test invocation of a for loop with complex string arguments."
|
||||
(let ((eshell-test-value "X"))
|
||||
(with-temp-eshell
|
||||
(eshell-match-command-output "for i in a b$eshell-test-value { echo $i }"
|
||||
"a\nbX\n"))))
|
||||
|
||||
(ert-deftest esh-cmd-test/for-loop-list ()
|
||||
"Test invocation of a for loop iterating over a list."
|
||||
|
@ -328,7 +335,13 @@ processes correctly."
|
|||
(eshell-match-command-output "for i in (list 1 2 (list 3 4)) { echo $i }"
|
||||
"1\n2\n(3 4)\n")))
|
||||
|
||||
(ert-deftest esh-cmd-test/for-loop-multiple-args ()
|
||||
(ert-deftest esh-cmd-test/for-loop-vector ()
|
||||
"Test invocation of a for loop iterating over a vector."
|
||||
(with-temp-eshell
|
||||
(eshell-match-command-output "for i in `[1 2 3] { echo $i }"
|
||||
"1\n2\n3\n")))
|
||||
|
||||
(ert-deftest esh-cmd-test/for-loop-mixed-args ()
|
||||
"Test invocation of a for loop iterating over multiple arguments."
|
||||
(with-temp-eshell
|
||||
(eshell-match-command-output "for i in 1 2 (list 3 4) { echo $i }"
|
||||
|
@ -348,13 +361,6 @@ processes correctly."
|
|||
"echo $name; for name in 3 { echo $name }; echo $name"
|
||||
"env-value\n3\nenv-value\n"))))
|
||||
|
||||
(ert-deftest esh-cmd-test/for-loop-for-items-shadow ()
|
||||
"Test that the variable `for-items' isn't shadowed inside for loops."
|
||||
(with-temp-eshell
|
||||
(with-no-warnings (setq-local for-items "hello"))
|
||||
(eshell-match-command-output "for i in 1 { echo $for-items }"
|
||||
"hello\n")))
|
||||
|
||||
(ert-deftest esh-cmd-test/for-loop-lisp-body ()
|
||||
"Test invocation of a for loop with a Lisp body form."
|
||||
(with-temp-eshell
|
||||
|
|
|
@ -135,16 +135,19 @@
|
|||
(ert-deftest esh-proc-test/sentinel/change-buffer ()
|
||||
"Check that changing the current buffer while running a command works.
|
||||
See bug#71778."
|
||||
(eshell-with-temp-buffer bufname ""
|
||||
(with-temp-eshell
|
||||
(let (eshell-test-value)
|
||||
(eshell-insert-command
|
||||
(concat (format "for i in 1 2 {sleep 1; echo hello} > #<%s>; " bufname)
|
||||
"setq eshell-test-value t"))
|
||||
(with-current-buffer bufname
|
||||
(eshell-wait-for (lambda () eshell-test-value))
|
||||
(should (equal (buffer-string) "hellohello")))
|
||||
(eshell-match-command-output "echo goodbye" "\\`goodbye\n")))))
|
||||
(let ((starting-process-list (process-list)))
|
||||
(eshell-with-temp-buffer bufname ""
|
||||
(with-temp-eshell
|
||||
(let (eshell-test-value)
|
||||
(eshell-insert-command
|
||||
(concat (format "for i in 1 2 {sleep 1; echo hello} > #<%s>; "
|
||||
bufname)
|
||||
"setq eshell-test-value t"))
|
||||
(with-current-buffer bufname
|
||||
(eshell-wait-for (lambda () eshell-test-value))
|
||||
(should (equal (buffer-string) "hellohello")))
|
||||
(should (equal (process-list) starting-process-list))
|
||||
(eshell-match-command-output "echo goodbye" "\\`goodbye\n"))))))
|
||||
|
||||
|
||||
;; Pipelines
|
||||
|
|
Loading…
Add table
Reference in a new issue