mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2026-09-10 07:46:51 -04:00
Allow evaluating Eshell forms using lexical binding
* lisp/eshell/esh-cmd.el (eshell-lexical-binding): New option. (eshell--eval): New defsubst... (eshell-do-eval, eshell-exec-lisp): * lisp/eshell/esh-var.el (eshell-parse-variable-ref): * lisp/eshell/em-banner.el (eshell-banner-initialize): ... use it. * lisp/eshell/em-ls.el (eshell-ls-applicable): Evaluate form using lexical binding. * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test--binding-check): New function. (esh-cmd-test/lexical-binding, esh-cmd-test/dynamic-binding): New tests. * etc/NEWS: Announce this change.
This commit is contained in:
parent
779a37c076
commit
f856da36cd
5
etc/NEWS
5
etc/NEWS
|
|
@ -286,6 +286,11 @@ some other command:
|
|||
See the "(eshell) Lisp Pipelines" node in the Eshell manual for more
|
||||
details.
|
||||
|
||||
---
|
||||
*** New user option 'eshell-lexical-binding'.
|
||||
When enabled, this causes Eshell to evaluate commands in Lisp syntax
|
||||
using lexical binding.
|
||||
|
||||
|
||||
* New Modes and Packages in Emacs 32.1
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
(eval-when-compile
|
||||
(require 'cl-lib))
|
||||
|
||||
(require 'esh-cmd)
|
||||
(require 'esh-util)
|
||||
(require 'esh-mode)
|
||||
|
||||
|
|
@ -77,7 +78,7 @@ This can be any sexp, and should end with at least two newlines."
|
|||
(unless eshell-non-interactive-p
|
||||
(cl-assert eshell-mode)
|
||||
(cl-assert eshell-banner-message)
|
||||
(let ((msg (eval eshell-banner-message)))
|
||||
(let ((msg (eshell--eval eshell-banner-message)))
|
||||
(cl-assert msg)
|
||||
(eshell-interactive-print msg))))
|
||||
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ calling FUNC with FILE as an argument."
|
|||
(not (eq (aref modes (+ ,index 3)) ?-)))
|
||||
(t
|
||||
;; Otherwise call FUNC.
|
||||
(,(eval func) ,file)))))
|
||||
(,(eval func t) ,file)))))
|
||||
|
||||
(defcustom eshell-ls-highlight-alist nil
|
||||
"This alist correlates test functions to color.
|
||||
|
|
|
|||
|
|
@ -126,6 +126,10 @@ There are several different kinds of commands, however."
|
|||
"If non-nil, prefer Lisp functions to external commands."
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom eshell-lexical-binding lexical-binding
|
||||
"If non-nil, use lexical binding when evaluating Eshell forms."
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom eshell-lisp-regexp "\\([(`]\\|#'\\)"
|
||||
"A regexp which, if matched at beginning of an argument, means Lisp.
|
||||
Such arguments will be passed to `read', and then evaluated."
|
||||
|
|
@ -1141,6 +1145,10 @@ the form (:eshell-background . PROCESSES)."
|
|||
(eshell-always-debug-command 'form
|
||||
"done %s\n\n%s" ,tag-symbol (eshell-stringify ,form)))))))
|
||||
|
||||
(defsubst eshell--eval (form)
|
||||
"Evaluate FORM, respecting `eshell-lexical-binding'."
|
||||
(eval form eshell-lexical-binding))
|
||||
|
||||
(defun eshell-do-eval (form &optional synchronous-p)
|
||||
"Evaluate FORM, simplifying it as we go.
|
||||
Unless SYNCHRONOUS-P is non-nil, throws `eshell-defer' if it needs to
|
||||
|
|
@ -1155,7 +1163,7 @@ again. Any forms preceding one that throw `eshell-defer' will
|
|||
have been replaced by constants."
|
||||
(cond
|
||||
((not (listp form))
|
||||
(list 'quote (eval form)))
|
||||
(list 'quote (eshell--eval form)))
|
||||
((memq (car form) '(quote function))
|
||||
form)
|
||||
(t
|
||||
|
|
@ -1232,10 +1240,10 @@ have been replaced by constants."
|
|||
(eshell-do-eval form synchronous-p)))))
|
||||
((eq (car form) 'setcar)
|
||||
(setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p))
|
||||
(eval form))
|
||||
(eshell--eval form))
|
||||
((eq (car form) 'setcdr)
|
||||
(setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p))
|
||||
(eval form))
|
||||
(eshell--eval form))
|
||||
((eq (car form) 'let)
|
||||
(unless (eq (car-safe (cadr args)) 'eshell-do-eval)
|
||||
(eshell-manipulate form "evaluating let args"
|
||||
|
|
@ -1251,7 +1259,7 @@ have been replaced by constants."
|
|||
(car args))
|
||||
;; These expressions should all be constants now.
|
||||
(mapcar (lambda (binding)
|
||||
(when (consp binding) (eval (cadr binding))))
|
||||
(when (consp binding) (eshell--eval (cadr binding))))
|
||||
(car args))
|
||||
(let (deferred result)
|
||||
;; Evaluate the `let' body, catching `eshell-defer' so we
|
||||
|
|
@ -1291,7 +1299,7 @@ have been replaced by constants."
|
|||
(unless (eq (caar args) 'eshell-do-eval)
|
||||
(eshell-manipulate form "handling special form"
|
||||
(setcar args `(eshell-do-eval ',(car args) ,synchronous-p))))
|
||||
(eval form))
|
||||
(eshell--eval form))
|
||||
((eq (car form) 'unwind-protect)
|
||||
;; `unwind-protect' has to be handled specially, because we
|
||||
;; only want to call `eshell-do-eval' on its first form, and
|
||||
|
|
@ -1317,7 +1325,7 @@ have been replaced by constants."
|
|||
(if (cddr args) (error "Unsupported form (setq X1 E1 X2 E2..)"))
|
||||
(eshell-manipulate form "evaluating arguments to setq"
|
||||
(setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p)))
|
||||
(list 'quote (eval form)))
|
||||
(list 'quote (eshell--eval form)))
|
||||
(t
|
||||
(if (and args (not (memq (car form) '(run-hooks))))
|
||||
(eshell-manipulate form
|
||||
|
|
@ -1358,7 +1366,7 @@ have been replaced by constants."
|
|||
(new-form
|
||||
(catch 'eshell-replace-command
|
||||
(ignore
|
||||
(setq result (eval form))))))
|
||||
(setq result (eshell--eval form))))))
|
||||
(if new-form
|
||||
(progn
|
||||
(eshell-manipulate form "substituting replacement form"
|
||||
|
|
@ -1484,7 +1492,7 @@ case."
|
|||
(let ((result
|
||||
(save-current-buffer
|
||||
(if form-p
|
||||
(eval func-or-form)
|
||||
(eshell--eval func-or-form)
|
||||
(apply func-or-form args)))))
|
||||
(and result (funcall printer result))
|
||||
result)
|
||||
|
|
|
|||
|
|
@ -593,7 +593,8 @@ Possible variable references are:
|
|||
(eshell-parse-double-quote)))))))
|
||||
(throw 'eshell-incomplete (concat "$" delim)))
|
||||
(when name
|
||||
`(eshell-get-variable ,(eval name) indices ,eshell-current-quoted)))))
|
||||
`(eshell-get-variable ,(eshell--eval name) indices
|
||||
,eshell-current-quoted)))))
|
||||
((assoc (char-to-string (char-after))
|
||||
eshell-variable-aliases-list)
|
||||
(forward-char)
|
||||
|
|
|
|||
|
|
@ -627,6 +627,31 @@ NAME is the name of the test case."
|
|||
(esh-cmd-test--deftest-invoke-directly complex "ls ." nil)
|
||||
(esh-cmd-test--deftest-invoke-directly complex-subcmd "echo {ls .}" nil)
|
||||
|
||||
|
||||
;; Lexical/dynamic binding
|
||||
|
||||
(defun esh-cmd-test--binding-check ()
|
||||
"Run a sequence of Eshell commands that depend on the binding type."
|
||||
(unwind-protect
|
||||
(with-temp-eshell
|
||||
(eshell-insert-command "(defun test-function () test-value)")
|
||||
(eshell-insert-command "(setq test-value 1)")
|
||||
(eshell-insert-command "(let ((test-value 2)) (test-function))")
|
||||
(eshell-last-output))
|
||||
(with-no-warnings
|
||||
(fmakunbound #'test-function)
|
||||
(makunbound 'test-value))))
|
||||
|
||||
(ert-deftest esh-cmd-test/lexical-binding ()
|
||||
"Test that enabling `eshell-lexical-binding' works."
|
||||
(let ((eshell-lexical-binding t))
|
||||
(should (string-match-p "\\`1\n\\'" (esh-cmd-test--binding-check)))))
|
||||
|
||||
(ert-deftest esh-cmd-test/dynamic-binding ()
|
||||
"Test that disabling `eshell-lexical-binding' works."
|
||||
(let ((eshell-lexical-binding nil))
|
||||
(should (string-match-p "\\`2\n\\'" (esh-cmd-test--binding-check)))))
|
||||
|
||||
|
||||
;; Error handling
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue