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

From: "J.P." <[email-masked]>
Date: Thu, 19 Oct 2023 07:02:44 -0700

v3 (erc-display-line redux). Properly offset renarrowed region after
inserting initial date stamp in `erc-insert-timestamp-left-and-right'.
Don't displace third-party markers when inserting left-sided stamps in
`erc-stamp--display-margin-mode'.

The first bug was introduced by

  c68dc7786fc * Manage some text props for ERC insertion-hook members

and causes right-sided stamps to appear inside the prompt, among other
unpleasant things (see third patch). Thanks to Corwin for spotting this.
The other bug has been around a bit longer, likely since

  63d8b2a59a4 * Make erc-fill-wrap work with left-sided stamps

It has the potential to break packages that place markers in
modification hooks (see last patch).

>From 15f2e73c4022edc1d5ba0ad9c2dea69bbabe3a97 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Thu, 19 Oct 2023 06:20:30 -0700
Subject: [PATCH 0/4] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (4):
  ; Mark erc-log test as :unstable
  [5.6] Restore missing metadata props in erc-display-line
  [5.6] Fix right stamps commingling with erc-prompt
  [5.6] Respect user markers in erc--insert-timestamp-left

 etc/ERC-NEWS                                  |  23 +++
 lisp/erc/erc-fill.el                          |   3 +-
 lisp/erc/erc-stamp.el                         |  20 ++-
 lisp/erc/erc.el                               | 146 +++++++++++-------
 test/lisp/erc/erc-fill-tests.el               |  57 +++----
 test/lisp/erc/erc-networks-tests.el           |   2 +-
 .../lisp/erc/erc-scenarios-display-message.el |  64 ++++++++
 test/lisp/erc/erc-scenarios-log.el            |   2 +-
 test/lisp/erc/erc-scenarios-stamp.el          |  90 +++++++++++
 test/lisp/erc/erc-tests.el                    |  63 ++++++++
 .../base/display-message/multibuf.eld         |  45 ++++++
 .../resources/base/renick/queries/solo.eld    |   2 +-
 .../base/reuse-buffers/channel/barnet.eld     |   2 +-
 .../base/reuse-buffers/channel/foonet.eld     |   2 +-
 .../erc/resources/erc-scenarios-common.el     |   4 +-
 .../fill/snapshots/merge-01-start.eld         |   2 +-
 .../fill/snapshots/merge-02-right.eld         |   2 +-
 .../fill/snapshots/merge-wrap-01.eld          |   2 +-
 .../fill/snapshots/monospace-01-start.eld     |   2 +-
 .../fill/snapshots/monospace-02-right.eld     |   2 +-
 .../fill/snapshots/monospace-03-left.eld      |   2 +-
 .../fill/snapshots/monospace-04-reset.eld     |   2 +-
 .../fill/snapshots/spacing-01-mono.eld        |   2 +-
 .../fill/snapshots/stamps-left-01.eld         |   2 +-
 24 files changed, 437 insertions(+), 106 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-display-message.el
 create mode 100644 test/lisp/erc/erc-scenarios-stamp.el
 create mode 100644 test/lisp/erc/resources/base/display-message/multibuf.eld

