Simplify usage of 'while' forms in Eshell's iterative evaluation

Now, 'eshell-do-eval' rewrites 'while' forms to let-bind variables for
the command and test bodies.  This means that external code, such as
command rewriting hooks, no longer has to worry about this, making it
easier to pass "normal" Lisp forms to 'eshell-do-eval' (bug#61954).

* lisp/eshell/esh-cmd.el (eshell-command-body, eshell-test-body): No
longer used outside of 'eshell-do-eval', so rename to...
(eshell--command-body, eshell--test-body): ... these.
(Command evaluation macros): Remove obsolete description about 'if'
and 'while' forms.
(eshell-rewrite-for-command, eshell-structure-basic-command): Remove
'eshell-command-body' and 'eshell-test-body'.
(eshell-do-eval): Reimplement handling of 'while' forms.
This commit is contained in:
Jim Porter 2023-01-28 17:04:11 -08:00
parent e01660ca50
commit 1565dbcae3

View file

@ -494,8 +494,8 @@ hooks should be run before and after the command."
(t
(list sym (car terms))))))
(defvar eshell-command-body)
(defvar eshell-test-body)
(defvar eshell--command-body)
(defvar eshell--test-body)
(defsubst eshell-invokify-arg (arg &optional share-output silent)
"Change ARG so it can be invoked from a structured command.
@ -540,9 +540,7 @@ implemented via rewriting, rather than as a function."
(if (listp elem)
elem
`(list ,elem)))
(nthcdr 3 terms))))
(eshell-command-body '(nil))
(eshell-test-body '(nil)))
(nthcdr 3 terms)))))
(while for-items
(let ((,(intern (cadr terms)) (car for-items))
(eshell--local-vars (cons ',(intern (cadr terms))
@ -579,8 +577,7 @@ function."
;; finally, create the form that represents this structured
;; command
`(let ((eshell-command-body '(nil))
(eshell-test-body '(nil)))
`(progn
(,func ,test ,body ,else)
(eshell-close-handles)))
@ -745,10 +742,6 @@ if none)."
;; `condition-case', `if', `let', `prog1', `progn', `quote', `setq',
;; `unwind-protect', and `while'.
;;
;; @ When using `while', first let-bind `eshell-test-body' and
;; `eshell-command-body' to '(nil). Eshell uses these variables to
;; handle evaluating its subforms multiple times.
;;
;; @ The two `special' variables are `eshell-current-handles' and
;; `eshell-current-subjob-p'. Bind them locally with a `let' if you
;; need to change them. Change them directly only if your intention
@ -1128,24 +1121,34 @@ have been replaced by constants."
(let ((args (cdr form)))
(cond
((eq (car form) 'while)
;; Wrap the `while' form with let-bindings for the command and
;; test bodies. This helps us resume evaluation midway
;; through the loop.
(let ((new-form (copy-tree `(let ((eshell--command-body nil)
(eshell--test-body nil))
(eshell--wrapped-while ,@args)))))
(eshell-manipulate "modifying while form"
(setcar form (car new-form))
(setcdr form (cdr new-form)))
(eshell-do-eval form synchronous-p)))
((eq (car form) 'eshell--wrapped-while)
(when eshell--command-body
(cl-assert (not synchronous-p))
(eshell-do-eval eshell--command-body)
(setq eshell--command-body nil
eshell--test-body nil))
;; `copy-tree' is needed here so that the test argument
;; doesn't get modified and thus always yield the same result.
(when (car eshell-command-body)
(cl-assert (not synchronous-p))
(eshell-do-eval (car eshell-command-body))
(setcar eshell-command-body nil)
(setcar eshell-test-body nil))
(unless (car eshell-test-body)
(setcar eshell-test-body (copy-tree (car args))))
(while (cadr (eshell-do-eval (car eshell-test-body) synchronous-p))
(setcar eshell-command-body
(if (cddr args)
`(progn ,@(copy-tree (cdr args)))
(copy-tree (cadr args))))
(eshell-do-eval (car eshell-command-body) synchronous-p)
(setcar eshell-command-body nil)
(setcar eshell-test-body (copy-tree (car args))))
(setcar eshell-command-body nil))
;; doesn't get modified and thus always yield the same result.
(unless eshell--test-body
(setq eshell--test-body (copy-tree (car args))))
(while (cadr (eshell-do-eval eshell--test-body synchronous-p))
(setq eshell--command-body
(if (cddr args)
`(progn ,@(copy-tree (cdr args)))
(copy-tree (cadr args))))
(eshell-do-eval eshell--command-body synchronous-p)
(setq eshell--command-body nil
eshell--test-body (copy-tree (car args)))))
((eq (car form) 'if)
(eshell-manipulate "evaluating if condition"
(setcar args (eshell-do-eval (car args) synchronous-p)))