Don't clobber stickiness text properties when printing Eshell prompt
authorJim Porter <jporterbugs@gmail.com>
Thu, 7 Nov 2024 18:08:33 +0000 (10:08 -0800)
committerEshel Yaron <me@eshelyaron.com>
Wed, 20 Nov 2024 16:11:37 +0000 (17:11 +0100)
* lisp/eshell/em-prompt.el (eshell--append-text-property): New
function...
(eshell-emit-prompt): ... use it.

* test/lisp/eshell/em-prompt-tests.el
(em-prompt-test/field-properties/merge-stickiness): New test.
(em-prompt-test/field-properties, em-prompt-test/after-failure): Reorder
stickiness values (bug#74230).

(cherry picked from commit df288d2e4148e6e72f21752a510f98536e7705ac)

lisp/eshell/em-prompt.el
test/lisp/eshell/em-prompt-tests.el

index 7de2bd4dc217c6c6b3c7832822372df1ce0b58de..5ebba49891205b1b316bb2a2b8a3188db43c732a 100644 (file)
@@ -119,6 +119,19 @@ arriving, or after."
     (add-hook 'eshell-post-command-hook 'eshell-emit-prompt nil t)
     (eshell-prompt-mode)))
 
+(defun eshell--append-text-property (start end prop value &optional object)
+  "Append to a text property from START to END.
+PROP is the text property to append to, and VALUE is the list of
+property values to append.  OBJECT is the object to propertize, as with
+`put-text-property' (which see)."
+  (let (next)
+    (while (< start end)
+      (setq next (next-single-property-change start prop object end))
+      (put-text-property start next prop
+                         (append (get-text-property start prop object) value)
+                         object)
+      (setq start next))))
+
 (defun eshell-emit-prompt ()
   "Emit a prompt if eshell is being used interactively."
   (when (boundp 'ansi-color-context-region)
@@ -126,19 +139,16 @@ arriving, or after."
   (run-hooks 'eshell-before-prompt-hook)
   (if (not eshell-prompt-function)
       (set-marker eshell-last-output-end (point))
-    (let ((prompt (funcall eshell-prompt-function)))
-      (add-text-properties
-       0 (length prompt)
-       (if eshell-highlight-prompt
-           '( read-only t
-              field prompt
-              font-lock-face eshell-prompt
-              front-sticky (read-only field font-lock-face)
-              rear-nonsticky (read-only field font-lock-face))
-         '( field prompt
-            front-sticky (field)
-            rear-nonsticky (field)))
-       prompt)
+    (let* ((prompt (funcall eshell-prompt-function))
+           (len (length prompt))
+           (sticky-props '(field)))
+      (put-text-property 0 len 'field 'prompt prompt)
+      (when eshell-highlight-prompt
+        (add-text-properties
+         0 len '(read-only t font-lock-face eshell-prompt) prompt)
+        (setq sticky-props `(read-only font-lock-face . ,sticky-props)))
+      (eshell--append-text-property 0 len 'front-sticky sticky-props prompt)
+      (eshell--append-text-property 0 len 'rear-nonsticky sticky-props prompt)
       (eshell-interactive-filter nil prompt)))
   (run-hooks 'eshell-after-prompt-hook))
 
index fbadade061f67b32b6019bd99e926c37785c5579..1c6e8e02293a2ab9d1ba8840325dbea0038b3e96 100644 (file)
@@ -57,8 +57,8 @@
                'read-only t
                'field 'prompt
                'font-lock-face 'eshell-prompt
-               'front-sticky '(read-only field font-lock-face)
-               'rear-nonsticky '(read-only field font-lock-face))))
+               'front-sticky '(read-only font-lock-face field)
+               'rear-nonsticky '(read-only font-lock-face field))))
      (should (equal last-input "echo hello\n"))
      (should (equal-including-properties
               last-output
@@ -88,6 +88,33 @@ This tests the case when `eshell-highlight-prompt' is nil."
                 (apply #'propertize "hello\n"
                        eshell-command-output-properties)))))))
 
+(ert-deftest em-prompt-test/field-properties/merge-stickiness ()
+  "Check that stickiness properties are properly merged on Eshell prompts."
+  (let ((eshell-prompt-function
+         (lambda ()
+           (concat (propertize (eshell/pwd) 'front-sticky '(front))
+                   (propertize "$ " 'rear-nonsticky '(rear))))))
+    (with-temp-eshell
+     (eshell-insert-command "echo hello")
+     (let ((last-prompt (field-string (1- eshell-last-input-start))))
+       (should (equal-including-properties
+                last-prompt
+                (concat
+                 (propertize
+                  (directory-file-name default-directory)
+                  'read-only t
+                  'field 'prompt
+                  'font-lock-face 'eshell-prompt
+                  'front-sticky '(front read-only font-lock-face field)
+                  'rear-nonsticky '(read-only font-lock-face field))
+                 (propertize
+                  "$ "
+                  'read-only t
+                  'field 'prompt
+                  'font-lock-face 'eshell-prompt
+                  'front-sticky '(read-only font-lock-face field)
+                  'rear-nonsticky '(rear read-only font-lock-face field)))))))))
+
 (ert-deftest em-prompt-test/after-failure ()
   "Check that current prompt shows the exit code of the last failed command."
   (with-temp-eshell
@@ -104,8 +131,8 @@ This tests the case when `eshell-highlight-prompt' is nil."
                'read-only t
                'field 'prompt
                'font-lock-face 'eshell-prompt
-               'front-sticky '(read-only field font-lock-face)
-               'rear-nonsticky '(read-only field font-lock-face)))))))
+               'front-sticky '(read-only font-lock-face field)
+               'rear-nonsticky '(read-only font-lock-face field)))))))
 
 \f
 ;; Prompt navigation