Package: emacs;
Reported by: Keith David Bershatsky <esq <at> lawlist.com>
Date: Sun, 25 Dec 2016 19:02:01 UTC
Severity: wishlist
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)))))
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.