mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-15 18:40:39 -08:00
Ensure that Eshell users can run lines of command output as input
Previously, this failed to work properly because any additional input the user entered would have no 'field' property, confusing 'eshell-get-old-input'. To fix this, we simply ensure that any user-entered text in the output field retains said output field (bug#61310). * lisp/eshell/esh-util.el (eshell-command-output-properties): New variable. (eshell--mark-as-output, eshell--mark-yanked-as-output): New functions, mostly copied from comint. * lisp/eshell/esh-proc.el (eshell-interactive-process-filter): * lisp/eshell/esh-mode.el (eshell-interactive-print): Call 'eshell--mark-as-output'. (eshell-get-old-input): Remove properties from the returned string just to be safe. * test/lisp/eshell/eshell-tests.el (eshell-test-value): New variable. (eshell-test/get-old-input/rerun-command) (eshell-test/get-old-input/run-output): New tests. * test/lisp/eshell/em-prompt-tests.el (em-prompt-test/field-properties) (em-prompt-test/field-properties/no-highlight): Use 'eshell-command-output-properties'.
This commit is contained in:
parent
c53255f677
commit
ab7c2f8092
5 changed files with 70 additions and 11 deletions
|
|
@ -525,9 +525,7 @@ Putting this function on `eshell-pre-command-hook' will mimic Plan 9's
|
||||||
(defun eshell-interactive-print (string)
|
(defun eshell-interactive-print (string)
|
||||||
"Print STRING to the eshell display buffer."
|
"Print STRING to the eshell display buffer."
|
||||||
(when string
|
(when string
|
||||||
(add-text-properties 0 (length string)
|
(eshell--mark-as-output 0 (length string) string)
|
||||||
'(field command-output rear-nonsticky (field))
|
|
||||||
string)
|
|
||||||
(eshell-interactive-filter nil string)))
|
(eshell-interactive-filter nil string)))
|
||||||
|
|
||||||
(defsubst eshell-begin-on-new-line ()
|
(defsubst eshell-begin-on-new-line ()
|
||||||
|
|
@ -891,7 +889,7 @@ If USE-CURRENT-REGION is non-nil, return the current region."
|
||||||
(let ((inhibit-field-text-motion)
|
(let ((inhibit-field-text-motion)
|
||||||
(end (point)))
|
(end (point)))
|
||||||
(beginning-of-line)
|
(beginning-of-line)
|
||||||
(buffer-substring (point) end)))))
|
(buffer-substring-no-properties (point) end)))))
|
||||||
|
|
||||||
(defun eshell-copy-old-input ()
|
(defun eshell-copy-old-input ()
|
||||||
"Insert after prompt old input at point as new input to be edited."
|
"Insert after prompt old input at point as new input to be edited."
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
(require 'esh-io)
|
(require 'esh-io)
|
||||||
|
(require 'esh-util)
|
||||||
|
|
||||||
(defgroup eshell-proc nil
|
(defgroup eshell-proc nil
|
||||||
"When Eshell invokes external commands, it always does so
|
"When Eshell invokes external commands, it always does so
|
||||||
|
|
@ -411,9 +412,7 @@ Used only on systems which do not support async subprocesses.")
|
||||||
"Send the output from PROCESS (STRING) to the interactive display.
|
"Send the output from PROCESS (STRING) to the interactive display.
|
||||||
This is done after all necessary filtering has been done."
|
This is done after all necessary filtering has been done."
|
||||||
(when string
|
(when string
|
||||||
(add-text-properties 0 (length string)
|
(eshell--mark-as-output 0 (length string) string)
|
||||||
'(field command-output rear-nonsticky (field))
|
|
||||||
string)
|
|
||||||
(require 'esh-mode)
|
(require 'esh-mode)
|
||||||
(declare-function eshell-interactive-filter "esh-mode" (buffer string))
|
(declare-function eshell-interactive-filter "esh-mode" (buffer string))
|
||||||
(eshell-interactive-filter (if process (process-buffer process)
|
(eshell-interactive-filter (if process (process-buffer process)
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,19 @@ function `string-to-number'.")
|
||||||
(defvar eshell-user-timestamp nil
|
(defvar eshell-user-timestamp nil
|
||||||
"A timestamp of when the user file was read.")
|
"A timestamp of when the user file was read.")
|
||||||
|
|
||||||
|
(defvar eshell-command-output-properties
|
||||||
|
`( field command-output
|
||||||
|
front-sticky (field)
|
||||||
|
rear-nonsticky (field)
|
||||||
|
;; Text inserted by a user in the middle of process output
|
||||||
|
;; should be marked as output. This is needed for commands
|
||||||
|
;; such as `yank' or `just-one-space' which don't use
|
||||||
|
;; `insert-and-inherit' and thus bypass default text property
|
||||||
|
;; inheritance.
|
||||||
|
insert-in-front-hooks (,#'eshell--mark-as-output
|
||||||
|
,#'eshell--mark-yanked-as-output))
|
||||||
|
"A list of text properties to apply to command output.")
|
||||||
|
|
||||||
;;; Obsolete variables:
|
;;; Obsolete variables:
|
||||||
|
|
||||||
(define-obsolete-variable-alias 'eshell-host-names
|
(define-obsolete-variable-alias 'eshell-host-names
|
||||||
|
|
@ -157,6 +170,27 @@ Otherwise, evaluates FORM with no error handling."
|
||||||
,@handlers)
|
,@handlers)
|
||||||
form))
|
form))
|
||||||
|
|
||||||
|
(defun eshell--mark-as-output (start end &optional object)
|
||||||
|
"Mark the text from START to END as Eshell output.
|
||||||
|
OBJECT can be a buffer or string. If nil, mark the text in the
|
||||||
|
current buffer."
|
||||||
|
(with-silent-modifications
|
||||||
|
(add-text-properties start end eshell-command-output-properties
|
||||||
|
object)))
|
||||||
|
|
||||||
|
(defun eshell--mark-yanked-as-output (start end)
|
||||||
|
"Mark yanked text from START to END as Eshell output."
|
||||||
|
;; `yank' removes the field text property from the text it inserts
|
||||||
|
;; due to `yank-excluded-properties', so arrange for this text
|
||||||
|
;; property to be reapplied in the `after-change-functions'.
|
||||||
|
(letrec ((hook
|
||||||
|
(lambda (start1 end1 _len1)
|
||||||
|
(remove-hook 'after-change-functions hook t)
|
||||||
|
(when (and (= start start1)
|
||||||
|
(= end end1))
|
||||||
|
(eshell--mark-as-output start1 end1)))))
|
||||||
|
(add-hook 'after-change-functions hook nil t)))
|
||||||
|
|
||||||
(defun eshell-find-delimiter
|
(defun eshell-find-delimiter
|
||||||
(open close &optional bound reverse-p backslash-p)
|
(open close &optional bound reverse-p backslash-p)
|
||||||
"From point, find the CLOSE delimiter corresponding to OPEN.
|
"From point, find the CLOSE delimiter corresponding to OPEN.
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@
|
||||||
(should (equal last-input "echo hello\n"))
|
(should (equal last-input "echo hello\n"))
|
||||||
(should (equal-including-properties
|
(should (equal-including-properties
|
||||||
last-output
|
last-output
|
||||||
(propertize "hello\n" 'rear-nonsticky '(field)
|
(apply #'propertize "hello\n"
|
||||||
'field 'command-output))))))
|
eshell-command-output-properties))))))
|
||||||
|
|
||||||
(ert-deftest em-prompt-test/field-properties/no-highlight ()
|
(ert-deftest em-prompt-test/field-properties/no-highlight ()
|
||||||
"Check that field properties are properly set on Eshell output/prompts.
|
"Check that field properties are properly set on Eshell output/prompts.
|
||||||
|
|
@ -77,8 +77,8 @@ This tests the case when `eshell-highlight-prompt' is nil."
|
||||||
(should (equal last-input "echo hello\n"))
|
(should (equal last-input "echo hello\n"))
|
||||||
(should (equal-including-properties
|
(should (equal-including-properties
|
||||||
last-output
|
last-output
|
||||||
(propertize "hello\n" 'rear-nonsticky '(field)
|
(apply #'propertize "hello\n"
|
||||||
'field 'command-output)))))))
|
eshell-command-output-properties)))))))
|
||||||
|
|
||||||
(ert-deftest em-prompt-test/next-previous-prompt ()
|
(ert-deftest em-prompt-test/next-previous-prompt ()
|
||||||
"Check that navigating forward/backward through old prompts works correctly."
|
"Check that navigating forward/backward through old prompts works correctly."
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@
|
||||||
(file-name-directory (or load-file-name
|
(file-name-directory (or load-file-name
|
||||||
default-directory))))
|
default-directory))))
|
||||||
|
|
||||||
|
(defvar eshell-test-value nil)
|
||||||
|
|
||||||
;;; Tests:
|
;;; Tests:
|
||||||
|
|
||||||
(ert-deftest eshell-test/pipe-headproc ()
|
(ert-deftest eshell-test/pipe-headproc ()
|
||||||
|
|
@ -160,6 +162,32 @@ insert the queued one at the next prompt, and finally run it."
|
||||||
(beginning-of-line))
|
(beginning-of-line))
|
||||||
(should (string= (eshell-get-old-input) "echo alpha"))))
|
(should (string= (eshell-get-old-input) "echo alpha"))))
|
||||||
|
|
||||||
|
(ert-deftest eshell-test/get-old-input/rerun-command ()
|
||||||
|
"Test that we can rerun an old command when point is on it."
|
||||||
|
(with-temp-eshell
|
||||||
|
(let ((eshell-test-value "first"))
|
||||||
|
(eshell-match-command-output "echo $eshell-test-value" "first"))
|
||||||
|
;; Go to the previous prompt.
|
||||||
|
(forward-line -2)
|
||||||
|
(let ((inhibit-field-text-motion t))
|
||||||
|
(end-of-line))
|
||||||
|
;; Rerun the command, but with a different variable value.
|
||||||
|
(let ((eshell-test-value "second"))
|
||||||
|
(eshell-send-input))
|
||||||
|
(eshell-match-output "second")))
|
||||||
|
|
||||||
|
(ert-deftest eshell-test/get-old-input/run-output ()
|
||||||
|
"Test that we can run a line of output as a command when point is on it."
|
||||||
|
(with-temp-eshell
|
||||||
|
(eshell-match-command-output "echo \"echo there\"" "echo there")
|
||||||
|
;; Go to the output, and insert "hello" after "echo".
|
||||||
|
(forward-line -1)
|
||||||
|
(forward-word)
|
||||||
|
(insert " hello")
|
||||||
|
;; Run the line as a command.
|
||||||
|
(eshell-send-input)
|
||||||
|
(eshell-match-output "(\"hello\" \"there\")")))
|
||||||
|
|
||||||
(provide 'eshell-tests)
|
(provide 'eshell-tests)
|
||||||
|
|
||||||
;;; eshell-tests.el ends here
|
;;; eshell-tests.el ends here
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue