]> git.eshelyaron.com Git - emacs.git/commitdiff
Ensure that Eshell users can run lines of command output as input
authorJim Porter <jporterbugs@gmail.com>
Mon, 6 Feb 2023 05:37:08 +0000 (21:37 -0800)
committerJim Porter <jporterbugs@gmail.com>
Fri, 10 Feb 2023 06:32:51 +0000 (22:32 -0800)
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'.

lisp/eshell/esh-mode.el
lisp/eshell/esh-proc.el
lisp/eshell/esh-util.el
test/lisp/eshell/em-prompt-tests.el
test/lisp/eshell/eshell-tests.el

index 503d9ba1b6307cb03e921f2b701ed74218076479..654e26777e06039a7ae4c3168ebfe3362d59ca7f 100644 (file)
@@ -525,9 +525,7 @@ Putting this function on `eshell-pre-command-hook' will mimic Plan 9's
 (defun eshell-interactive-print (string)
   "Print STRING to the eshell display buffer."
   (when string
-    (add-text-properties 0 (length string)
-                         '(field command-output rear-nonsticky (field))
-                         string)
+    (eshell--mark-as-output 0 (length string) string)
     (eshell-interactive-filter nil string)))
 
 (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)
             (end (point)))
         (beginning-of-line)
-        (buffer-substring (point) end)))))
+        (buffer-substring-no-properties (point) end)))))
 
 (defun eshell-copy-old-input ()
   "Insert after prompt old input at point as new input to be edited."
index 27cd521e82e5d3059419eae64a6b07fdbc1c414a..a86e75027952ffab24114f6b89191788647e9674 100644 (file)
@@ -24,6 +24,7 @@
 ;;; Code:
 
 (require 'esh-io)
+(require 'esh-util)
 
 (defgroup eshell-proc nil
   "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.
 This is done after all necessary filtering has been done."
   (when string
-    (add-text-properties 0 (length string)
-                         '(field command-output rear-nonsticky (field))
-                         string)
+    (eshell--mark-as-output 0 (length string) string)
     (require 'esh-mode)
     (declare-function eshell-interactive-filter "esh-mode" (buffer string))
     (eshell-interactive-filter (if process (process-buffer process)
index 9549e7f1a105c0a539b5ebde21bc100fd0eab1b6..c06857577899feb65a738912887bad8a4ba5d07a 100644 (file)
@@ -132,6 +132,19 @@ function `string-to-number'.")
 (defvar eshell-user-timestamp nil
   "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:
 
 (define-obsolete-variable-alias 'eshell-host-names
@@ -157,6 +170,27 @@ Otherwise, evaluates FORM with no error handling."
         ,@handlers)
     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
   (open close &optional bound reverse-p backslash-p)
   "From point, find the CLOSE delimiter corresponding to OPEN.
index db45e2ae3a73cd648789ea020598d8d8aff03659..257549e40fbb2a6dbb6e2e2f9baedd62f0dfc68f 100644 (file)
@@ -54,8 +54,8 @@
      (should (equal last-input "echo hello\n"))
      (should (equal-including-properties
               last-output
-              (propertize "hello\n" 'rear-nonsticky '(field)
-                          'field 'command-output))))))
+              (apply #'propertize "hello\n"
+                     eshell-command-output-properties))))))
 
 (ert-deftest em-prompt-test/field-properties/no-highlight ()
   "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-including-properties
                 last-output
-                (propertize "hello\n" 'rear-nonsticky '(field)
-                            'field 'command-output)))))))
+                (apply #'propertize "hello\n"
+                       eshell-command-output-properties)))))))
 
 (ert-deftest em-prompt-test/next-previous-prompt ()
   "Check that navigating forward/backward through old prompts works correctly."
index 776cfb9b92fc6c0ad5cf4cda3e844e4e0ff79838..743cc28b9b51a06d254ef702c749b3b05a0bdc5a 100644 (file)
@@ -34,6 +34,8 @@
                            (file-name-directory (or load-file-name
                                                     default-directory))))
 
+(defvar eshell-test-value nil)
+
 ;;; Tests:
 
 (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))
    (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)
 
 ;;; eshell-tests.el ends here