From fd4e9a51ac13816fc7e0a0cd8e2e4f47a7b0226a Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Sat, 28 Jan 2023 17:04:11 -0800 Subject: [PATCH 3/3] 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'. * 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. --- lisp/eshell/esh-cmd.el | 59 ++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 5dbbd770582..93f2616020c 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -494,8 +494,8 @@ eshell-rewrite-named-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 @@ eshell-rewrite-for-command (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 @@ eshell-structure-basic-command ;; 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 @@ eshell-separate-commands ;; `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 @@ eshell-do-eval (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))) -- 2.25.1