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

"J.P." <[email protected]>
Newsgroups gmane.emacs.erc.general
Message-ID <[email protected]>
One of my patches for this feature introduced a corner-case regression
involving the option `erc-echo-timestamps'. If `cursor-sensor-mode' is
somehow enabled outside of this module, then timestamps will still be
echoed even when `erc-echo-timestamps' is nil.

  commit ad3dc74e074719a58226e23a45c4556cd54c0a48
  Author: F. Jason Park <[email protected]>
  Date:   Wed Nov 24 03:10:20 2021 -0800
  
      Expose insertion time as text prop in erc-stamp
      
      * lisp/erc/erc-stamp.el (erc-add-timestamp): Add new text property
      [...]
      (erc-echo-timestamp): Make interactive and show timestamps even when
      the variable `erc-echo-timestamps' is nil.
      (erc--echo-ts-csf): Add new function to serve as value of
      cursor-sensor function text properties.
      * test/lisp/erc/erc-stamp-tests.el: New file.  (Bug#60936.)
  
   lisp/erc/erc-stamp.el            |  15 ++-
   test/lisp/erc/erc-stamp-tests.el | 207 +++++++++++++++++++++++++++++++++++++++
   2 files changed, 217 insertions(+), 5 deletions(-)

In addition to addressing the above, the attached patch includes a new
optional parameter for the command `erc-echo-timestamp'. It allows for
specifying a timezone for the echoed stamp via prefix argument or a new
option, `erc-echo-timestamp-zone'.

These changes are intended for ERC 5.6.
0001-5.6-Allow-alternate-ert-info-text-in-ERC-test-utilit.patch (text/x-patch, 15 KB)
From 1ca0862854ff5f926ed45b06cc494aa7f7f2b1b7 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email protected]>
Date: Fri, 25 Aug 2023 19:03:26 -0700
Subject: [PATCH 1/2] [5.6] ; Allow alternate ert-info text in ERC test utility

