Package: emacs;
Reported by: Keith David Bershatsky <esq <at> lawlist.com>
Date: Sun, 25 Dec 2016 19:02:01 UTC
Severity: wishlist
To reply to this bug, email your comments to 25270 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
View this report as an mbox folder, status mbox, maintainer mbox
bug-gnu-emacs <at> gnu.org
:bug#25270
; Package emacs
.
(Sun, 25 Dec 2016 19:02:01 GMT) Full text and rfc822 format available.Keith David Bershatsky <esq <at> lawlist.com>
:bug-gnu-emacs <at> gnu.org
.
(Sun, 25 Dec 2016 19:02:02 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Keith David Bershatsky <esq <at> lawlist.com> To: bug-gnu-emacs <at> gnu.org Subject: eshell -- programmatically send input -- feature request Date: Sun, 25 Dec 2016 11:00:16 -0800
As far as I am aware, users of eshell have been limited to sending input programmatically by inserting the command into the eshell buffer (at the command prompt) and then executing `eshell-send-input`. Some users (like myself) may feel that such a solution is lo-tech -- i.e., not very eloquent. Perhaps this is because I, and perhaps others, am/are spoiled by functions available when running shell such as: (let ((buf (shell))) (comint-send-string buf "ls -la") (comint-send-input)) or (let ((buf (shell))) (comint-simple-send buf "ls -la")) Here is a link to a thread entitled "How to programmatically execute a command in eshell?": http://emacs.stackexchange.com/questions/7617/how-to-programmatically-execute-a-command-in-eshell And the accepted answer is as follows: (with-current-buffer "*eshell*" (eshell-return-to-prompt) (insert "ls") (eshell-send-input)) The following is an example of how this new feature might be implemented: SAMPLE USAGE: (eshell-send-input nil nil nil "ls -la /") (require 'eshell) (defun eshell-send-input (&optional use-region queue-p no-newline input-string-a) "Send the input received to Eshell for parsing and processing. After `eshell-last-output-end', sends all text from that marker to point as input. Before that marker, calls `eshell-get-old-input' to retrieve old input, copies it to the end of the buffer, and sends it. - If USE-REGION is non-nil, the current region (between point and mark) will be used as input. - If QUEUE-P is non-nil, input will be queued until the next prompt, rather than sent to the currently active process. If no process, the input is processed immediately. - If NO-NEWLINE is non-nil, the input is sent without an implied final newline." (interactive "P") ;; Note that the input string does not include its terminal newline. (let ((proc-running-p (and (eshell-interactive-process) (not queue-p))) (inhibit-point-motion-hooks t) after-change-functions) (unless (and proc-running-p (not (eq (process-status (eshell-interactive-process)) 'run))) (if (or proc-running-p (>= (point) eshell-last-output-end)) (goto-char (point-max)) ;; This is for a situation when point is before `point-max'. (let ((copy (or input-string-a (eshell-get-old-input use-region)))) (goto-char eshell-last-output-end) (insert-and-inherit copy))) (unless (or no-newline (and eshell-send-direct-to-subprocesses proc-running-p)) (insert-before-markers-and-inherit ?\n)) (if proc-running-p (progn (eshell-update-markers eshell-last-output-end) (if (or eshell-send-direct-to-subprocesses (= eshell-last-input-start eshell-last-input-end)) (unless no-newline (process-send-string (eshell-interactive-process) "\n")) (process-send-region (eshell-interactive-process) eshell-last-input-start eshell-last-input-end))) (if (and (null input-string-a) (= eshell-last-output-end (point))) ;; This next line is for a situation when nothing is there -- just make a new command prompt. (run-hooks 'eshell-post-command-hook) (let (input) (eshell-condition-case err (progn (setq input (or input-string-a (buffer-substring-no-properties eshell-last-output-end (1- (point))))) (run-hook-with-args 'eshell-expand-input-functions eshell-last-output-end (1- (point))) (let ((cmd (eshell-parse-command-input eshell-last-output-end (1- (point)) nil input-string-a))) (when cmd (eshell-update-markers eshell-last-output-end) (setq input (buffer-substring-no-properties eshell-last-input-start (1- eshell-last-input-end))) (run-hooks 'eshell-input-filter-functions) (and (catch 'eshell-terminal (ignore (if (eshell-invoke-directly cmd) (eval cmd) (eshell-eval-command cmd input)))) (eshell-life-is-too-much))))) (quit (eshell-reset t) (run-hooks 'eshell-post-command-hook) (signal 'quit nil)) (error (eshell-reset t) (eshell-interactive-print (concat (error-message-string err) "\n")) (run-hooks 'eshell-post-command-hook) (insert-and-inherit input))))))))) (defun eshell-parse-command-input (beg end &optional args input-string-b) "Parse the command input from BEG to END. The difference is that `eshell-parse-command' expects a complete command string (and will error if it doesn't get one), whereas this function will inform the caller whether more input is required. - If nil is returned, more input is necessary (probably because a multi-line input string wasn't terminated properly). Otherwise, it will return the parsed command." (let (delim command) (if (setq delim (catch 'eshell-incomplete (ignore (setq command (eshell-parse-command (cons beg end) args t input-string-b))))) (ignore (message "Expecting completion of delimiter %c ..." (if (listp delim) (car delim) delim))) command))) (defun eshell-parse-command (command &optional args toplevel input-string-c) "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." (let* ( eshell--sep-terms (terms (if input-string-c (eshell-parse-arguments--temp-buffer input-string-c) (append (if (consp command) (eshell-parse-arguments (car command) (cdr command)) (let ((here (point)) (inhibit-point-motion-hooks t)) (with-silent-modifications ;; FIXME: Why not use a temporary buffer and avoid this ;; "insert&delete" business? --Stef (insert command) (prog1 (eshell-parse-arguments here (point)) (delete-region here (point)))))) args))) (commands (mapcar (function (lambda (cmd) (setq cmd (if (or (not (car eshell--sep-terms)) (string= (car eshell--sep-terms) ";")) (eshell-parse-pipeline cmd) `(eshell-do-subjob (list ,(eshell-parse-pipeline cmd))))) (setq eshell--sep-terms (cdr eshell--sep-terms)) (if eshell-in-pipeline-p cmd `(eshell-trap-errors ,cmd)))) (eshell-separate-commands terms "[&;]" nil 'eshell--sep-terms))) ) (let ((cmd commands)) (while cmd (if (cdr cmd) (setcar cmd `(eshell-commands ,(car cmd)))) (setq cmd (cdr cmd)))) (if toplevel `(eshell-commands (progn (run-hooks 'eshell-pre-command-hook) (catch 'top-level (progn ,@commands)) (run-hooks 'eshell-post-command-hook))) (macroexp-progn commands)))) (defun eshell-parse-arguments--temp-buffer (input-string-d) "Parse all of the arguments at point from BEG to END. Returns the list of arguments in their raw form. Point is left at the end of the arguments." (with-temp-buffer (insert input-string-d) (let ((inhibit-point-motion-hooks t) (args (list t)) delim) (with-silent-modifications (remove-text-properties (point-min) (point-max) '(arg-begin nil arg-end nil)) (goto-char (point-min)) (if (setq delim (catch 'eshell-incomplete (while (not (eobp)) (let* ((here (point)) (arg (eshell-parse-argument))) (if (= (point) here) (error "Failed to parse argument '%s'" (buffer-substring here (point-max)))) (and arg (nconc args (list arg))))))) (throw 'eshell-incomplete (if (listp delim) delim (list delim (point) (cdr args))))) (cdr args)))))
bug-gnu-emacs <at> gnu.org
:bug#25270
; Package emacs
.
(Fri, 13 May 2022 14:04:02 GMT) Full text and rfc822 format available.Message #8 received at 25270 <at> debbugs.gnu.org (full text, mbox):
From: Lars Ingebrigtsen <larsi <at> gnus.org> To: Keith David Bershatsky <esq <at> lawlist.com> Cc: Jim Porter <jporterbugs <at> gmail.com>, 25270 <at> debbugs.gnu.org Subject: Re: bug#25270: eshell -- programmatically send input -- feature request Date: Fri, 13 May 2022 16:02:51 +0200
Keith David Bershatsky <esq <at> lawlist.com> writes: > As far as I am aware, users of eshell have been limited to sending > input programmatically by inserting the command into the eshell buffer > (at the command prompt) and then executing `eshell-send-input`. Some > users (like myself) may feel that such a solution is lo-tech -- i.e., > not very eloquent. [...] > The following is an example of how this new feature might be implemented: > > SAMPLE USAGE: (eshell-send-input nil nil nil "ls -la /") > > (require 'eshell) > > (defun eshell-send-input (&optional use-region queue-p no-newline input-string-a) (I'm going through old bug reports that unfortunately weren't resolved at the time.) I think this makes sense, but I'm not very familiar with eshell internals, so I've added Jim to the CCs; perhaps he has some comments. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no
bug-gnu-emacs <at> gnu.org
:bug#25270
; Package emacs
.
(Mon, 16 May 2022 05:47:01 GMT) Full text and rfc822 format available.Message #11 received at 25270 <at> debbugs.gnu.org (full text, mbox):
From: Jim Porter <jporterbugs <at> gmail.com> To: Lars Ingebrigtsen <larsi <at> gnus.org>, Keith David Bershatsky <esq <at> lawlist.com> Cc: 25270 <at> debbugs.gnu.org Subject: Re: bug#25270: eshell -- programmatically send input -- feature request Date: Sun, 15 May 2022 22:46:47 -0700
On 5/13/2022 7:02 AM, Lars Ingebrigtsen wrote: > Keith David Bershatsky <esq <at> lawlist.com> writes: > >> As far as I am aware, users of eshell have been limited to sending >> input programmatically by inserting the command into the eshell buffer >> (at the command prompt) and then executing `eshell-send-input`. Some >> users (like myself) may feel that such a solution is lo-tech -- i.e., >> not very eloquent. > > [...] > >> The following is an example of how this new feature might be implemented: >> >> SAMPLE USAGE: (eshell-send-input nil nil nil "ls -la /") >> >> (require 'eshell) >> >> (defun eshell-send-input (&optional use-region queue-p no-newline input-string-a) > > (I'm going through old bug reports that unfortunately weren't resolved > at the time.) > > I think this makes sense, but I'm not very familiar with eshell > internals, so I've added Jim to the CCs; perhaps he has some comments. Hm, I think it's reasonable to have something similar to `comint-send-string' for Eshell, but I'm not quite sure what the best way to do this would be. I think a separate function, like `eshell-send-string', would probably be a nicer API, since it could be called like `comint-send-string'. Also, for the code posted in the original message, I'm not sure the changes to `eshell-parse-command' are needed. It should already let you pass a command string to it. Maybe this is because there's an issue with how `eshell-parse-command' temporarily inserts COMMAND into the buffer (see the FIXME comment in the code in the original message)? If there is, we'd probably have to think quite a bit more about how to resolve it. Some background: I think it would be pretty risky to try to perform Eshell argument parsing anywhere *but* in the contents of the current Eshell buffer, as in `eshell-parse-arguments--temp-buffer' in the original message. See `eshell-with-temp-command' in Emacs 29 (which is what the FIXME comment morphed into), in particular this part: ;; 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. Basically, Eshell is extremely flexible, and argument parsing is handled by `eshell-parse-argument-hook', which can do all sorts of things to change how arguments are parsed, and may even be buffer-local to a particular Eshell instance. Parsing Eshell arguments anywhere but the "target" Eshell buffer is asking for trouble. I'll see if I can put together a patch along the above line in the next couple weeks.
bug-gnu-emacs <at> gnu.org
:bug#25270
; Package emacs
.
(Mon, 16 May 2022 12:14:01 GMT) Full text and rfc822 format available.Message #14 received at 25270 <at> debbugs.gnu.org (full text, mbox):
From: Lars Ingebrigtsen <larsi <at> gnus.org> To: Jim Porter <jporterbugs <at> gmail.com> Cc: Keith David Bershatsky <esq <at> lawlist.com>, 25270 <at> debbugs.gnu.org Subject: Re: bug#25270: eshell -- programmatically send input -- feature request Date: Mon, 16 May 2022 14:13:11 +0200
Jim Porter <jporterbugs <at> gmail.com> writes: > Also, for the code posted in the original message, I'm not sure the > changes to `eshell-parse-command' are needed. It should already let > you pass a command string to it. Maybe this is because there's an > issue with how `eshell-parse-command' temporarily inserts COMMAND into > the buffer (see the FIXME comment in the code in the original > message)? If there is, we'd probably have to think quite a bit more > about how to resolve it. Yes, if I understood correctly, that was the main problem -- inserting things into the buffer to eval it sometimes leaves artefacts (or doesn't work), so it'd be better to talk directly to the underlying shell to get things evalled. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no
bug-gnu-emacs <at> gnu.org
:bug#25270
; Package emacs
.
(Mon, 16 May 2022 17:34:02 GMT) Full text and rfc822 format available.Message #17 received at 25270 <at> debbugs.gnu.org (full text, mbox):
From: Jim Porter <jporterbugs <at> gmail.com> To: Lars Ingebrigtsen <larsi <at> gnus.org> Cc: Keith David Bershatsky <esq <at> lawlist.com>, 25270 <at> debbugs.gnu.org Subject: Re: bug#25270: eshell -- programmatically send input -- feature request Date: Mon, 16 May 2022 10:33:49 -0700
On 5/16/2022 5:13 AM, Lars Ingebrigtsen wrote: > Jim Porter <jporterbugs <at> gmail.com> writes: > >> Also, for the code posted in the original message, I'm not sure the >> changes to `eshell-parse-command' are needed. It should already let >> you pass a command string to it. Maybe this is because there's an >> issue with how `eshell-parse-command' temporarily inserts COMMAND into >> the buffer (see the FIXME comment in the code in the original >> message)? If there is, we'd probably have to think quite a bit more >> about how to resolve it. > > Yes, if I understood correctly, that was the main problem -- inserting > things into the buffer to eval it sometimes leaves artefacts (or doesn't > work), so it'd be better to talk directly to the underlying shell to get > things evalled. I think for talking to Eshell itself, inserting things into the buffer (temporarily) should be ok. Eshell already does this (as of Emacs 29, I think) when parsing some complex commands. If you have some $-expansions inside double-quotes, e.g. 'echo "${echo \"hi there\"}"', Eshell temporarily inserts 'echo "hi there"' into the buffer to parse it, and then removes it before proceeding. For sending input to a child process being run inside Eshell, we wouldn't do all this, since Eshell wouldn't be treating the input as an Eshell command to be parsed. In that case, we could just send the string to the child process directly (possibly with some extra Eshell bookkeeping).
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.