Interdiff:
diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el
index 57fd7f39e50..b515513dcb7 100644
--- a/lisp/erc/erc-stamp.el
+++ b/lisp/erc/erc-stamp.el
@@ -492,8 +492,11 @@ erc--conceal-prompt
     (put-text-property erc-insert-marker (1- erc-input-marker)
                        'display `((margin left-margin) ,prompt))))
 
-(cl-defmethod erc-insert-timestamp-left (string)
+(defun erc-insert-timestamp-left (string)
   "Insert timestamps at the beginning of the line."
+  (erc--insert-timestamp-left string))
+
+(cl-defmethod erc--insert-timestamp-left (string)
   (goto-char (point-min))
   (let* ((ignore-p (and erc-timestamp-only-if-changed-flag
 			(string-equal string erc-timestamp-last-inserted)))
@@ -504,13 +507,12 @@ erc-insert-timestamp-left
     (erc-put-text-property 0 len 'invisible erc-stamp--invisible-property s)
     (insert s)))
 
-(cl-defmethod erc-insert-timestamp-left
+(cl-defmethod erc--insert-timestamp-left
   (string &context (erc-stamp--display-margin-mode (eql t)))
   (unless (and erc-timestamp-only-if-changed-flag
                (string-equal string erc-timestamp-last-inserted))
     (goto-char (point-min))
-    (insert-before-markers-and-inherit
-     (setq erc-timestamp-last-inserted string))
+    (insert-and-inherit (setq erc-timestamp-last-inserted string))
     (dolist (p erc-stamp--inherited-props)
       (when-let ((v (get-text-property (point) p)))
         (put-text-property (point-min) (point) p v)))
@@ -704,10 +706,12 @@ erc-insert-timestamp-left-and-right
   (unless erc-stamp--date-format-end
     (add-hook 'erc-insert-pre-hook #'erc-stamp--lr-date-on-pre-modify -95 t)
     (add-hook 'erc-send-pre-functions #'erc-stamp--lr-date-on-pre-modify -95 t)
-    (let ((erc--insert-marker (point-min-marker)))
+    (let ((erc--insert-marker (point-min-marker))
+          (end-marker (point-max-marker)))
       (set-marker-insertion-type erc--insert-marker t)
       (erc-stamp--lr-date-on-pre-modify nil)
-      (narrow-to-region erc--insert-marker (point-max))
+      (narrow-to-region erc--insert-marker end-marker)
+      (set-marker end-marker nil)
       (set-marker erc--insert-marker nil)))
   (let* ((ct (or erc-stamp--current-time (erc-stamp--current-time)))
          (ts-right (with-suppressed-warnings
diff --git a/test/lisp/erc/erc-fill-tests.el b/test/lisp/erc/erc-fill-tests.el
index f6c4c268017..80f5fd22ac6 100644
--- a/test/lisp/erc/erc-fill-tests.el
+++ b/test/lisp/erc/erc-fill-tests.el
@@ -203,36 +203,39 @@ erc-fill-wrap--monospace
   (unless (>= emacs-major-version 29)
     (ert-skip "Emacs version too low, missing `buffer-text-pixel-size'"))
 
-  (erc-fill-tests--wrap-populate
-
-   (lambda ()
-     (should (= erc-fill--wrap-value 27))
-     (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
-     (erc-fill-tests--compare "monospace-01-start")
-
-     (ert-info ("Shift right by one (plus)")
-       ;; Args are all `erc-fill-wrap-nudge' +1 because interactive "p"
-       (ert-with-message-capture messages
-         ;; M-x erc-fill-wrap-nudge RET =
-         (ert-simulate-command '(erc-fill-wrap-nudge 2))
-         (should (string-match (rx "for further adjustment") messages)))
-       (should (= erc-fill--wrap-value 29))
-       (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
-       (erc-fill-tests--compare "monospace-02-right"))
-
-     (ert-info ("Shift left by five")
-       ;; "M-x erc-fill-wrap-nudge RET -----"
-       (ert-simulate-command '(erc-fill-wrap-nudge -4))
-       (should (= erc-fill--wrap-value 25))
-       (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
-       (erc-fill-tests--compare "monospace-03-left"))
+  (let ((erc-prompt (lambda () "ABC>")))
+    (erc-fill-tests--wrap-populate
 
-     (ert-info ("Reset")
-       ;; M-x erc-fill-wrap-nudge RET 0
-       (ert-simulate-command '(erc-fill-wrap-nudge 0))
+     (lambda ()
        (should (= erc-fill--wrap-value 27))
        (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
-       (erc-fill-tests--compare "monospace-04-reset")))))
+       (erc-fill-tests--compare "monospace-01-start")
+
+       (ert-info ("Shift right by one (plus)")
+         ;; Args are all `erc-fill-wrap-nudge' +1 because interactive "p"
+         (ert-with-message-capture messages
+           ;; M-x erc-fill-wrap-nudge RET =
+           (ert-simulate-command '(erc-fill-wrap-nudge 2))
+           (should (string-match (rx "for further adjustment") messages)))
+         (should (= erc-fill--wrap-value 29))
+         (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
+         (erc-fill-tests--compare "monospace-02-right"))
+
+       (ert-info ("Shift left by five")
+         ;; "M-x erc-fill-wrap-nudge RET -----"
+         (ert-simulate-command '(erc-fill-wrap-nudge -4))
+         (should (= erc-fill--wrap-value 25))
+         (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
+         (erc-fill-tests--compare "monospace-03-left"))
+
+       (ert-info ("Reset")
+         ;; M-x erc-fill-wrap-nudge RET 0
+         (ert-simulate-command '(erc-fill-wrap-nudge 0))
+         (should (= erc-fill--wrap-value 27))
+         (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
+         (erc-fill-tests--compare "monospace-04-reset"))
+
+       (erc--assert-input-bounds)))))
 
 (defun erc-fill-tests--simulate-refill ()
   ;; Simulate `erc-fill-wrap-refill-buffer' synchronously and without
diff --git a/test/lisp/erc/erc-scenarios-stamp.el b/test/lisp/erc/erc-scenarios-stamp.el
new file mode 100644
index 00000000000..d6b5d868ce5
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-stamp.el
@@ -0,0 +1,90 @@
+;;; erc-scenarios-stamp.el --- Misc `erc-stamp' scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(require 'erc-stamp)
+
+(defvar erc-scenarios-stamp--user-marker nil)
+
+(defun erc-scenarios-stamp--on-post-modify ()
+  (when-let (((erc--check-msg-prop 'erc-cmd 4)))
+    (set-marker erc-scenarios-stamp--user-marker (point-max))
+    (ert-info ("User marker correctly placed at `erc-insert-marker'")
+      (should (= ?\n (char-before erc-scenarios-stamp--user-marker)))
+      (should (= erc-scenarios-stamp--user-marker erc-insert-marker))
+      (save-excursion
+        (goto-char erc-scenarios-stamp--user-marker)
+        ;; The raw message ends in " Iabefhkloqv".  However,
+        ;; `erc-server-004' only prints up to the 5th parameter.
+        (should (looking-back "CEIMRUabefhiklmnoqstuv\n"))))))
+
+(ert-deftest erc-scenarios-stamp--left/display-margin-mode ()
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/reconnect")
+       (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect))
+       (port (process-contact dumb-server :service))
+       (erc-scenarios-stamp--user-marker (make-marker))
+       (erc-stamp--current-time 704591940)
+       (erc-stamp--tz t)
+       (erc-server-flood-penalty 0.1)
+       (erc-timestamp-only-if-changed-flag nil)
+       (erc-insert-timestamp-function #'erc-insert-timestamp-left)
+       (erc-modules (cons 'fill-wrap erc-modules))
+       (erc-timestamp-only-if-changed-flag nil)
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :full-name "tester"
+                                :nick "tester")
+
+        (add-hook 'erc-insert-post-hook #'erc-scenarios-stamp--on-post-modify
+                  nil t)
+        (funcall expect 5 "This server is in debug mode")
+
+        (ert-info ("Stamps appear in left margin and are invisible")
+          (should (eq 'erc-timestamp (field-at-pos (pos-bol))))
+          (should (= (pos-bol) (field-beginning (pos-bol))))
+          (should (eq 'msg (get-text-property (pos-bol) 'erc-msg)))
+          (should (eq 'NOTICE (get-text-property (pos-bol) 'erc-cmd)))
+          (should (= ?- (char-after (field-end (pos-bol)))))
+          (should (equal (get-text-property (1+ (field-end (pos-bol)))
+                                            'erc-speaker)
+                         "irc.foonet.org"))
+          (should (pcase (get-text-property (pos-bol) 'display)
+                    (`((margin left-margin) ,s)
+                     (eq 'timestamp (get-text-property 0 'invisible s))))))
+
+        ;; We set a third-party marker at the end of 004's message (on
+        ;; then "\n"), post-insertion.
+        (ert-info ("User markers untouched by subsequent message left stamp")
+          (save-excursion
+            (goto-char erc-scenarios-stamp--user-marker)
+            (should (looking-back "CEIMRUabefhiklmnoqstuv\n"))
+            (should (looking-at (rx "[")))))))))
+
+;;; erc-scenarios-stamp.el ends here
-- 
2.41.0

>From 943d2abafe5f16c77f540b48d686d50e85fd52e7 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Sun, 15 Oct 2023 13:43:12 -0700
Subject: [PATCH 1/4] ; Mark erc-log test as :unstable

* test/lisp/erc/erc-scenarios-log.el (erc-scenarios-log--truncate):
Mark :unstable for now.
* test/lisp/erc/resources/base/renick/queries/solo.eld: Timeouts.
* test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld: Timeouts.
* test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld: Timeouts.
* test/lisp/erc/resources/erc-scenarios-common.el: Timeouts.
---
 test/lisp/erc/erc-scenarios-log.el                            | 2 +-
 test/lisp/erc/resources/base/renick/queries/solo.eld          | 2 +-
 test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld | 2 +-
 test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld | 2 +-
 test/lisp/erc/resources/erc-scenarios-common.el               | 4 ++--
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/test/lisp/erc/erc-scenarios-log.el b/test/lisp/erc/erc-scenarios-log.el
index f7e7d61c92e..cd28ea54b2e 100644
--- a/test/lisp/erc/erc-scenarios-log.el
+++ b/test/lisp/erc/erc-scenarios-log.el
@@ -149,7 +149,7 @@ erc-scenarios-log--clear-stamp
     (when noninteractive (delete-directory tempdir :recursive))))
 
 (ert-deftest erc-scenarios-log--truncate ()
-  :tags '(:expensive-test)
+  :tags '(:expensive-test :unstable)
   (erc-scenarios-common-with-cleanup
       ((erc-scenarios-common-dialog "base/assoc/bouncer-history")
        (dumb-server (erc-d-run "localhost" t 'foonet))
diff --git a/test/lisp/erc/resources/base/renick/queries/solo.eld b/test/lisp/erc/resources/base/renick/queries/solo.eld
index 12fa7d264e9..fa4c075adac 100644
--- a/test/lisp/erc/resources/base/renick/queries/solo.eld
+++ b/test/lisp/erc/resources/base/renick/queries/solo.eld
@@ -30,7 +30,7 @@
  (0 ":irc.foonet.org NOTICE tester :[09:56:57] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")
  (0 ":irc.foonet.org 305 tester :You are no longer marked as being away"))
 
-((mode 1 "MODE #foo")
+((mode 10 "MODE #foo")
  (0 ":irc.foonet.org 324 tester #foo +nt")
  (0 ":irc.foonet.org 329 tester #foo 1622454985")
  (0.1 ":[email-masked] PRIVMSG #foo :bob: Farewell, pretty lady: you must hold the credit of your father.")
diff --git a/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld b/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld
index efc2506fd6f..d106a45cf66 100644
--- a/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld
+++ b/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld
@@ -56,7 +56,7 @@
  (0.1 ":[email-masked] PRIVMSG #chan :tester, welcome!")
  (0 ":[email-masked] PRIVMSG #chan :tester, welcome!"))
 
-((mode 1 "MODE #chan")
+((mode 10 "MODE #chan")
  (0 ":irc.barnet.org 324 tester #chan +nt")
  (0 ":irc.barnet.org 329 tester #chan 1620205534")
  (0.1 ":[email-masked] PRIVMSG #chan :joe: Chi non te vede, non te pretia.")
diff --git a/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld b/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld
index a11cfac2e73..603afa2fc3e 100644
--- a/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld
+++ b/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld
@@ -52,7 +52,7 @@
  (0.1 ":[email-masked] PRIVMSG #chan :tester, welcome!")
  (0 ":[email-masked] PRIVMSG #chan :tester, welcome!"))
 
-((mode 1 "MODE #chan")
+((mode 10 "MODE #chan")
  (0 ":irc.foonet.org 324 tester #chan +nt")
  (0 ":irc.foonet.org 329 tester #chan 1620205534")
  (0.1 ":[email-masked] PRIVMSG #chan :alice: Thou desirest me to stop in my tale against the hair.")
diff --git a/test/lisp/erc/resources/erc-scenarios-common.el b/test/lisp/erc/resources/erc-scenarios-common.el
index 5354b300b47..9e134e6932f 100644
--- a/test/lisp/erc/resources/erc-scenarios-common.el
+++ b/test/lisp/erc/resources/erc-scenarios-common.el
@@ -574,7 +574,7 @@ erc-scenarios-common--upstream-reconnect
                                 :password "changeme"
                                 :full-name "tester")
         (erc-scenarios-common-assert-initial-buf-name nil port)
-        (erc-d-t-wait-for 3 (eq (erc-network) 'foonet))
+        (erc-d-t-wait-for 6 (eq (erc-network) 'foonet))
         (erc-d-t-wait-for 3 (string= (buffer-name) "foonet"))
         (funcall expect 5 "foonet")))
 
@@ -713,7 +713,7 @@ erc-scenarios-common--join-network-id
         (erc-d-t-wait-for 3 (eq erc-server-process erc-server-process-foo))
         (funcall expect 3 "<bob>")
         (erc-d-t-absent-for 0.1 "<joe>")
-        (funcall expect 10 "not given me")))
+        (funcall expect 20 "not given me")))
 
     (ert-info ("All #chan@barnet output received")
       (with-current-buffer chan-buf-bar
-- 
2.41.0

>From 3996279b48589764c07329c63a39aa573546b7b5 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Sun, 15 Oct 2023 17:22:22 -0700
Subject: [PATCH 2/4] [5.6] Restore missing metadata props in erc-display-line

* etc/ERC-NEWS: Designate `erc-display-message' as the favored means
of inserting messages.
* lisp/erc/erc-fill.el (erc-fill-wrap): Skip any `unknown' `erc-msg'.
* lisp/erc/erc-stamp.el (erc-stamp--current-time): Use an existing
`erc-ts' text property, when present, for the current message time.
* lisp/erc/erc.el (erc-display-line-1): Update doc string.
(erc-display-line): Convert to a thin wrapper around
`erc-display-message', and move its existing body to a new function,
`erc--route-insertion'.
(erc--route-insertion): Adopt former body of `erc-display-line'.  Copy
`erc--msg-props' hash table when inserting a message in multiple
buffers.  At present, only `erc-server-QUIT' uses this facility.
Also, improve readability with at most one recursive call for the
fall-through case.
(erc--compose-text-properties, erc--merge-text-properties-p): Rename
former to latter to avoid confusion with `composition' property.
(erc-display-message): Update doc string.  Attempt to adapt a non-nil
TYPE parameter for use as the value of the `erc-msg' text property
before resorting to a value of `unknown'.  But only do this when
PARSED is nil, and MSG is a string.  Call `erc--route-insertion'
instead of `erc-display-line'.  Use new name for
`erc--compose-text-properties'.
(erc-put-text-property): Update name of variable
`erc--compose-text-properties'.
* test/lisp/erc/erc-networks-tests.el (erc-networks--set-name): Mock
`erc--route-insertion' instead of `erc-display-line'.
* test/lisp/erc/erc-scenarios-display-message.el: New file.
* test/lisp/erc/erc-tests.el (erc--route-insertion): New test.
* test/lisp/erc/resources/base/display-message/multibuf.eld: New test
data.
* test/lisp/erc/resources/fill/snapshots/merge-01-start.eld: Update.
* test/lisp/erc/resources/fill/snapshots/merge-02-right.eld: Update.
* test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld: Update.
* test/lisp/erc/resources/fill/snapshots/monospace-01-start.eld: Update.
* test/lisp/erc/resources/fill/snapshots/monospace-02-right.eld: Update.
* test/lisp/erc/resources/fill/snapshots/monospace-03-left.eld: Update.
* test/lisp/erc/resources/fill/snapshots/monospace-04-reset.eld: Update.
* test/lisp/erc/resources/fill/snapshots/spacing-01-mono.eld: Update.
* test/lisp/erc/resources/fill/snapshots/stamps-left-01.eld: Update.
(Bug#60936)
---
 etc/ERC-NEWS                                  |  23 +++
 lisp/erc/erc-fill.el                          |   3 +-
 lisp/erc/erc-stamp.el                         |   4 +-
 lisp/erc/erc.el                               | 146 +++++++++++-------
 test/lisp/erc/erc-networks-tests.el           |   2 +-
 .../lisp/erc/erc-scenarios-display-message.el |  64 ++++++++
 test/lisp/erc/erc-tests.el                    |  63 ++++++++
 .../base/display-message/multibuf.eld         |  45 ++++++
 .../fill/snapshots/merge-01-start.eld         |   2 +-
 .../fill/snapshots/merge-02-right.eld         |   2 +-
 .../fill/snapshots/merge-wrap-01.eld          |   2 +-
 .../fill/snapshots/monospace-01-start.eld     |   2 +-
 .../fill/snapshots/monospace-02-right.eld     |   2 +-
 .../fill/snapshots/monospace-03-left.eld      |   2 +-
 .../fill/snapshots/monospace-04-reset.eld     |   2 +-
 .../fill/snapshots/spacing-01-mono.eld        |   2 +-
 .../fill/snapshots/stamps-left-01.eld         |   2 +-
 17 files changed, 301 insertions(+), 67 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-display-message.el
 create mode 100644 test/lisp/erc/resources/base/display-message/multibuf.eld

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 2e56539f210..282a538e04d 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -288,6 +288,29 @@ ERC also provisionally reserves the same depth interval for
 continue to modify non-ERC hooks locally whenever possible, especially
 in new code.
 
+*** Message insertion function 'erc-display-message' heavily favored.
+Displaying "local" messages, like help text and interactive-command
+feedback, in ERC buffers has never been straightforward.  As such,
+ancient patterns, like the pairing of preformatted "notice" text with
+ERC's oldest insertion function, 'erc-display-line', still appear
+quite frequently in the wild despite having been largely phased out of
+ERC's own code base in 2002.  That this specific example has endured
+makes some sense because it's probably seen as less cumbersome than
+fiddling with the more powerful and complicated 'erc-display-message'.
+
+The latest twist in this saga comes with this release, in which a
+healthy dose of \"pre-insertion business\" has been invited to take up
+residence in 'erc-display-message'.  While this would seem to put
+antiquated patterns, like the above mentioned 'erc-make-notice' combo,
+at risk of having messages ignored or subject to degraded treatment by
+built-in modules, a prophylactic measure has been erected to recast
+'erc-display-line' as a thin wrapper around 'erc-display-message'.
+And though nothing of the sort has been done for the lower-level
+'erc-display-line-1' (now an obsolete alias for 'erc-insert-line'),
+some fallback code has been put in place to ensure baseline
+functionality.  As always, if you find these developments disturbing,
+please say so on the tracker.
+
 *** ERC now manages timestamp-related properties a bit differently.
 For starters, the 'cursor-sensor-functions' text property is absent by
 default unless the option 'erc-echo-timestamps' is already enabled on
diff --git a/lisp/erc/erc-fill.el b/lisp/erc/erc-fill.el
index 0048956e075..e28c3563ebf 100644
--- a/lisp/erc/erc-fill.el
+++ b/lisp/erc/erc-fill.el
@@ -539,7 +539,8 @@ erc-fill-wrap
     (goto-char (point-min))
     (let ((len (or (and erc-fill--wrap-length-function
                         (funcall erc-fill--wrap-length-function))
-                   (and-let* ((msg-prop (erc--check-msg-prop 'erc-msg)))
+                   (and-let* ((msg-prop (erc--check-msg-prop 'erc-msg))
+                              ((not (eq msg-prop 'unknown))))
                      (when-let ((e (erc--get-speaker-bounds))
                                 (b (pop e))
                                 ((or erc-fill--wrap-action-dedent-p
diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el
index 394643c03cb..57fd7f39e50 100644
--- a/lisp/erc/erc-stamp.el
+++ b/lisp/erc/erc-stamp.el
@@ -219,7 +219,9 @@ erc-stamp--current-time
   (erc-compat--current-lisp-time))
 
 (cl-defmethod erc-stamp--current-time :around ()
-  (or erc-stamp--current-time (cl-call-next-method)))
+  (or erc-stamp--current-time
+      (and erc--msg-props (gethash 'erc-ts erc--msg-props))
+      (cl-call-next-method)))
 
 (defvar erc-stamp--skip nil
   "Non-nil means inhibit `erc-add-timestamp' completely.")
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 5bf6496e926..0513a5c785c 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -3003,13 +3003,26 @@ erc--traverse-inserted
 (defvar erc--insert-marker nil
   "Internal override for `erc-insert-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
-interpret the control characters.  Then, `erc-insert-pre-hook' gets called.
-If `erc-insert-this' is still t, STRING gets inserted into the buffer.
-Afterwards, `erc-insert-modify' and `erc-insert-post-hook' get called.
-If STRING is nil, the function does nothing."
+(define-obsolete-function-alias 'erc-display-line-1 'erc-insert-line "30.1")
+(defun erc-insert-line (string buffer)
+  "Insert STRING in an `erc-mode' BUFFER.
+When STRING is nil, do nothing.  Otherwise, start off by running
+`erc-insert-pre-hook' in BUFFER with `erc-insert-this' bound to
+t.  If the latter remains non-nil afterward, insert STRING into
+BUFFER, ensuring a trailing newline.  After that, narrow BUFFER
+around STRING, along with its final line ending, and run
+`erc-insert-modify' and `erc-insert-post-hook', respectively.  In
+all cases, run `erc-insert-done-hook' unnarrowed before exiting,
+and update positions in `buffer-undo-list'.
+
+In general, expect to be called from a higher-level insertion
+function, like `erc-display-message', especially when modules
+should consider STRING as a candidate for formatting with
+enhancements like indentation, fontification, timestamping, etc.
+Otherwise, when called directly, allow built-in modules to ignore
+STRING, which may make it appear incongruous in situ (unless
+preformatted or anticipated by third-party members of the various
+modification hooks)."
   (when string
     (with-current-buffer (or buffer (process-buffer erc-server-process))
       (let ((insert-position (marker-position erc-insert-marker)))
@@ -3021,7 +3034,7 @@ erc-display-line-1
             (when (erc-string-invisible-p string)
               (erc-put-text-properties 0 (length string)
                                        '(invisible intangible) string)))
-          (erc-log (concat "erc-display-line: " string
+          (erc-log (concat "erc-display-message: " string
                            (format "(%S)" string) " in buffer "
                            (format "%s" buffer)))
           (setq erc-insert-this t)
@@ -3091,39 +3104,45 @@ erc-is-valid-nick-p
   "Check if NICK is a valid IRC nickname."
   (string-match (concat "\\`" erc-valid-nick-regexp "\\'") nick))
 
-(defun erc-display-line (string &optional buffer)
-  "Display STRING in the ERC BUFFER.
-All screen output must be done through this function.  If BUFFER is nil
-or omitted, the default ERC buffer for the `erc-session-server' is used.
-The BUFFER can be an actual buffer, a list of buffers, `all' or `active'.
-If BUFFER = `all', the string is displayed in all the ERC buffers for the
-current session.  `active' means the current active buffer
-\(`erc-active-buffer').  If the buffer can't be resolved, the current
-buffer is used.  `erc-display-line-1' is used to display STRING.
-
-If STRING is nil, the function does nothing."
-  (let (new-bufs)
+(defun erc--route-insertion (string buffer)
+  "Insert STRING in BUFFER.
+See `erc-display-message' for acceptable BUFFER types."
+  (let (seen msg-props)
     (dolist (buf (cond
                   ((bufferp buffer) (list buffer))
-                  ((listp buffer) buffer)
+                  ((consp buffer)
+                   (setq msg-props erc--msg-props)
+                   buffer)
                   ((processp buffer) (list (process-buffer buffer)))
                   ((eq 'all buffer)
                    ;; Hmm, or all of the same session server?
                    (erc-buffer-list nil erc-server-process))
-                  ((and (eq 'active buffer) (erc-active-buffer))
-                   (list (erc-active-buffer)))
+                  ((and-let* (((eq 'active buffer))
+                              (b (erc-active-buffer)))
+                        (list b)))
                   ((erc-server-buffer-live-p)
                    (list (process-buffer erc-server-process)))
                   (t (list (current-buffer)))))
       (when (buffer-live-p buf)
-        (erc-display-line-1 string buf)
-        (push buf new-bufs)))
-    (when (null new-bufs)
-      (erc-display-line-1 string (if (erc-server-buffer-live-p)
-                                     (process-buffer erc-server-process)
-                                   (current-buffer))))))
-
-(defvar erc--compose-text-properties nil
+        (when msg-props
+          (setq erc--msg-props (copy-hash-table msg-props)))
+        (erc-insert-line string buf)
+        (setq seen t)))
+    (unless (or seen (null buffer))
+      (erc--route-insertion string nil))))
+
+(defun erc-display-line (string &optional buffer)
+  "Insert STRING in BUFFER as a plain \"local\" message.
+Take pains to ensure modification hooks see messages created by
+the old pattern (erc-display-line (erc-make-notice) my-buffer) as
+being equivalent to a `erc-display-message' TYPE of `notice'."
+  (let ((erc--msg-prop-overrides erc--msg-prop-overrides))
+    (when (eq 'erc-notice-face (get-text-property 0 'font-lock-face string))
+      (unless (assq 'erc-msg erc--msg-prop-overrides)
+        (push '(erc-msg . notice) erc--msg-prop-overrides)))
+    (erc-display-message nil nil buffer string)))
+
+(defvar erc--merge-text-properties-p nil
   "Non-nil when `erc-put-text-property' defers to `erc--merge-prop'.")
 
 ;; To save space, we could maintain a map of all readable property
@@ -3432,14 +3451,24 @@ erc-display-message
 Insert MSG or text derived from MSG into an ERC buffer, possibly
 after applying formatting by way of either a `format-spec' known
 to a message-catalog entry or a TYPE known to a specialized
-string handler.  Additionally, derive internal metadata, faces,
-and other text properties from the various overloaded parameters,
-such as PARSED, when it's an `erc-response' object, and MSG, when
-it's a key (symbol) for a \"message catalog\" entry.  Expect
-ARGS, when applicable, to be `format-spec' args known to such an
-entry, and TYPE, when non-nil, to be a symbol handled by
+string handler.  Additionally, derive metadata, faces, and other
+text properties from the various overloaded parameters, such as
+PARSED, when it's an `erc-response' object, and MSG, when it's a
+key (symbol) for a \"message catalog\" entry.  Expect ARGS, when
+applicable, to be `format-spec' args known to such an entry, and
+TYPE, when non-nil, to be a symbol handled by
 `erc-display-message-highlight' (necessarily accompanied by a
-string MSG).
+string MSG).  Expect BUFFER to be among the sort accepted by the
+function `erc-display-line'.
+
+Expect BUFFER to be a live `erc-mode' buffer, a list of such
+buffers, or the symbols `all' or `active'.  If `all', insert
+STRING in all buffers for the current session.  If `active',
+defer to the function `erc-active-buffer', which may return the
+session's server buffer if the previously active buffer has been
+killed.  If BUFFER is nil or a network process, pretend it's set
+to the appropriate server buffer.  Otherwise, use the current
+buffer.
 
 When TYPE is a list of symbols, call handlers from left to right
 without influencing how they behave when encountering existing
@@ -3451,24 +3480,31 @@ erc-display-message
 being (erc-error-face erc-notice-face) throughout MSG when
 `erc-notice-highlight-type' is left at its default, `all'.
 
-As of ERC 5.6, assume user code will use this function instead of
-`erc-display-line' when it's important that insert hooks treat
-MSG in a manner befitting messages received from a server.  That
-is, expect to process most nontrivial informational messages, for
-which PARSED is typically nil, when the caller desires
-buttonizing and other effects."
+As of ERC 5.6, assume third-party code will use this function
+instead of lower-level ones, like `erc-insert-line', when needing
+ERC to process arbitrary informative messages as if they'd been
+sent from a server.  That is, guarantee \"local\" messages, for
+which PARSED is typically nil, will be subject to buttonizing,
+filling, and other effects."
   (let ((string (if (symbolp msg)
                     (apply #'erc-format-message msg args)
                   msg))
         (erc--msg-props
          (or erc--msg-props
-             (let* ((table (make-hash-table :size 5))
-                    (cmd (and parsed (erc--get-eq-comparable-cmd
-                                      (erc-response.command parsed))))
-                    (m (cond ((and msg (symbolp msg)) msg)
-                             ((and cmd (memq cmd '(PRIVMSG NOTICE)) 'msg))
-                             (t 'unknown))))
-               (puthash 'erc-msg m table)
+             (let ((table (make-hash-table :size 5))
+                   (cmd (and parsed (erc--get-eq-comparable-cmd
+                                     (erc-response.command parsed)))))
+               (puthash 'erc-msg
+                        (cond ((and msg (symbolp msg)) msg)
+                              ((and cmd (memq cmd '(PRIVMSG NOTICE)) 'msg))
+                              (type (pcase type
+                                      ((pred symbolp) type)
+                                      ((pred listp)
+                                       (intern (mapconcat #'prin1-to-string
+                                                          type "-")))
+                                      (_ 'unknown)))
+                              (t 'unknown))
+                        table)
                (when cmd
                  (puthash 'erc-cmd cmd table))
                (and erc--msg-prop-overrides
@@ -3481,7 +3517,7 @@ erc-display-message
            ((null type)
             string)
            ((listp type)
-            (let ((erc--compose-text-properties
+            (let ((erc--merge-text-properties-p
                    (and (eq (car type) t) (setq type (cdr type)))))
               (dolist (type type)
                 (setq string (erc-display-message-highlight type string))))
@@ -3490,13 +3526,13 @@ erc-display-message
             (erc-display-message-highlight type string))))
 
     (if (not (erc-response-p parsed))
-        (erc-display-line string buffer)
+        (erc--route-insertion string buffer)
       (unless (erc-hide-current-message-p parsed)
         (erc-put-text-property 0 (length string) 'erc-parsed parsed string)
 	(when (erc-response.tags parsed)
 	  (erc-put-text-property 0 (length string) 'tags (erc-response.tags parsed)
 				 string))
-	(erc-display-line string buffer)))))
+        (erc--route-insertion string buffer)))))
 
 (defun erc-message-type-member (position list)
   "Return non-nil if the erc-parsed text-property at POSITION is in LIST.
@@ -6481,7 +6517,7 @@ erc-put-text-property
 
 You can redefine or `defadvice' this function in order to add
 EmacsSpeak support."
-  (if erc--compose-text-properties
+  (if erc--merge-text-properties-p
       (erc--merge-prop start end property value object)
     (put-text-property start end property value object)))
 
diff --git a/test/lisp/erc/erc-networks-tests.el b/test/lisp/erc/erc-networks-tests.el
index e95d99c128f..45ef0d10a6e 100644
--- a/test/lisp/erc/erc-networks-tests.el
+++ b/test/lisp/erc/erc-networks-tests.el
@@ -1206,7 +1206,7 @@ erc-networks--set-name
           calls)
       (erc-mode)
 
-      (cl-letf (((symbol-function 'erc-display-line)
+      (cl-letf (((symbol-function 'erc--route-insertion)
                  (lambda (&rest r) (push r calls))))
 
         (ert-info ("Signals when `erc-server-announced-name' unset")
diff --git a/test/lisp/erc/erc-scenarios-display-message.el b/test/lisp/erc/erc-scenarios-display-message.el
new file mode 100644
index 00000000000..51bdf305ad5
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-display-message.el
@@ -0,0 +1,64 @@
+;;; erc-scenarios-display-message.el --- erc-display-message -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(ert-deftest erc-scenarios-display-message--multibuf ()
+  :tags '(:expensive-test)
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/display-message")
+       (dumb-server (erc-d-run "localhost" t 'multibuf))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (erc-modules (cons 'fill-wrap erc-modules))
+       (erc-autojoin-channels-alist '((foonet "#chan")))
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :full-name "tester")
+        (funcall expect 10 "debug mode")))
+
+    (ert-info ("User dummy is a member of #chan")
+      (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan"))
+        (funcall expect 10 "dummy")))
+
+    (ert-info ("Dummy's QUIT notice in query contains metadata props")
+      (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "dummy"))
+        (funcall expect 10 "<dummy> hi")
+        (funcall expect 10 "*** dummy ([email-masked]) has quit")
+        (should (eq 'QUIT (get-text-property (match-beginning 0) 'erc-msg)))))
+
+    (ert-info ("Dummy's QUIT notice in #chan contains metadata props")
+      (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan"))
+        (funcall expect 10 "*** dummy ([email-masked]) has quit")
+        (should (eq 'QUIT (get-text-property (match-beginning 0) 'erc-msg)))))
+
+    (erc-cmd-QUIT "")))
+
+(eval-when-compile (require 'erc-join))
+
+;;; erc-scenarios-display-message.el ends here
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 4f4662f5075..02dfc55b6d5 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1938,6 +1938,69 @@ erc-format-privmessage
                2 5 (erc-speaker "Bob" font-lock-face erc-nick-default-face)
                5 12 (font-lock-face erc-default-face))))))
 
+(ert-deftest erc--route-insertion ()
+  (erc-tests--send-prep)
+  (erc-tests--set-fake-server-process "sleep" "1")
+  (setq erc-networks--id (erc-networks--id-create 'foonet))
+
+  (let* ((erc-modules) ; for `erc--open-target'
+         (server-buffer (current-buffer))
+         (spam-buffer (save-excursion (erc--open-target "#spam")))
+         (chan-buffer (save-excursion (erc--open-target "#chan")))
+         calls)
+    (cl-letf (((symbol-function 'erc-insert-line)
+               (lambda (&rest r) (push (cons 'line-1 r) calls))))
+
+      (with-current-buffer chan-buffer
+
+        (ert-info ("Null `buffer' routes to live server-buffer")
+          (erc--route-insertion "null" nil)
+          (should (equal (pop calls) `(line-1 "null" ,server-buffer)))
+          (should-not calls))
+
+        (ert-info ("Cons `buffer' routes to live members")
+          ;; Copies a let-bound `erc--msg-props' before mutating.
+          (let* ((table (map-into '(erc-msg msg) 'hash-table))
+                 (erc--msg-props table))
+            (erc--route-insertion "cons" (list server-buffer spam-buffer))
+            (should-not (eq table erc--msg-props)))
+          (should (equal (pop calls) `(line-1 "cons" ,spam-buffer)))
+          (should (equal (pop calls) `(line-1 "cons" ,server-buffer)))
+          (should-not calls))
+
+        (ert-info ("Variant `all' inserts in all session buffers")
+          (erc--route-insertion "all" 'all)
+          (should (equal (pop calls) `(line-1 "all" ,chan-buffer)))
+          (should (equal (pop calls) `(line-1 "all" ,spam-buffer)))
+          (should (equal (pop calls) `(line-1 "all" ,server-buffer)))
+          (should-not calls))
+
+        (ert-info ("Variant `active' routes to active buffer if alive")
+          (should (eq chan-buffer (erc-with-server-buffer erc-active-buffer)))
+          (erc-set-active-buffer spam-buffer)
+          (erc--route-insertion "act" 'active)
+          (should (equal (pop calls) `(line-1 "act" ,spam-buffer)))
+          (should (eq (erc-active-buffer) spam-buffer))
+          (should-not calls))
+
+        (ert-info ("Variant `active' falls back to current buffer")
+          (should (eq spam-buffer (erc-active-buffer)))
+          (kill-buffer "#spam")
+          (erc--route-insertion "nact" 'active)
+          (should (equal (pop calls) `(line-1 "nact" ,server-buffer)))
+          (should (eq (erc-with-server-buffer erc-active-buffer)
+                      server-buffer))
+          (should-not calls))
+
+        (ert-info ("Dead single buffer defaults to live server-buffer")
+          (should-not (get-buffer "#spam"))
+          (erc--route-insertion "dead" 'spam-buffer)
+          (should (equal (pop calls) `(line-1 "dead" ,server-buffer)))
+          (should-not calls))))
+
+    (should-not (buffer-live-p spam-buffer))
+    (kill-buffer chan-buffer)))
+
 (defvar erc-tests--ipv6-examples
   '("1:2:3:4:5:6:7:8"
     "::ffff:10.0.0.1" "::ffff:1.2.3.4" "::ffff:0.0.0.0"
diff --git a/test/lisp/erc/resources/base/display-message/multibuf.eld b/test/lisp/erc/resources/base/display-message/multibuf.eld
new file mode 100644
index 00000000000..e49a654cd06
--- /dev/null
+++ b/test/lisp/erc/resources/base/display-message/multibuf.eld
@@ -0,0 +1,45 @@
+;; -*- mode: lisp-data; -*-
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
+ (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1")
+ (0.01 ":irc.foonet.org 003 tester :This server was created Sat, 14 Oct 2023 16:08:20 UTC")
+ (0.02 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server")
+ (0.01 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server")
+ (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server")
+ (0.00 ":irc.foonet.org 251 tester :There are 0 users and 5 invisible on 1 server(s)")
+ (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections")
+ (0.00 ":irc.foonet.org 254 tester 2 :channels formed")
+ (0.00 ":irc.foonet.org 255 tester :I have 5 clients and 0 servers")
+ (0.00 ":irc.foonet.org 265 tester 5 5 :Current local users 5, max 5")
+ (0.02 ":irc.foonet.org 266 tester 5 5 :Current global users 5, max 5")
+ (0.01 ":irc.foonet.org 422 tester :MOTD File is missing")
+ (0.00 ":irc.foonet.org 221 tester +i")
+ (0.01 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
+
+((mode 10 "MODE tester +i")
+ (0.00 ":irc.foonet.org 221 tester +i"))
+
+((join 10 "JOIN #chan")
+ (0.03 ":[email-masked] JOIN #chan")
+ (0.03 ":irc.foonet.org 353 tester = #chan :@fsbot bob alice dummy tester")
+ (0.01 ":irc.foonet.org 366 tester #chan :End of NAMES list")
+ (0.00 ":[email-masked] PRIVMSG #chan :tester, welcome!")
+ (0.01 ":[email-masked] PRIVMSG #chan :tester, welcome!"))
+
+((mode 10 "MODE #chan")
+ (0.01 ":[email-masked] PRIVMSG #chan :alice: Persuade this rude wretch willingly to die.")
+ (0.01 ":irc.foonet.org 324 tester #chan +Cnt")
+ (0.01 ":irc.foonet.org 329 tester #chan 1697299707")
+ (0.03 ":[email-masked] PRIVMSG #chan :bob: It might be yours or hers, for aught I know.")
+ (0.07 ":[email-masked] PRIVMSG #chan :Would all themselves laugh mortal.")
+ (0.04 ":[email-masked] PRIVMSG tester :hi")
+ (0.06 ":[email-masked] PRIVMSG #chan :alice: It hath pleased the devil drunkenness to give place to the devil wrath; one unperfectness shows me another, to make me frankly despise myself.")
+ (0.05 ":[email-masked] QUIT :Quit: \2ERC\2 5.6-git (IRC client for GNU Emacs 30.0.50)")
+ (0.08 ":[email-masked] PRIVMSG #chan :You speak of him when he was less furnished than now he is with that which makes him both without and within."))
+
+((quit 10 "QUIT :\2ERC\2")
+ (0.04 ":[email-masked] QUIT :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)")
+ (0.02 "ERROR :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)"))
diff --git a/test/lisp/erc/resources/fill/snapshots/merge-01-start.eld b/test/lisp/erc/resources/fill/snapshots/merge-01-start.eld
index 238d8cc73c2..8a6f2289f5d 100644
--- a/test/lisp/erc/resources/fill/snapshots/merge-01-start.eld
+++ b/test/lisp/erc/resources/fill/snapshots/merge-01-start.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr  1 2023]\n<bob> zero.[07:00]\n<alice> one.\n<alice> two.\n<bob> three.\n<bob> four.\n<Dummy> five.\n<Dummy> six.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#6=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc-msg datestamp erc-ts 1680332400 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #5=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #5#) 459 466 (wrap-prefix #1# line-prefix #5#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #5# display (#6# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 (8)))) 475 480 (wrap-prefix #1# line-prefix #7#) 480 486 (wrap-prefix #1# line-prefix #7#) 487 488 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #8=(space :width (- 27 0)) display #9="") 488 493 (wrap-prefix #1# line-prefix #8# display #9#) 493 495 (wrap-prefix #1# line-prefix #8# display #9#) 495 499 (wrap-prefix #1# line-prefix #8#) 500 501 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 501 504 (wrap-prefix #1# line-prefix #10#) 504 512 (wrap-prefix #1# line-prefix #10#) 513 514 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #9#) 514 517 (wrap-prefix #1# line-prefix #11# display #9#) 517 519 (wrap-prefix #1# line-prefix #11# display #9#) 519 524 (wrap-prefix #1# line-prefix #11#) 525 526 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #12=(space :width (- 27 (8)))) 526 531 (wrap-prefix #1# line-prefix #12#) 531 538 (wrap-prefix #1# line-prefix #12#) 539 540 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 0)) display #9#) 540 545 (wrap-prefix #1# line-prefix #13# display #9#) 545 547 (wrap-prefix #1# line-prefix #13# display #9#) 547 551 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr  1 2023]\n<bob> zero.[07:00]\n<alice> one.\n<alice> two.\n<bob> three.\n<bob> four.\n<Dummy> five.\n<Dummy> six.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#6=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc-msg datestamp erc-ts 1680332400 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #5=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #5#) 459 466 (wrap-prefix #1# line-prefix #5#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #5# display (#6# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 (8)))) 475 480 (wrap-prefix #1# line-prefix #7#) 480 486 (wrap-prefix #1# line-prefix #7#) 487 488 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #8=(space :width (- 27 0)) display #9="") 488 493 (wrap-prefix #1# line-prefix #8# display #9#) 493 495 (wrap-prefix #1# line-prefix #8# display #9#) 495 499 (wrap-prefix #1# line-prefix #8#) 500 501 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 501 504 (wrap-prefix #1# line-prefix #10#) 504 512 (wrap-prefix #1# line-prefix #10#) 513 514 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #9#) 514 517 (wrap-prefix #1# line-prefix #11# display #9#) 517 519 (wrap-prefix #1# line-prefix #11# display #9#) 519 524 (wrap-prefix #1# line-prefix #11#) 525 526 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #12=(space :width (- 27 (8)))) 526 531 (wrap-prefix #1# line-prefix #12#) 531 538 (wrap-prefix #1# line-prefix #12#) 539 540 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 0)) display #9#) 540 545 (wrap-prefix #1# line-prefix #13# display #9#) 545 547 (wrap-prefix #1# line-prefix #13# display #9#) 547 551 (wrap-prefix #1# line-prefix #13#))
diff --git a/test/lisp/erc/resources/fill/snapshots/merge-02-right.eld b/test/lisp/erc/resources/fill/snapshots/merge-02-right.eld
index d1ce9198e69..3eb4be4919b 100644
--- a/test/lisp/erc/resources/fill/snapshots/merge-02-right.eld
+++ b/test/lisp/erc/resources/fill/snapshots/merge-02-right.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr  1 2023]\n<bob> zero.[07:00]\n<alice> one.\n<alice> two.\n<bob> three.\n<bob> four.\n<Dummy> five.\n<Dummy> six.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 29) line-prefix (space :width (- 29 (18)))) 21 22 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 29 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#6=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 29 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 29 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc-msg datestamp erc-ts 1680332400 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 29 (18)))) 455 456 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #5=(space :width (- 29 (6)))) 456 459 (wrap-prefix #1# line-prefix #5#) 459 466 (wrap-prefix #1# line-prefix #5#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #5# display (#6# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 29 (8)))) 475 480 (wrap-prefix #1# line-prefix #7#) 480 486 (wrap-prefix #1# line-prefix #7#) 487 488 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #8=(space :width (- 29 0)) display #9="") 488 493 (wrap-prefix #1# line-prefix #8# display #9#) 493 495 (wrap-prefix #1# line-prefix #8# display #9#) 495 499 (wrap-prefix #1# line-prefix #8#) 500 501 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 29 (6)))) 501 504 (wrap-prefix #1# line-prefix #10#) 504 512 (wrap-prefix #1# line-prefix #10#) 513 514 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 29 0)) display #9#) 514 517 (wrap-prefix #1# line-prefix #11# display #9#) 517 519 (wrap-prefix #1# line-prefix #11# display #9#) 519 524 (wrap-prefix #1# line-prefix #11#) 525 526 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #12=(space :width (- 29 (8)))) 526 531 (wrap-prefix #1# line-prefix #12#) 531 538 (wrap-prefix #1# line-prefix #12#) 539 540 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 29 0)) display #9#) 540 545 (wrap-prefix #1# line-prefix #13# display #9#) 545 547 (wrap-prefix #1# line-prefix #13# display #9#) 547 551 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr  1 2023]\n<bob> zero.[07:00]\n<alice> one.\n<alice> two.\n<bob> three.\n<bob> four.\n<Dummy> five.\n<Dummy> six.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 29) line-prefix (space :width (- 29 (18)))) 21 22 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 29 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#6=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 29 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 29 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc-msg datestamp erc-ts 1680332400 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 29 (18)))) 455 456 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #5=(space :width (- 29 (6)))) 456 459 (wrap-prefix #1# line-prefix #5#) 459 466 (wrap-prefix #1# line-prefix #5#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #5# display (#6# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 29 (8)))) 475 480 (wrap-prefix #1# line-prefix #7#) 480 486 (wrap-prefix #1# line-prefix #7#) 487 488 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #8=(space :width (- 29 0)) display #9="") 488 493 (wrap-prefix #1# line-prefix #8# display #9#) 493 495 (wrap-prefix #1# line-prefix #8# display #9#) 495 499 (wrap-prefix #1# line-prefix #8#) 500 501 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 29 (6)))) 501 504 (wrap-prefix #1# line-prefix #10#) 504 512 (wrap-prefix #1# line-prefix #10#) 513 514 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 29 0)) display #9#) 514 517 (wrap-prefix #1# line-prefix #11# display #9#) 517 519 (wrap-prefix #1# line-prefix #11# display #9#) 519 524 (wrap-prefix #1# line-prefix #11#) 525 526 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #12=(space :width (- 29 (8)))) 526 531 (wrap-prefix #1# line-prefix #12#) 531 538 (wrap-prefix #1# line-prefix #12#) 539 540 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 29 0)) display #9#) 540 545 (wrap-prefix #1# line-prefix #13# display #9#) 545 547 (wrap-prefix #1# line-prefix #13# display #9#) 547 551 (wrap-prefix #1# line-prefix #13#))
diff --git a/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld b/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld
index d70184724ba..82c6d52cf7c 100644
--- a/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld
+++ b/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr  1 2023]\n<bob> zero.[07:00]\n* bob one\n<bob> two.\n* bob three\n<bob> four.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#6=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc-msg datestamp erc-ts 1680332400 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #5=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #5#) 459 466 (wrap-prefix #1# line-prefix #5#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #5# display (#6# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG erc-ctcp ACTION wrap-prefix #1# line-prefix #7=(space :width (- 27 (6)))) 475 476 (wrap-prefix #1# line-prefix #7#) 476 479 (wrap-prefix #1# line-prefix #7#) 479 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #8=(space :width (- 27 0)) display #9="") 485 488 (wrap-prefix #1# line-prefix #8# display #9#) 488 490 (wrap-prefix #1# line-prefix #8# display #9#) 490 494 (wrap-prefix #1# line-prefix #8#) 495 496 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG erc-ctcp ACTION wrap-prefix #1# line-prefix #10=(space :width (- 27 (2)))) 496 497 (wrap-prefix #1# line-prefix #10#) 497 500 (wrap-prefix #1# line-prefix #10#) 500 506 (wrap-prefix #1# line-prefix #10#) 507 508 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #9#) 508 511 (wrap-prefix #1# line-prefix #11# display #9#) 511 513 (wrap-prefix #1# line-prefix #11# display #9#) 513 518 (wrap-prefix #1# line-prefix #11#))
\ No newline at end of file
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr  1 2023]\n<bob> zero.[07:00]\n* bob one\n<bob> two.\n* bob three\n<bob> four.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#6=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc-msg datestamp erc-ts 1680332400 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #5=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #5#) 459 466 (wrap-prefix #1# line-prefix #5#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #5# display (#6# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG erc-ctcp ACTION wrap-prefix #1# line-prefix #7=(space :width (- 27 (6)))) 475 476 (wrap-prefix #1# line-prefix #7#) 476 479 (wrap-prefix #1# line-prefix #7#) 479 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #8=(space :width (- 27 0)) display #9="") 485 488 (wrap-prefix #1# line-prefix #8# display #9#) 488 490 (wrap-prefix #1# line-prefix #8# display #9#) 490 494 (wrap-prefix #1# line-prefix #8#) 495 496 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG erc-ctcp ACTION wrap-prefix #1# line-prefix #10=(space :width (- 27 (2)))) 496 497 (wrap-prefix #1# line-prefix #10#) 497 500 (wrap-prefix #1# line-prefix #10#) 500 506 (wrap-prefix #1# line-prefix #10#) 507 508 (erc-msg msg erc-ts 1680332400 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #9#) 508 511 (wrap-prefix #1# line-prefix #11# display #9#) 511 513 (wrap-prefix #1# line-prefix #11# display #9#) 513 518 (wrap-prefix #1# line-prefix #11#))
diff --git a/test/lisp/erc/resources/fill/snapshots/monospace-01-start.eld b/test/lisp/erc/resources/fill/snapshots/monospace-01-start.eld
index def97738ce6..84a1e34670c 100644
--- a/test/lisp/erc/resources/fill/snapshots/monospace-01-start.eld
+++ b/test/lisp/erc/resources/fill/snapshots/monospace-01-start.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
\ No newline at end of file
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
diff --git a/test/lisp/erc/resources/fill/snapshots/monospace-02-right.eld b/test/lisp/erc/resources/fill/snapshots/monospace-02-right.eld
index be3e2b33cfd..83394f2f639 100644
--- a/test/lisp/erc/resources/fill/snapshots/monospace-02-right.eld
+++ b/test/lisp/erc/resources/fill/snapshots/monospace-02-right.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 29) line-prefix (space :width (- 29 (18)))) 21 22 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 29 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 29 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 29 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
\ No newline at end of file
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 29) line-prefix (space :width (- 29 (18)))) 21 22 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 29 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 29 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 29 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
diff --git a/test/lisp/erc/resources/fill/snapshots/monospace-03-left.eld b/test/lisp/erc/resources/fill/snapshots/monospace-03-left.eld
index 098257d0b49..1605628b29f 100644
--- a/test/lisp/erc/resources/fill/snapshots/monospace-03-left.eld
+++ b/test/lisp/erc/resources/fill/snapshots/monospace-03-left.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 25) line-prefix (space :width (- 25 (18)))) 21 22 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 25 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 25 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 25 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
\ No newline at end of file
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 25) line-prefix (space :width (- 25 (18)))) 21 22 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 25 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 25 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 25 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
diff --git a/test/lisp/erc/resources/fill/snapshots/monospace-04-reset.eld b/test/lisp/erc/resources/fill/snapshots/monospace-04-reset.eld
index def97738ce6..84a1e34670c 100644
--- a/test/lisp/erc/resources/fill/snapshots/monospace-04-reset.eld
+++ b/test/lisp/erc/resources/fill/snapshots/monospace-04-reset.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
\ No newline at end of file
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
diff --git a/test/lisp/erc/resources/fill/snapshots/spacing-01-mono.eld b/test/lisp/erc/resources/fill/snapshots/spacing-01-mono.eld
index 360b3dafafd..7a7e01de49d 100644
--- a/test/lisp/erc/resources/fill/snapshots/spacing-01-mono.eld
+++ b/test/lisp/erc/resources/fill/snapshots/spacing-01-mono.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n<bob> This buffer is for text.\n*** one two three\n*** four five six\n<bob> Somebody stop me\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 190 191 (line-spacing 0.5) 191 192 (erc-msg msg erc-cmd PRIVMSG erc-ts 0 wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 348 349 (line-spacing 0.5) 349 350 (erc-msg msg erc-cmd PRIVMSG erc-ts 0 wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 435 436 (line-spacing 0.5) 436 437 (erc-msg msg erc-cmd PRIVMSG erc-ts 0 wrap-prefix #1# line-prefix #5=(space :width (- 27 0)) display #6="") 437 440 (wrap-prefix #1# line-prefix #5# display #6#) 440 442 (wrap-prefix #1# line-prefix #5# display #6#) 442 466 (wrap-prefix #1# line-prefix #5#) 466 467 (line-spacing 0.5) 467 468 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #7=(space :width (- 27 (4)))) 468 484 (wrap-prefix #1# line-prefix #7#) 485 486 (erc-msg unknown erc-ts 0 wrap-prefix #1# line-prefix #8=(space :width (- 27 (4)))) 486 502 (wrap-prefix #1# line-prefix #8#) 502 503 (line-spacing 0.5) 503 504 (erc-msg msg erc-cmd PRIVMSG erc-ts 0 wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 504 507 (wrap-prefix #1# line-prefix #9#) 507 525 (wrap-prefix #1# line-prefix #9#))
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n<bob> This buffer is for text.\n*** one two three\n*** four five six\n<bob> Somebody stop me\n" 2 3 (erc-msg datestamp erc-ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 190 191 (line-spacing 0.5) 191 192 (erc-msg msg erc-cmd PRIVMSG erc-ts 0 wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 348 349 (line-spacing 0.5) 349 350 (erc-msg msg erc-cmd PRIVMSG erc-ts 0 wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 435 436 (line-spacing 0.5) 436 437 (erc-msg msg erc-cmd PRIVMSG erc-ts 0 wrap-prefix #1# line-prefix #5=(space :width (- 27 0)) display #6="") 437 440 (wrap-prefix #1# line-prefix #5# display #6#) 440 442 (wrap-prefix #1# line-prefix #5# display #6#) 442 466 (wrap-prefix #1# line-prefix #5#) 466 467 (line-spacing 0.5) 467 468 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #7=(space :width (- 27 (4)))) 468 484 (wrap-prefix #1# line-prefix #7#) 485 486 (erc-msg notice erc-ts 0 wrap-prefix #1# line-prefix #8=(space :width (- 27 (4)))) 486 502 (wrap-prefix #1# line-prefix #8#) 502 503 (line-spacing 0.5) 503 504 (erc-msg msg erc-cmd PRIVMSG erc-ts 0 wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 504 507 (wrap-prefix #1# line-prefix #9#) 507 525 (wrap-prefix #1# line-prefix #9#))
diff --git a/test/lisp/erc/resources/fill/snapshots/stamps-left-01.eld b/test/lisp/erc/resources/fill/snapshots/stamps-left-01.eld
index cd3537d3c94..bb248ffb28e 100644
--- a/test/lisp/erc/resources/fill/snapshots/stamps-left-01.eld
+++ b/test/lisp/erc/resources/fill/snapshots/stamps-left-01.eld
@@ -1 +1 @@
-#("\n\n[00:00]*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.\n[00:00]<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n[00:00]<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg unknown erc-ts 0 display #3=(#5=(margin left-margin) #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix #2=(space :width (- 27 (4)))) 3 9 (display #3# field erc-timestamp wrap-prefix #1# line-prefix #2#) 9 171 (wrap-prefix #1# line-prefix #2#) 172 173 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG display #6=(#5# #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1# line-prefix #4=(space :width (- 27 (8)))) 173 179 (display #6# field erc-timestamp wrap-prefix #1# line-prefix #4#) 179 180 (wrap-prefix #1# line-prefix #4#) 180 185 (wrap-prefix #1# line-prefix #4#) 185 187 (wrap-prefix #1# line-prefix #4#) 187 190 (wrap-prefix #1# line-prefix #4#) 190 303 (wrap-prefix #1# line-prefix #4#) 304 336 (wrap-prefix #1# line-prefix #4#) 337 338 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG display #8=(#5# #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1# line-prefix #7=(space :width (- 27 (6)))) 338 344 (display #8# field erc-timestamp wrap-prefix #1# line-prefix #7#) 344 345 (wrap-prefix #1# line-prefix #7#) 345 348 (wrap-prefix #1# line-prefix #7#) 348 350 (wrap-prefix #1# line-prefix #7#) 350 355 (wrap-prefix #1# line-prefix #7#) 355 430 (wrap-prefix #1# line-prefix #7#))
\ No newline at end of file
+#("\n\n[00:00]*** This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.\n[00:00]<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n[00:00]<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc-msg notice erc-ts 0 display #3=(#5=(margin left-margin) #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix #2=(space :width (- 27 (4)))) 3 9 (display #3# field erc-timestamp wrap-prefix #1# line-prefix #2#) 9 171 (wrap-prefix #1# line-prefix #2#) 172 173 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG display #6=(#5# #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1# line-prefix #4=(space :width (- 27 (8)))) 173 179 (display #6# field erc-timestamp wrap-prefix #1# line-prefix #4#) 179 180 (wrap-prefix #1# line-prefix #4#) 180 185 (wrap-prefix #1# line-prefix #4#) 185 187 (wrap-prefix #1# line-prefix #4#) 187 190 (wrap-prefix #1# line-prefix #4#) 190 303 (wrap-prefix #1# line-prefix #4#) 304 336 (wrap-prefix #1# line-prefix #4#) 337 338 (erc-msg msg erc-ts 0 erc-cmd PRIVMSG display #8=(#5# #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1# line-prefix #7=(space :width (- 27 (6)))) 338 344 (display #8# field erc-timestamp wrap-prefix #1# line-prefix #7#) 344 345 (wrap-prefix #1# line-prefix #7#) 345 348 (wrap-prefix #1# line-prefix #7#) 348 350 (wrap-prefix #1# line-prefix #7#) 350 355 (wrap-prefix #1# line-prefix #7#) 355 430 (wrap-prefix #1# line-prefix #7#))
-- 
2.41.0

>From 53bb212154471469768594e7db3c5f48918e316d Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Wed, 18 Oct 2023 23:20:07 -0700
Subject: [PATCH 3/4] [5.6] Fix right stamps commingling with erc-prompt

* lisp/erc/erc-stamp.el (erc-insert-timestamp-left-and-right): Fix bug
that saw the prompt being inserted after messages but just inside the
narrowed operating portion of the buffer, which meant remaining
modification hooks would see it upon visiting.  Thanks to Corwin Brust
for catching this.
* test/lisp/erc/erc-fill-tests.el (erc-fill-wrap--monospace): Use
custom `erc-prompt' function to guarantee invariants asserted by
`erc--assert-input-bounds' are preserved throughout.  (Bug#60936)
---
 lisp/erc/erc-stamp.el           |  6 ++--
 test/lisp/erc/erc-fill-tests.el | 57 +++++++++++++++++----------------
 2 files changed, 34 insertions(+), 29 deletions(-)

diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el
index 57fd7f39e50..c8fd7c35392 100644
--- a/lisp/erc/erc-stamp.el
+++ b/lisp/erc/erc-stamp.el
@@ -704,10 +704,12 @@ erc-insert-timestamp-left-and-right
   (unless erc-stamp--date-format-end
     (add-hook 'erc-insert-pre-hook #'erc-stamp--lr-date-on-pre-modify -95 t)
     (add-hook 'erc-send-pre-functions #'erc-stamp--lr-date-on-pre-modify -95 t)
-    (let ((erc--insert-marker (point-min-marker)))
+    (let ((erc--insert-marker (point-min-marker))
+          (end-marker (point-max-marker)))
       (set-marker-insertion-type erc--insert-marker t)
       (erc-stamp--lr-date-on-pre-modify nil)
-      (narrow-to-region erc--insert-marker (point-max))
+      (narrow-to-region erc--insert-marker end-marker)
+      (set-marker end-marker nil)
       (set-marker erc--insert-marker nil)))
   (let* ((ct (or erc-stamp--current-time (erc-stamp--current-time)))
          (ts-right (with-suppressed-warnings
diff --git a/test/lisp/erc/erc-fill-tests.el b/test/lisp/erc/erc-fill-tests.el
index f6c4c268017..80f5fd22ac6 100644
--- a/test/lisp/erc/erc-fill-tests.el
+++ b/test/lisp/erc/erc-fill-tests.el
@@ -203,36 +203,39 @@ erc-fill-wrap--monospace
   (unless (>= emacs-major-version 29)
     (ert-skip "Emacs version too low, missing `buffer-text-pixel-size'"))
 
-  (erc-fill-tests--wrap-populate
-
-   (lambda ()
-     (should (= erc-fill--wrap-value 27))
-     (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
-     (erc-fill-tests--compare "monospace-01-start")
-
-     (ert-info ("Shift right by one (plus)")
-       ;; Args are all `erc-fill-wrap-nudge' +1 because interactive "p"
-       (ert-with-message-capture messages
-         ;; M-x erc-fill-wrap-nudge RET =
-         (ert-simulate-command '(erc-fill-wrap-nudge 2))
-         (should (string-match (rx "for further adjustment") messages)))
-       (should (= erc-fill--wrap-value 29))
-       (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
-       (erc-fill-tests--compare "monospace-02-right"))
-
-     (ert-info ("Shift left by five")
-       ;; "M-x erc-fill-wrap-nudge RET -----"
-       (ert-simulate-command '(erc-fill-wrap-nudge -4))
-       (should (= erc-fill--wrap-value 25))
-       (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
-       (erc-fill-tests--compare "monospace-03-left"))
+  (let ((erc-prompt (lambda () "ABC>")))
+    (erc-fill-tests--wrap-populate
 
-     (ert-info ("Reset")
-       ;; M-x erc-fill-wrap-nudge RET 0
-       (ert-simulate-command '(erc-fill-wrap-nudge 0))
+     (lambda ()
        (should (= erc-fill--wrap-value 27))
        (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
-       (erc-fill-tests--compare "monospace-04-reset")))))
+       (erc-fill-tests--compare "monospace-01-start")
+
+       (ert-info ("Shift right by one (plus)")
+         ;; Args are all `erc-fill-wrap-nudge' +1 because interactive "p"
+         (ert-with-message-capture messages
+           ;; M-x erc-fill-wrap-nudge RET =
+           (ert-simulate-command '(erc-fill-wrap-nudge 2))
+           (should (string-match (rx "for further adjustment") messages)))
+         (should (= erc-fill--wrap-value 29))
+         (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
+         (erc-fill-tests--compare "monospace-02-right"))
+
+       (ert-info ("Shift left by five")
+         ;; "M-x erc-fill-wrap-nudge RET -----"
+         (ert-simulate-command '(erc-fill-wrap-nudge -4))
+         (should (= erc-fill--wrap-value 25))
+         (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
+         (erc-fill-tests--compare "monospace-03-left"))
+
+       (ert-info ("Reset")
+         ;; M-x erc-fill-wrap-nudge RET 0
+         (ert-simulate-command '(erc-fill-wrap-nudge 0))
+         (should (= erc-fill--wrap-value 27))
+         (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ")
+         (erc-fill-tests--compare "monospace-04-reset"))
+
+       (erc--assert-input-bounds)))))
 
 (defun erc-fill-tests--simulate-refill ()
   ;; Simulate `erc-fill-wrap-refill-buffer' synchronously and without
-- 
2.41.0

>From 15f2e73c4022edc1d5ba0ad9c2dea69bbabe3a97 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Wed, 18 Oct 2023 23:20:07 -0700
Subject: [PATCH 4/4] [5.6] Respect user markers in erc--insert-timestamp-left

* lisp/erc/erc-stamp.el (erc-insert-timestamp-left): Convert to normal
function that calls existing generic version in order to dissuade
users from adding their own methods, which could complicate
troubleshooting, etc.
(erc--insert-timestamp-left): Rename both methods using internal
convention.  In `erc-stamp--display-margin-mode' implementation, don't
insert before user markers.
* test/lisp/erc/erc-scenarios-stamp.el: New file.  (Bug#60936)
---
 lisp/erc/erc-stamp.el                | 10 ++--
 test/lisp/erc/erc-scenarios-stamp.el | 90 ++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+), 4 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-stamp.el

diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el
index c8fd7c35392..b515513dcb7 100644
--- a/lisp/erc/erc-stamp.el
+++ b/lisp/erc/erc-stamp.el
@@ -492,8 +492,11 @@ erc--conceal-prompt
     (put-text-property erc-insert-marker (1- erc-input-marker)
                        'display `((margin left-margin) ,prompt))))
 
-(cl-defmethod erc-insert-timestamp-left (string)
+(defun erc-insert-timestamp-left (string)
   "Insert timestamps at the beginning of the line."
+  (erc--insert-timestamp-left string))
+
+(cl-defmethod erc--insert-timestamp-left (string)
   (goto-char (point-min))
   (let* ((ignore-p (and erc-timestamp-only-if-changed-flag
 			(string-equal string erc-timestamp-last-inserted)))
@@ -504,13 +507,12 @@ erc-insert-timestamp-left
     (erc-put-text-property 0 len 'invisible erc-stamp--invisible-property s)
     (insert s)))
 
-(cl-defmethod erc-insert-timestamp-left
+(cl-defmethod erc--insert-timestamp-left
   (string &context (erc-stamp--display-margin-mode (eql t)))
   (unless (and erc-timestamp-only-if-changed-flag
                (string-equal string erc-timestamp-last-inserted))
     (goto-char (point-min))
-    (insert-before-markers-and-inherit
-     (setq erc-timestamp-last-inserted string))
+    (insert-and-inherit (setq erc-timestamp-last-inserted string))
     (dolist (p erc-stamp--inherited-props)
       (when-let ((v (get-text-property (point) p)))
         (put-text-property (point-min) (point) p v)))
diff --git a/test/lisp/erc/erc-scenarios-stamp.el b/test/lisp/erc/erc-scenarios-stamp.el
new file mode 100644
index 00000000000..d6b5d868ce5
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-stamp.el
@@ -0,0 +1,90 @@
+;;; erc-scenarios-stamp.el --- Misc `erc-stamp' scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(require 'erc-stamp)
+
+(defvar erc-scenarios-stamp--user-marker nil)
+
+(defun erc-scenarios-stamp--on-post-modify ()
+  (when-let (((erc--check-msg-prop 'erc-cmd 4)))
+    (set-marker erc-scenarios-stamp--user-marker (point-max))
+    (ert-info ("User marker correctly placed at `erc-insert-marker'")
+      (should (= ?\n (char-before erc-scenarios-stamp--user-marker)))
+      (should (= erc-scenarios-stamp--user-marker erc-insert-marker))
+      (save-excursion
+        (goto-char erc-scenarios-stamp--user-marker)
+        ;; The raw message ends in " Iabefhkloqv".  However,
+        ;; `erc-server-004' only prints up to the 5th parameter.
+        (should (looking-back "CEIMRUabefhiklmnoqstuv\n"))))))
+
+(ert-deftest erc-scenarios-stamp--left/display-margin-mode ()
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/reconnect")
+       (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect))
+       (port (process-contact dumb-server :service))
+       (erc-scenarios-stamp--user-marker (make-marker))
+       (erc-stamp--current-time 704591940)
+       (erc-stamp--tz t)
+       (erc-server-flood-penalty 0.1)
+       (erc-timestamp-only-if-changed-flag nil)
+       (erc-insert-timestamp-function #'erc-insert-timestamp-left)
+       (erc-modules (cons 'fill-wrap erc-modules))
+       (erc-timestamp-only-if-changed-flag nil)
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :full-name "tester"
+                                :nick "tester")
+
+        (add-hook 'erc-insert-post-hook #'erc-scenarios-stamp--on-post-modify
+                  nil t)
+        (funcall expect 5 "This server is in debug mode")
+
+        (ert-info ("Stamps appear in left margin and are invisible")
+          (should (eq 'erc-timestamp (field-at-pos (pos-bol))))
+          (should (= (pos-bol) (field-beginning (pos-bol))))
+          (should (eq 'msg (get-text-property (pos-bol) 'erc-msg)))
+          (should (eq 'NOTICE (get-text-property (pos-bol) 'erc-cmd)))
+          (should (= ?- (char-after (field-end (pos-bol)))))
+          (should (equal (get-text-property (1+ (field-end (pos-bol)))
+                                            'erc-speaker)
+                         "irc.foonet.org"))
+          (should (pcase (get-text-property (pos-bol) 'display)
+                    (`((margin left-margin) ,s)
+                     (eq 'timestamp (get-text-property 0 'invisible s))))))
+
+        ;; We set a third-party marker at the end of 004's message (on
+        ;; then "\n"), post-insertion.
+        (ert-info ("User markers untouched by subsequent message left stamp")
+          (save-excursion
+            (goto-char erc-scenarios-stamp--user-marker)
+            (should (looking-back "CEIMRUabefhiklmnoqstuv\n"))
+            (should (looking-at (rx "[")))))))))
+
+;;; erc-scenarios-stamp.el ends here
-- 
2.41.0