* test/lisp/erc/erc-tests.el
(erc-tests--assert-printed-in-subprocess): Don't insist that arguments
to the Emacs "-load" invocation option be actual disk files.
* test/lisp/erc/resources/base/assoc/bumped/again.eld: Adjust timeouts.
* test/lisp/erc/resources/base/assoc/bumped/foisted.eld: Adjust timeouts.
* test/lisp/erc/resources/base/assoc/bumped/refoisted.eld: Adjust timeouts.
* test/lisp/erc/resources/base/netid/bouncer/barnet.eld: Adjust timeouts.
* test/lisp/erc/resources/base/netid/bouncer/foonet.eld: Adjust
timeouts.
* test/lisp/erc/resources/base/renick/self/qual-chester.eld: Adjust
timeouts.
* test/lisp/erc/resources/base/renick/self/qual-tester.eld: Adjust
timeouts.
* test/lisp/erc/resources/erc-d/erc-d-t.el
(erc-d-t--wait-message-prefix, erc-d-t-wait-for, erc-d-t-ensure-for):
Add and use new variable to make `ert-info' message prefix adjustable.
The immediate use for this is to make it easier to distinguish between
consecutive assertions in which the first waits for a condition and
the second ensures it holds for some duration.
* test/lisp/erc/resources/erc-d/erc-d-u.el
(erc-d-u--read-exchange-default): Skip killed buffers.
* test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld: Adjust
timeout.
* test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld: Adjust
timeouts.
* test/lisp/erc/resources/erc-d/resources/linger.eld: Adjust timeouts.
---
 test/lisp/erc/erc-tests.el                             |  3 +--
 test/lisp/erc/resources/base/assoc/bumped/again.eld    | 10 +++++-----
 test/lisp/erc/resources/base/assoc/bumped/foisted.eld  | 10 +++++-----
 .../lisp/erc/resources/base/assoc/bumped/refoisted.eld |  8 ++++----
 test/lisp/erc/resources/base/netid/bouncer/barnet.eld  |  2 +-
 test/lisp/erc/resources/base/netid/bouncer/foonet.eld  |  2 +-
 .../erc/resources/base/renick/self/qual-chester.eld    |  2 +-
 .../erc/resources/base/renick/self/qual-tester.eld     |  2 +-
 test/lisp/erc/resources/erc-d/erc-d-t.el               |  7 +++++--
 test/lisp/erc/resources/erc-d/erc-d-u.el               |  1 +
 .../erc/resources/erc-d/resources/dynamic-barnet.eld   |  4 ++--
 .../erc/resources/erc-d/resources/dynamic-foonet.eld   |  2 +-
 test/lisp/erc/resources/erc-d/resources/linger.eld     |  4 ++--
 13 files changed, 30 insertions(+), 27 deletions(-)

diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 9fdad823059..7e01efe95cf 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -2038,8 +2038,7 @@ erc-tests--assert-printed-in-subprocess
          ;; This is for integrations testing with managed configs
          ;; ("starter kits") that use a different package manager.
          (init (and-let* ((found (getenv "ERC_TESTS_INIT"))
-                          (files (split-string found ","))
-                          ((seq-every-p #'file-exists-p files)))
+                          (files (split-string found ",")))
                  (mapcan (lambda (f) (list "-l" f)) files)))
          (prog
           `(progn
diff --git a/test/lisp/erc/resources/base/assoc/bumped/again.eld b/test/lisp/erc/resources/base/assoc/bumped/again.eld
index ab3c7b06214..aef164b6237 100644
--- a/test/lisp/erc/resources/base/assoc/bumped/again.eld
+++ b/test/lisp/erc/resources/base/assoc/bumped/again.eld
@@ -1,10 +1,10 @@
 ;; -*- mode: lisp-data; -*-
-((nick 1 "NICK tester"))
-((user 1 "USER user 0 * :tester")
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
  (0.0 ":irc.foonet.org 433 * tester :Nickname is reserved by a different account")
  (0.0 ":irc.foonet.org FAIL NICK NICKNAME_RESERVED tester :Nickname is reserved by a different account"))
 
-((nick 3 "NICK tester`")
+((nick 10 "NICK tester`")
  (0.1 ":irc.foonet.org 001 tester` :Welcome to the foonet IRC Network tester`")
  (0.0 ":irc.foonet.org 002 tester` :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5")
  (0.0 ":irc.foonet.org 003 tester` :This server was created Fri, 24 Sep 2021 01:38:36 UTC")
@@ -21,10 +21,10 @@
  (0.2 ":irc.foonet.org 266 tester` 3 3 :Current global users 3, max 3")
  (0.0 ":irc.foonet.org 422 tester` :MOTD File is missing"))
 
-((mode-user 3.2 "MODE tester` +i")
+((mode-user 10 "MODE tester` +i")
  (0.0 ":irc.foonet.org 221 tester` +i")
  (0.0 ":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."))
 
-((privmsg 42.6 "PRIVMSG NickServ :IDENTIFY tester changeme")
+((privmsg 10 "PRIVMSG NickServ :IDENTIFY tester changeme")
  (0.01 ":tester`[email protected] NICK tester")
  (0.0 ":NickServ!NickServ@localhost NOTICE tester :You're now logged in as tester"))
diff --git a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld
index 5c36e58d9d3..0f7aadac564 100644
--- a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld
+++ b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld
@@ -1,6 +1,6 @@
 ;; -*- mode: lisp-data; -*-
-((nick 1 "NICK tester"))
-((user 1 "USER user 0 * :tester")
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
  (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
  (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5")
  (0.0 ":irc.foonet.org 003 tester :This server was created Fri, 24 Sep 2021 01:38:36 UTC")
@@ -17,14 +17,14 @@
  (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
  (0.0 ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 10 "MODE tester +i")
  (0.0 ":irc.foonet.org 221 tester +i")
  (0.0 ":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."))
 
-((privmsg 17.21 "PRIVMSG bob :hi")
+((privmsg 10 "PRIVMSG bob :hi")
  (0.02 ":[email protected] PRIVMSG tester :hola")
  (0.01 ":[email protected] PRIVMSG tester :how r u?"))
 
-((quit 18.19 "QUIT :" quit)
+((quit 10 "QUIT :" quit)
  (0.01 ":[email protected] QUIT :Quit: " quit))
 ((drop 1 DROP))
diff --git a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld
index 33e4168ac46..63366d3f576 100644
--- a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld
+++ b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld
@@ -1,6 +1,6 @@
 ;; -*- mode: lisp-data; -*-
-((nick 1 "NICK tester"))
-((user 1 "USER user 0 * :tester")
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
  (0.1 ":irc.foonet.org 001 dummy :Welcome to the foonet IRC Network dummy")
  (0.0 ":irc.foonet.org 002 dummy :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5")
  (0.0 ":irc.foonet.org 003 dummy :This server was created Fri, 24 Sep 2021 01:38:36 UTC")
@@ -22,10 +22,10 @@
  (0.01 ":[email protected] PRIVMSG dummy :back?")
  )
 
-((mode-user 1.2 "MODE dummy +i")
+((mode-user 10 "MODE dummy +i")
  (0.0 ":irc.foonet.org 221 dummy +i")
  (0.0 ":irc.foonet.org NOTICE dummy :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."))
 
-((renick 42.6 "NICK tester")
+((renick 10 "NICK tester")
  (0.01 ":[email protected] NICK tester")
  (0.0 ":NickServ!NickServ@localhost NOTICE dummy :You're now logged in as tester"))
diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld
index 204d01fef77..596383c2699 100644
--- a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld
+++ b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld
@@ -38,4 +38,4 @@
  (0.05 ":[email protected] PRIVMSG #chan :mike: As he regards his aged father's life.")
  (0.05 ":[email protected] PRIVMSG #chan :joe: It is a rupture that you may easily heal; and the cure of it not only saves your brother, but keeps you from dishonor in doing it."))
 
-((linger 1 LINGER))
+((linger 2 LINGER))
diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld
index 4445350ca0c..2e1a3ac27da 100644
--- a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld
+++ b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld
@@ -43,4 +43,4 @@
  (0.1 ":[email protected] PRIVMSG #chan :bob: Orlando, my liege; the youngest son of Sir Rowland de Boys.")
  (0.1 ":[email protected] PRIVMSG #chan :alice: The ape is dead, and I must conjure him."))
 
-((linger 1 LINGER))
+((linger 2 LINGER))
diff --git a/test/lisp/erc/resources/base/renick/self/qual-chester.eld b/test/lisp/erc/resources/base/renick/self/qual-chester.eld
index 75b50fe68bd..a224e0451d7 100644
--- a/test/lisp/erc/resources/base/renick/self/qual-chester.eld
+++ b/test/lisp/erc/resources/base/renick/self/qual-chester.eld
@@ -18,7 +18,7 @@
  (0 ":irc.foonet.org 266 chester 3 4 :Current global users 3, max 4")
  (0 ":irc.foonet.org 422 chester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE chester +i")
+((mode-user 10 "MODE chester +i")
  (0 ":irc.foonet.org 221 chester +i")
  (0 ":irc.foonet.org NOTICE chester :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."))
 
diff --git a/test/lisp/erc/resources/base/renick/self/qual-tester.eld b/test/lisp/erc/resources/base/renick/self/qual-tester.eld
index 25199226658..27061c65223 100644
--- a/test/lisp/erc/resources/base/renick/self/qual-tester.eld
+++ b/test/lisp/erc/resources/base/renick/self/qual-tester.eld
@@ -18,7 +18,7 @@
  (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4")
  (0 ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 10 "MODE tester +i")
  (0 ":irc.foonet.org 221 tester +i")
  (0 ":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."))
 
diff --git a/test/lisp/erc/resources/erc-d/erc-d-t.el b/test/lisp/erc/resources/erc-d/erc-d-t.el
index 7b2adf4f07b..cf869fb3c70 100644
--- a/test/lisp/erc/resources/erc-d/erc-d-t.el
+++ b/test/lisp/erc/resources/erc-d/erc-d-t.el
@@ -83,6 +83,8 @@ erc-d-t-with-cleanup
                (ignore-errors (kill-buffer buf)))))
          (sleep-for erc-d-t-cleanup-sleep-secs)))))
 
+(defvar erc-d-t--wait-message-prefix "Awaiting: ")
+
 (defmacro erc-d-t-wait-for (max-secs msg &rest body)
   "Wait for BODY to become non-nil.
 Or signal error with MSG after MAX-SECS.  When MAX-SECS is negative,
@@ -99,7 +101,7 @@ erc-d-t-wait-for
   (let ((inverted (make-symbol "inverted"))
         (time-out (make-symbol "time-out"))
         (result (make-symbol "result")))
-    `(ert-info ((concat "Awaiting: " ,msg))
+    `(ert-info ((concat erc-d-t--wait-message-prefix ,msg))
        (let ((,time-out (abs ,max-secs))
              (,inverted (< ,max-secs 0))
              (,result ',result))
@@ -120,7 +122,8 @@ erc-d-t-ensure-for
   (unless (or (stringp msg) (memq (car-safe msg) '(format concat)))
     (push msg body)
     (setq msg (prin1-to-string body)))
-  `(erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body))))
+  `(let ((erc-d-t--wait-message-prefix "Sustaining: "))
+     (erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body)))))
 
 (defun erc-d-t-search-for (timeout text &optional from on-success)
   "Wait for TEXT to appear in current buffer before TIMEOUT secs.
diff --git a/test/lisp/erc/resources/erc-d/erc-d-u.el b/test/lisp/erc/resources/erc-d/erc-d-u.el
index e26fa8b47dd..c7d6859e3e1 100644
--- a/test/lisp/erc/resources/erc-d/erc-d-u.el
+++ b/test/lisp/erc/resources/erc-d/erc-d-u.el
@@ -74,6 +74,7 @@ erc-d-u--read-exchange-default
   (let ((hunks (erc-d-u-scan-e-sd info))
         (pos (erc-d-u-scan-e-pos info)))
     (or (and (erc-d-u-scan-d-hunks hunks)
+             (buffer-live-p (erc-d-u-scan-d-buf hunks))
              (with-current-buffer (erc-d-u-scan-d-buf hunks)
                (goto-char pos)
                (condition-case _err
diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld b/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld
index 4994e9c5503..e8feb2e6fd8 100644
--- a/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld
+++ b/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld
@@ -18,14 +18,14 @@
  (0. ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3")
  (0. ":irc.barnet.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 2 "MODE tester +i")
  (0. ":irc.barnet.org 221 tester +Zi")
  (0. ":irc.barnet.org 306 tester :You have been marked as being away")
  (0 ":[email protected] JOIN #chan")
  (0 ":irc.barnet.org 353 joe = #chan :[email protected] @%[email protected]")
  (0 ":irc.barnet.org 366 joe #chan :End of NAMES list"))
 
-((mode 1 "MODE #chan")
+((mode 3 "MODE #chan")
  (0 ":irc.barnet.org 324 tester #chan +nt")
  (0 ":irc.barnet.org 329 tester #chan 1620805269")
  (0.1 ":[email protected] PRIVMSG #chan :mike: Yes, a dozen; and as many to the vantage, as would store the world they played for.")
diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld b/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld
index a47998e7d32..4855c178861 100644
--- a/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld
+++ b/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld
@@ -17,7 +17,7 @@
  (0. ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
  (0. ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 2 "MODE tester +i")
  (0. ":irc.foonet.org 221 tester +Zi")
  (0. ":irc.foonet.org 306 tester :You have been marked as being away")
  (0 ":[email protected] JOIN #chan")
diff --git a/test/lisp/erc/resources/erc-d/resources/linger.eld b/test/lisp/erc/resources/erc-d/resources/linger.eld
index 36c81a3af4b..e456370a800 100644
--- a/test/lisp/erc/resources/erc-d/resources/linger.eld
+++ b/test/lisp/erc/resources/erc-d/resources/linger.eld
@@ -20,14 +20,14 @@
  (0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3")
  (0 ":irc.example.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 2 "MODE tester +i")
  (0 ":irc.example.org 221 tester +Zi")
  (0 ":irc.example.org 306 tester :You have been marked as being away")
  (0 ":tester!~tester@localhost JOIN #chan")
  (0 ":irc.example.org 353 alice = #chan :[email protected] @%[email protected]")
  (0 ":irc.example.org 366 alice #chan :End of NAMES list"))
 
-((mode-chan 1.2 "MODE #chan")
+((mode-chan 2 "MODE #chan")
  (0 ":[email protected] PRIVMSG #chan :hey"))
 
 ((linger 1.0 LINGER))
-- 
2.41.0
0002-5.6-Add-optional-timezone-param-to-erc-echo-timestam.patch (text/x-patch, 10.4 KB)
From 9a5b2bd7e9ce32bafbb3f204cc1b4a7d5069e9e5 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email protected]>
Date: Wed, 30 Aug 2023 23:15:22 -0700
Subject: [PATCH 2/2] [5.6] Add optional timezone param to erc-echo-timestamp

* etc/ERC-NEWS: Mention option `erc-echo-timestamp-zone'.
* lisp/erc/erc-stamp.el (erc-echo-timestamps): Mention that some
finagling is required if enabling this option after activating the
module.
(erc-echo-timestamp-format): Add additional Custom choice constants.
(erc-echo-timestamp-zone): New option to specify timezone for option
`erc-echo-timestamps' and function `erc-echo-timestamp'.
(erc-stamp-mode, erc-stamp-enable, erc-stamp-disable): Call
`erc-stamp--setup' instead of `erc-munge-invisibility-spec'.
(erc-munge-invisibility-spec): Perform teardown when boolean flag
options, like `erc-timestamp-intangible' and `erc-echo-timestamps' are
nil.
(erc-stamp--setup): Call `erc-munge-invisibility-spec).
(erc-stamp--last-stamp, erc-stamp--on-clear-message): New function and
helper state variable to tell Emacs not to clear the current timestamp
message when navigating within the same IRC message.
(erc-echo-timestamp): Add optional `zone' parameter, to be passed
directly to `format-time-string', when non-interactive, and massaged
sensibly otherwise.  Set the local variable `erc-stamp--last-stamp'.
* test/lisp/erc/erc-stamp-tests.el (erc-echo-timestamp): New test.
(Bug#60936)
---
 etc/ERC-NEWS                     | 13 +++--
 lisp/erc/erc-stamp.el            | 83 ++++++++++++++++++++++++++------
 test/lisp/erc/erc-stamp-tests.el | 30 ++++++++++++
 3 files changed, 107 insertions(+), 19 deletions(-)

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 7ee55982b17..69088732c0d 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -203,11 +203,18 @@ continued integration.  With the existing design, merely loading the
 library 'erc-log' caused 'truncate' to start writing logs, possibly
 against a user's wishes.
 
+** The function 'erc-echo-timestamp' is now a command.
+The option 'erc-echo-timestamps' (plural) enables the contextual
+echoing of timestamps to the echo area when moving between messages in
+an ERC buffer.  This functionality is now available on demand by
+invoking the newly interactive function 'erc-echo-timestamp' atop any
+message.  And the new companion option 'erc-echo-timestamp-zone'
+determines the default timezone when not specified with a prefix
+argument.
+
 ** Miscellaneous UX changes.
 Some minor quality-of-life niceties have finally made their way to
-ERC.  For example, the function 'erc-echo-timestamp' is now
-interactive and can be invoked on any message to view its timestamp in
-the echo area.  Fool visibility has become togglable with the new
+ERC.  For example, fool visibility has become togglable with the new
 command 'erc-match-toggle-hidden-fools'.  The 'button' module's
 'erc-button-previous' now moves to the beginning instead of the end of
 buttons.  A new command, 'erc-news', can be invoked to visit this very
diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el
index a021cd26607..be12d6080d2 100644
--- a/lisp/erc/erc-stamp.el
+++ b/lisp/erc/erc-stamp.el
@@ -136,14 +136,27 @@ erc-echo-timestamps
   "If non-nil, print timestamp in the minibuffer when point is moved.
 Using this variable, you can turn off normal timestamping,
 and simply move point to an irc message to see its timestamp
-printed in the minibuffer."
+printed in the minibuffer.  When attempting to enable this option
+after `erc-stamp-mode' is already active, you may need to run the
+command `erc-show-timestamps', `erc-hide-timestamps', or similar
+in the appropriate ERC buffer."
   :type 'boolean)
 
 (defcustom erc-echo-timestamp-format "Timestamped %A, %H:%M:%S"
   "Format string to be used when `erc-echo-timestamps' is non-nil.
 This string specifies the format of the timestamp being echoed in
 the minibuffer."
-  :type 'string)
+  :type '(choice (const "Timestamped %A, %H:%M:%S")
+                 (const  "%Y-%m-%d %H:%M:%S %Z")
+                 string))
+
+(defcustom erc-echo-timestamp-zone nil
+  "Default timezone for the option `erc-echo-timestamps'.
+Also affects the command `erc-echo-timestamp' (singular).  See
+the ZONE parameter of `format-time-string' for a description of
+acceptable value types."
+  :type '(choice boolean number (const wall) (list number string))
+  :package-version '(ERC . "5.6")) ; FIXME sync on release
 
 (defcustom erc-timestamp-intangible nil
   "Whether the timestamps should be intangible, i.e. prevent the point
@@ -167,14 +180,16 @@ stamp
    (add-hook 'erc-send-modify-hook #'erc-add-timestamp 60)
    (add-hook 'erc-mode-hook #'erc-stamp--recover-on-reconnect)
    (add-hook 'erc--pre-clear-functions #'erc-stamp--reset-on-clear)
-   (unless erc--updating-modules-p
-     (erc-buffer-do #'erc-munge-invisibility-spec)))
+   (unless erc--updating-modules-p (erc-buffer-do #'erc-stamp--setup)))
   ((remove-hook 'erc-mode-hook #'erc-munge-invisibility-spec)
    (remove-hook 'erc-insert-modify-hook #'erc-add-timestamp)
    (remove-hook 'erc-send-modify-hook #'erc-add-timestamp)
    (remove-hook 'erc-mode-hook #'erc-stamp--recover-on-reconnect)
    (remove-hook 'erc--pre-clear-functions #'erc-stamp--reset-on-clear)
    (erc-with-all-buffers-of-server nil nil
+     (let (erc-echo-timestamps erc-hide-timestamps erc-timestamp-intangible)
+       (erc-stamp--setup))
+     (kill-local-variable 'erc-stamp--last-stamp)
      (kill-local-variable 'erc-timestamp-last-inserted)
      (kill-local-variable 'erc-timestamp-last-inserted-left)
      (kill-local-variable 'erc-timestamp-last-inserted-right))))
@@ -640,14 +655,31 @@ erc-format-timestamp
 ;; please modify this function and move it to a more appropriate
 ;; location.
 (defun erc-munge-invisibility-spec ()
-  (and erc-timestamp-intangible (not (bound-and-true-p cursor-intangible-mode))
-       (cursor-intangible-mode 1))
-  (and erc-echo-timestamps (not (bound-and-true-p cursor-sensor-mode))
-       (cursor-sensor-mode 1))
+  (if erc-timestamp-intangible
+      (cursor-intangible-mode +1) ; idempotent
+    (when (bound-and-true-p cursor-intangible-mode)
+      (cursor-intangible-mode -1)))
+  (if erc-echo-timestamps
+      (progn
+        (cursor-sensor-mode +1) ; idempotent
+        (when (<= 29 emacs-major-version)
+          (add-function :before-until (local 'clear-message-function)
+                        #'erc-stamp--on-clear-message)))
+    (when (bound-and-true-p cursor-sensor-mode)
+      (cursor-sensor-mode -1))
+    (remove-function (local 'clear-message-function)
+                     #'erc-stamp--on-clear-message))
   (if erc-hide-timestamps
       (add-to-invisibility-spec 'timestamp)
     (remove-from-invisibility-spec 'timestamp)))
 
+(defun erc-stamp--setup ()
+  "Enable or disable buffer-local `erc-stamp-mode' modifications."
+  (if erc-stamp-mode
+      (erc-munge-invisibility-spec)
+    (let (erc-echo-timestamps erc-hide-timestamps erc-timestamp-intangible)
+      (erc-munge-invisibility-spec))))
+
 (defun erc-hide-timestamps ()
   "Hide timestamp information from display."
   (interactive)
@@ -677,14 +709,33 @@ erc-toggle-timestamps
 	    (erc-munge-invisibility-spec)))
 	(erc-buffer-list)))
 
-(defun erc-echo-timestamp (dir stamp)
-  "Print timestamp text-property of an IRC message."
-  ;; Could also pass an &optional `zone' arg to `format-time-string'.
-  (interactive (list 'entered (get-text-property (point) 'erc-timestamp)))
-  (when (eq 'entered dir)
-    (when stamp
-      (message "%s" (format-time-string erc-echo-timestamp-format
-					stamp)))))
+(defvar-local erc-stamp--last-stamp nil)
+
+(defun erc-stamp--on-clear-message (&rest _)
+  "Return `dont-clear-message' when operating inside the same stamp."
+  (and erc-stamp--last-stamp erc-echo-timestamps
+       (eq (get-text-property (point) 'erc-timestamp) erc-stamp--last-stamp)
+       'dont-clear-message))
+
+(defun erc-echo-timestamp (dir stamp &optional zone)
+  "Display timestamp of message at point in echo area.
+Interactively, interpret a numeric prefix as a ZONE offset in
+hours (or seconds, if its abs value is larger than 14), and
+interpret a \"raw\" prefix as UTC.  To specify a zone for use
+with the option `erc-echo-timestamps', see the companion option
+`erc-echo-timestamp-zone'."
+  (interactive (list nil (get-text-property (point) 'erc-timestamp)
+                     (pcase current-prefix-arg
+                       ((and (pred numberp) v)
+                        (if (<= (abs v) 14) (* v 3600) v))
+                       (`(,_) t))))
+  (if (and stamp (or (null dir) (and erc-echo-timestamps (eq 'entered dir))))
+      (progn
+        (setq erc-stamp--last-stamp stamp)
+        (message (format-time-string erc-echo-timestamp-format
+                                     stamp (or zone erc-echo-timestamp-zone))))
+    (when (and erc-echo-timestamps (eq 'left dir))
+      (setq erc-stamp--last-stamp nil))))
 
 (defun erc--echo-ts-csf (_window _before dir)
   (erc-echo-timestamp dir (get-text-property (point) 'erc-timestamp)))
diff --git a/test/lisp/erc/erc-stamp-tests.el b/test/lisp/erc/erc-stamp-tests.el
index c448416cd69..b00aa6dcabf 100644
--- a/test/lisp/erc/erc-stamp-tests.el
+++ b/test/lisp/erc/erc-stamp-tests.el
@@ -274,4 +274,34 @@ erc-timestamp-intangible--left
       (when noninteractive
         (kill-buffer)))))
 
+(ert-deftest erc-echo-timestamp ()
+  (should-not erc-echo-timestamps)
+  (should-not erc-stamp--last-stamp)
+  (insert (propertize "abc" 'erc-timestamp 433483200))
+  (goto-char (point-min))
+  (let ((inhibit-message t)
+        (erc-echo-timestamp-format "%Y-%m-%d %H:%M:%S %Z")
+        (erc-echo-timestamp-zone (list (* 60 60 -4) "EDT")))
+
+    ;; No-op when non-interactive and option is nil
+    (should-not (erc--echo-ts-csf nil nil 'entered))
+    (should-not erc-stamp--last-stamp)
+
+    ;; Non-interactive (cursor sensor function)
+    (let ((erc-echo-timestamps t))
+      (should (equal (erc--echo-ts-csf nil nil 'entered)
+                     "1983-09-27 00:00:00 EDT")))
+    (should (= 433483200 erc-stamp--last-stamp))
+
+    ;; Interactive
+    (should (equal (call-interactively #'erc-echo-timestamp)
+                   "1983-09-27 00:00:00 EDT"))
+    ;; Interactive with zone
+    (let ((current-prefix-arg '(4)))
+      (should (equal (call-interactively #'erc-echo-timestamp)
+                     "1983-09-27 04:00:00 GMT")))
+    (let ((current-prefix-arg -7))
+      (should (equal (call-interactively #'erc-echo-timestamp)
+                     "1983-09-26 21:00:00 -07")))))
+
 ;;; erc-stamp-tests.el ends here
-- 
2.41.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.