Subject: bug#60936: 30.0.50; ERC >5.5: Add erc-fill style based on visual-line-mode

From: "J.P." <[email-masked]>
Date: Sun, 21 May 2023 21:20:57 -0700

The following commit introduced a regression:

  commit 05f6fdb9e7893329baff675bd31fb36ad64c756d
  Author: F. Jason Park <[email-masked]>

  Preserve ERC prompt and its bounding markers

  1 file changed, 27 insertions(+), 22 deletions(-)
  lisp/erc/erc.el | 49 +++++++++++++++++++++++++++----------------------

To reproduce from emacs -Q:

  1. Eval:

     (require 'erc)
     (setq erc-prompt (lambda () (format-time-string "%T>"))
           erc-autojoin-channels-alist '((ErgoTestnet "#test")))
     (erc-tls :server "testnet.ergo.chat")

  2. In #test, note the timestamp in the prompt
  3. Say "something" RET
  4. Notice that the prompt doesn't change, whereas in ERC 5.5 and
     earlier, it would change on every outgoing message

The attached patch fixes the regression and changes the behavior to
redraw the prompt on every incoming message as well, but only when
`erc-prompt' is a function. Doing this should bring us one step closer
to being able to look at

  bug#51082 erc-prompt: support substitution patterns "%target" and "%network"     

However, we'd still be missing user-mode tracking, which seems fairly
trivial to add.

>From 292f741020f6dc39103803d6ca0cb8b7fb9e2b61 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Thu, 18 May 2023 23:47:27 -0700
Subject: [PATCH] [5.6] Recompute erc-prompt when inserting messages

* lisp/erc/erc.el (erc--refresh-prompt): New function for redrawing
the prompt in a couple select places.
(erc-display-line-1, erc-display-msg): Replace the prompt after
inserting messages.
* test/lisp/erc/erc-tests.el (erc--refresh-prompt): New
test.  (Bug#60936)
---
 lisp/erc/erc.el            | 16 +++++-
 test/lisp/erc/erc-tests.el | 99 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 113 insertions(+), 2 deletions(-)

diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 495e25212ce..16bb2c38b1b 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -2787,6 +2787,18 @@ erc--assert-input-bounds
           (cl-assert (< erc-insert-marker erc-input-marker))
           (cl-assert (= (field-end erc-insert-marker) erc-input-marker)))))
 
+(defun erc--refresh-prompt ()
+  "Re-render ERC's prompt when the option `erc-prompt' is a function."
+  (erc--assert-input-bounds)
+  (when (functionp erc-prompt)
+    (save-excursion
+      (goto-char erc-insert-marker)
+      ;; Avoid `erc-prompt' (the named function), which appends a
+      ;; space, and `erc-display-prompt', which propertizes all but
+      ;; that space.
+      (insert-and-inherit (funcall erc-prompt))
+      (delete-region (point) (1- erc-input-marker)))))
+
 (defun erc-display-line-1 (string buffer)
   "Display STRING in `erc-mode' BUFFER.
 Auxiliary function used in `erc-display-line'.  The line gets filtered to
@@ -2830,7 +2842,7 @@ erc-display-line-1
                   (when erc-remove-parsed-property
                     (remove-text-properties (point-min) (point-max)
                                             '(erc-parsed nil))))
-                (erc--assert-input-bounds)))))
+                (erc--refresh-prompt)))))
         (run-hooks 'erc-insert-done-hook)
         (erc-update-undo-list (- (or (marker-position erc-insert-marker)
                                      (point-max))
@@ -6452,7 +6464,7 @@ erc-display-msg
           (narrow-to-region insert-position (point))
           (run-hooks 'erc-send-modify-hook)
           (run-hooks 'erc-send-post-hook))
-        (erc--assert-input-bounds)))))
+        (erc--refresh-prompt)))))
 
 (defun erc-command-symbol (command)
   "Return the ERC command symbol for COMMAND if it exists and is bound."
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index b624186d88d..1c75f35e1b5 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -269,6 +269,105 @@ erc-hide-prompt
       (kill-buffer "bob")
       (kill-buffer "ServNet"))))
 
+(ert-deftest erc--refresh-prompt ()
+  (let* ((counter 0)
+         (erc-prompt (lambda ()
+                       (format "%s %d>"
+                               (erc-format-target-and/or-network)
+                               (cl-incf counter))))
+         erc-accidental-paste-threshold-seconds
+         erc-insert-modify-hook
+         erc--input-review-functions
+         erc-send-completed-hook)
+
+    (ert-info ("Server buffer")
+      (with-current-buffer (get-buffer-create "ServNet")
+        (erc-tests--send-prep)
+        (goto-char erc-insert-marker)
+        (should (looking-at-p "ServNet 3>"))
+        (erc-tests--set-fake-server-process "sleep" "1")
+        (set-process-sentinel erc-server-process #'ignore)
+        (setq erc-network 'ServNet
+              erc-server-current-nick "tester"
+              erc-networks--id (erc-networks--id-create nil)
+              erc-server-users (make-hash-table :test 'equal))
+        (set-process-query-on-exit-flag erc-server-process nil)
+        ;; Incoming message redraws prompt
+        (erc-display-message nil 'notice nil "Welcome")
+        (should (looking-at-p "ServNet 4>"))
+        ;; Say something
+        (save-excursion (goto-char erc-input-marker)
+                        (insert "Howdy")
+                        (erc-send-current-line)
+                        (forward-line -1)
+                        (should (looking-at "No target"))
+                        (forward-line -1)
+                        (should (looking-at "<tester> Howdy")))
+        (should (looking-at-p "ServNet 6>"))
+        ;; Space after prompt is unpropertized
+        (should (get-text-property (1- erc-input-marker) 'erc-prompt))
+        (should-not (get-text-property erc-input-marker 'erc-prompt))
+        ;; No sign of old prompts
+        (save-excursion
+          (goto-char (point-min))
+          (should-not (search-forward (rx (any "3-5") ">") nil t)))))
+
+    (ert-info ("Channel buffer")
+      (with-current-buffer (get-buffer-create "#chan")
+        (erc-tests--send-prep)
+        (goto-char erc-insert-marker)
+        (should (looking-at-p "#chan 9>"))
+        (setq erc-server-process (buffer-local-value 'erc-server-process
+                                                     (get-buffer "ServNet"))
+              erc-networks--id (erc-with-server-buffer erc-networks--id)
+              erc--target (erc--target-from-string "#chan")
+              erc-default-recipients (list "#chan")
+              erc-channel-users (make-hash-table :test 'equal))
+        (erc-update-current-channel-member "alice" "alice")
+        (erc-update-current-channel-member "bob" "bob")
+        (erc-update-current-channel-member "tester" "tester")
+        (erc-display-message nil nil (current-buffer)
+                             (erc-format-privmessage "alice" "Hi" nil t))
+        (should (looking-at-p "#chan@ServNet 10>"))
+        (save-excursion (goto-char erc-input-marker)
+                        (insert "Howdy")
+                        (erc-send-current-line)
+                        (forward-line -1)
+                        (should (looking-at "<tester> Howdy")))
+        (should (looking-at-p "#chan@ServNet 11>"))
+        (save-excursion (goto-char erc-input-marker)
+                        (insert "/query bob")
+                        (erc-send-current-line))
+        ;; Query does not redraw (nor /help, only message input)
+        (should (looking-at-p "#chan@ServNet 11>"))
+        ;; No sign of old prompts
+        (save-excursion
+          (goto-char (point-min))
+          (should-not (search-forward (rx (or "9" "10") ">") nil t)))))
+
+    (ert-info ("Query buffer")
+      (with-current-buffer (get-buffer "bob")
+        (goto-char erc-insert-marker)
+        (should (looking-at-p "bob@ServNet 14>"))
+        (erc-display-message nil nil (current-buffer)
+                             (erc-format-privmessage "bob" "Hi" nil t))
+        (should (looking-at-p "bob@ServNet 15>"))
+        (save-excursion (goto-char erc-input-marker)
+                        (insert "Howdy")
+                        (erc-send-current-line)
+                        (forward-line -1)
+                        (should (looking-at "<tester> Howdy")))
+        (should (looking-at-p "bob@ServNet 16>"))
+        ;; No sign of old prompts
+        (save-excursion
+          (goto-char (point-min))
+          (should-not (search-forward (rx (or "14" "15") ">") nil t)))))
+
+    (when noninteractive
+      (kill-buffer "#chan")
+      (kill-buffer "bob")
+      (kill-buffer "ServNet"))))
+
 (ert-deftest erc--initialize-markers ()
   (let ((proc (start-process "true" (current-buffer) "true"))
         erc-modules
-- 
2.40.0