From df288d2e4148e6e72f21752a510f98536e7705ac Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Thu, 7 Nov 2024 10:08:33 -0800 Subject: [PATCH] Don't clobber stickiness text properties when printing Eshell prompt * 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). --- lisp/eshell/em-prompt.el | 36 ++++++++++++++++++----------- test/lisp/eshell/em-prompt-tests.el | 35 ++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el index de62b5c7d97..37970ac0ba5 100644 --- a/lisp/eshell/em-prompt.el +++ b/lisp/eshell/em-prompt.el @@ -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)) diff --git a/test/lisp/eshell/em-prompt-tests.el b/test/lisp/eshell/em-prompt-tests.el index fbadade061f..1c6e8e02293 100644 --- a/test/lisp/eshell/em-prompt-tests.el +++ b/test/lisp/eshell/em-prompt-tests.el @@ -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))))))) ;; Prompt navigation