[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#25270: eshell -- programmatically send input -- feature request
From: |
Keith David Bershatsky |
Subject: |
bug#25270: 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)))))
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- bug#25270: eshell -- programmatically send input -- feature request,
Keith David Bershatsky <=