From 9ab8b968d63d9287639bbc574873bf8fde769fea Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Sun, 17 Sep 2023 21:06:46 -0700 Subject: [PATCH] Use 'eshell-with-temp-command' (indirectly) to parse Eshell script files * lisp/eshell/esh-cmd.el (eshell--region-p): New function. (eshell-with-temp-command, eshell-parse-command): Support '(:file . FILENAME)' to use the contents of FILENAME. * lisp/eshell/em-script.el (eshell-source-file): Call 'eshell-parse-command' and use backticks. --- lisp/eshell/em-script.el | 27 +++++------------ lisp/eshell/esh-cmd.el | 64 ++++++++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/lisp/eshell/em-script.el b/lisp/eshell/em-script.el index 55a05076342..9f6f720b8b0 100644 --- a/lisp/eshell/em-script.el +++ b/lisp/eshell/em-script.el @@ -89,26 +89,13 @@ This includes when running `eshell-command'." (defun eshell-source-file (file &optional args subcommand-p) "Execute a series of Eshell commands in FILE, passing ARGS. Comments begin with `#'." - (let ((orig (point)) - (here (point-max))) - (goto-char (point-max)) - (with-silent-modifications - ;; FIXME: Why not use a temporary buffer and avoid this - ;; "insert&delete" business? --Stef - (insert-file-contents file) - (goto-char (point-max)) - (throw 'eshell-replace-command - (prog1 - (list 'let - (list (list 'eshell-command-name (list 'quote file)) - (list 'eshell-command-arguments - (list 'quote args))) - (let ((cmd (eshell-parse-command (cons here (point))))) - (if subcommand-p - (setq cmd (list 'eshell-as-subcommand cmd))) - cmd)) - (delete-region here (point)) - (goto-char orig)))))) + (let ((cmd (eshell-parse-command `(:file . ,file)))) + (when subcommand-p + (setq cmd `(eshell-as-subcommand ,cmd))) + (throw 'eshell-replace-command + `(let ((eshell-command-name ',file) + (eshell-command-arguments ',args)) + ,cmd)))) (defun eshell/source (&rest args) "Source a file in a subshell environment." diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index a4542dd917d..0d73b2d6e69 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -350,48 +350,62 @@ This only returns external (non-Lisp) processes." ;; Command parsing -(defmacro eshell-with-temp-command (region &rest body) - "Narrow the buffer to REGION and execute the forms in BODY. +(defsubst eshell--region-p (object) + "Return non-nil if OBJECT is a pair of numbers or markers." + (and (consp object) + (number-or-marker-p (car object)) + (number-or-marker-p (cdr object)))) -REGION is a cons cell (START . END) that specifies the region to -which to narrow the buffer. REGION can also be a string, in -which case the macro temporarily inserts it into the buffer at -point, and narrows the buffer to the inserted string. Before -executing BODY, point is set to the beginning of the narrowed -REGION. +(defmacro eshell-with-temp-command (command &rest body) + "Temporarily insert COMMAND into the buffer and execute the forms in BODY. + +COMMAND can be a string to insert, a cons cell (START . END) +specifying a region in the current buffer, or (:file . FILENAME) +to temporarily insert the contents of FILENAME. + +Before executing BODY, narrow the buffer to the text for COMMAND +and and set point to the beginning of the narrowed region. The value returned is the last form in BODY." (declare (indent 1)) - `(let ((reg ,region)) - (if (stringp reg) + (let ((command-sym (make-symbol "command")) + (begin-sym (make-symbol "begin")) + (end-sym (make-symbol "end"))) + `(let ((,command-sym ,command)) + (if (eshell--region-p ,command-sym) + (save-restriction + (narrow-to-region (car ,command-sym) (cdr ,command-sym)) + (goto-char (car ,command-sym)) + ,@body) ;; Since parsing relies partly on buffer-local state ;; (e.g. that of `eshell-parse-argument-hook'), we need to ;; perform the parsing in the Eshell buffer. - (let ((begin (point)) end) + (let ((,begin-sym (point)) ,end-sym) (with-silent-modifications - (insert reg) - (setq end (point)) + (if (stringp ,command-sym) + (insert ,command-sym) + (forward-char (cadr (insert-file-contents (cdr ,command-sym))))) + (setq ,end-sym (point)) (unwind-protect (save-restriction - (narrow-to-region begin end) - (goto-char begin) + (narrow-to-region ,begin-sym ,end-sym) + (goto-char ,begin-sym) ,@body) - (delete-region begin end)))) - (save-restriction - (narrow-to-region (car reg) (cdr reg)) - (goto-char (car reg)) - ,@body)))) + (delete-region ,begin-sym ,end-sym)))))))) (defun eshell-parse-command (command &optional args toplevel) "Parse the COMMAND, adding ARGS if given. -COMMAND can either be a string, or a cons cell demarcating a buffer -region. TOPLEVEL, if non-nil, means that the outermost command (the -user's input command) is being parsed, and that pre and post command -hooks should be run before and after the command." +COMMAND can be a string, a cons cell (START . END) demarcating a +buffer region, or (:file . FILENAME) to parse the contents of +FILENAME. + +TOPLEVEL, if non-nil, means that the outermost command (the +user's input command) is being parsed, and that pre and post +command hooks should be run before and after the command." (pcase-let* ((terms (append - (if (consp command) + (if (eshell--region-p command) (eshell-parse-arguments (car command) (cdr command)) (eshell-with-temp-command command (goto-char (point-max)) -- 2.39.2