diff options
Diffstat (limited to 'lisp/eshell/esh-mode.el')
-rw-r--r-- | lisp/eshell/esh-mode.el | 79 |
1 files changed, 78 insertions, 1 deletions
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index 87166ef3bf1..04680e4305c 100644 --- a/lisp/eshell/esh-mode.el +++ b/lisp/eshell/esh-mode.el @@ -63,6 +63,7 @@ (require 'esh-cmd) (require 'esh-arg) ;For eshell-parse-arguments (require 'cl-lib) +(require 'subr-x) (defgroup eshell-mode nil "This module contains code for handling input from the user." @@ -105,7 +106,8 @@ The input is contained in the region from `eshell-last-input-start' to "If t, send any input immediately to a subprocess." :type 'boolean) -(defcustom eshell-expand-input-functions nil +(defcustom eshell-expand-input-functions + '(eshell-expand-to-eshell-shell-command) "Functions to call before input is parsed. Each function is passed two arguments, which bounds the region of the current input text." @@ -671,6 +673,81 @@ newline." (run-hooks 'eshell-post-command-hook) (insert-and-inherit input))))))))) +(defun eshell-shell-command (command) + "Execute COMMAND using the operating system shell." + (throw 'eshell-replace-command + (with-connection-local-variables + (eshell-parse-command shell-file-name + (list shell-command-switch command))))) + +(defun eshell-expand-to-eshell-shell-command (beg end) + "Expand Eshell input starting with '||' to use `eshell-shell-command'. + +This is intended to provide a convenient way to bypass Eshell's +own pipelining which can be slow when executing shell pipelines +which move a lot of data. It is also useful to avoid +roundtripping data when running pipelines on remote hosts. + +For example, this function expands the input + + || cat *.ogg | my-cool-decoder >file + +to + + eshell-shell-command \"cat *.ogg | my-cool-decoder >file\" + +Executing the latter command will not copy all the data in the +*.ogg files into Emacs buffers, as would normally happen with +Eshell's own pipelining. + +This function tries to extract Eshell-specific redirects and +avoids passing these to the operating system shell. For example, + + || foo | bar >>#<buffer scratch> + +will be expanded to + + eshell-shell-command \"foo | bar\" >>#<buffer scratch> + +This function works well in combination with adding +`eshell-restore-unexpanded-input' to `eshell-input-filter-functions'." + (save-excursion + (goto-char beg) + (when (looking-at "||\\s-*") + (let ((end (copy-marker end)) ; needs to be a marker + redirects) + ;; drop the || + (delete-region beg (match-end 0)) + ;; extract Eshell-specific redirects + (while (search-forward-regexp "[0-9]?>+&?[0-9]?\\s-*\\S-" nil t) + (let ((beg (match-beginning 0))) + (forward-char -1) ; start from the redirect target + (when-let ((end (cond + ;; this is a redirect to a process or a + ;; buffer + ((looking-at "#<") + (forward-char 1) + (1+ (eshell-find-delimiter ?\< ?\>))) + ;; this is a redirect to a virtual target + ((and (looking-at "/\\S-+") + (assoc (match-string 0) + eshell-virtual-targets)) + (match-end 0))))) + (push (buffer-substring-no-properties beg end) redirects) + (delete-region beg end) + (just-one-space)))) + ;; wrap the remaining text and reinstate Eshell redirects + (let ((pipeline (string-trim + (buffer-substring-no-properties beg end)))) + (delete-region beg end) + (insert "eshell-shell-command ") + (prin1 pipeline (current-buffer)) + (when redirects + (insert " " (string-join (nreverse redirects) " ")))))))) + +(custom-add-option 'eshell-expand-input-functions + 'eshell-expand-to-eshell-shell-command) + (defsubst eshell-kill-new () "Add the last input text to the kill ring." (kill-ring-save eshell-last-input-start eshell-last-input-end)) |