Restore deferred date-stamp insertions in ERC

* lisp/erc/erc-stamp.el (erc-stamp--recover-on-reconnect): Treat
`erc-stamp--deferred-date-stamp' as a permanent-local variable.
(erc-stamp--date): Document expected possible values for `fn' slot.
(erc-stamp--defer-date-insertion-on-post-modify): Use the function
`ignore' to mean a new `erc-timer-hook' member has been requested.
Use nil to mean one has already run.  Deferred date stamps are new in
ERC 5.6 and Emacs 30.
(erc-stamp--date-mode): Improve doc string.
* test/lisp/erc/erc-scenarios-stamp.el
(erc-scenarios-stamp--date-mode/reconnect): New test.
This commit is contained in:
F. Jason Park 2024-06-05 00:22:28 -07:00
parent 772fb960a9
commit f6bfa1844b
2 changed files with 70 additions and 4 deletions

View file

@ -203,6 +203,7 @@ from entering them and instead jump over them."
(dolist (var '(erc-timestamp-last-inserted
erc-timestamp-last-inserted-left
erc-timestamp-last-inserted-right
erc-stamp--deferred-date-stamp
erc-stamp--date-stamps))
(when-let (existing (alist-get var priors))
(set var existing)))))
@ -668,7 +669,9 @@ value of t means the option's value doesn't require trimming.")
:documentation "Time recorded by `erc-insert-timestamp-left-and-right'.")
( str (error "Missing `str' field") :type string
:documentation "Stamp rendered by `erc-insert-timestamp-left-and-right'.")
( fn nil :type (or null function)
( fn #'ignore :type (or null function)
;; Use `ignore' as a third state to mean the creation of a bespoke
;; date-insertion function has been requested but not completed.
:documentation "Deferred insertion function created by post-modify hook.")
( marker (make-marker) :type marker
:documentation "Insertion marker."))
@ -701,6 +704,9 @@ Non-nil between insertion-modification and \"done\" (or timer) hook.")
(defun erc-stamp--find-insertion-point (p target-time)
"Scan buffer backwards from P looking for TARGET-TIME.
Return P or, if found, a position less than P."
;; Continue searching after encountering a message without a
;; timestamp because date stamps must be unique, and
;; "Re-establishing connection" messages should have stamps.
(while-let ((q (previous-single-property-change (1- p) 'erc--ts))
(qq (erc--get-inserted-msg-beg q))
(ts (get-text-property qq 'erc--ts))
@ -720,7 +726,7 @@ inserted is a date stamp."
Do so when `erc-stamp--deferred-date-stamp' and its `fn' slot are
non-nil."
(when-let ((data erc-stamp--deferred-date-stamp)
((null (erc-stamp--date-fn data)))
((eq (erc-stamp--date-fn data) #'ignore))
(ct (erc-stamp--date-ts data))
(rendered (erc-stamp--date-str data))
(buffer (current-buffer))
@ -730,7 +736,7 @@ non-nil."
(fset symbol
(lambda (&rest _)
(remove-hook hook-var symbol)
(setf (erc-stamp--date-fn data) #'ignore)
(setf (erc-stamp--date-fn data) nil)
(when (buffer-live-p buffer)
(with-current-buffer buffer
(setq erc-stamp--date-stamps
@ -770,7 +776,21 @@ non-nil."
;; a standalone module to allow completely decoupling from and
;; possibly deprecating `erc-insert-timestamp-left-and-right'.
(define-minor-mode erc-stamp--date-mode
"Insert date stamps as standalone messages."
"When enabled, insert date stamps as standalone messages.
Only do so when `erc-insert-timestamp-function' is set to
`erc-insert-timestamp-left-and-right'. On `erc-insert-modify-hook',
hold off on inserting a date stamp immediately because that would force
other members of the hook to rely on heuristics and implementation
details to detect a prepended stamp's presence, not to mention
compromise the integrity of the `erc-parsed' text property. Instead,
tell `erc-insert-post-hook', via `erc-stamp--deferred-date-stamp', to
schedule a date stamp for insertion on the next go around of
`erc-timer-hook', which only runs on server-sent messages. Expect users
to know that non-server-sent messages, such as local informational
messages, won't induce a date stamp's insertion but will instead defer
it until the next arrival, which can include \"PING\"s or messages that
otherwise don't insert anything, such as those skipped on account of
`erc-ignore'."
:interactive nil
(if erc-stamp--date-mode
(progn

View file

@ -180,4 +180,50 @@
(funcall expect 5 "This server is in debug mode")))))
;; Assert that only one date stamp per day appears in the server
;; buffer when reconnecting.
(ert-deftest erc-scenarios-stamp--date-mode/reconnect ()
:tags '(:expensive-test)
(erc-scenarios-common-with-cleanup
((erc-scenarios-common-dialog "base/reconnect")
(erc-server-flood-penalty 0.1)
(erc-stamp--tz t)
(erc-server-auto-reconnect t)
;; Start close to midnight: 2024-06-02T23:58:11.055Z
(erc-stamp--current-time (if (< emacs-major-version 29)
'(26205 1811 55000 0)
'(1717372691055 . 1000)))
(erc-insert-post-hook (cons (lambda ()
(setq erc-stamp--current-time
(time-add erc-stamp--current-time 0.1)))
erc-insert-post-hook))
(dumb-server (erc-d-run "localhost" t
'unexpected-disconnect 'unexpected-disconnect))
;; Define overriding formatting function for catalog entry
;; `disconnected' to spoof time progressing past midnight.
(erc-message-english-disconnected
(let ((orig erc-message-english-disconnected))
(lambda (&rest _)
(setq erc-stamp--current-time
(time-add erc-stamp--current-time 120))
orig)))
(port (process-contact dumb-server :service))
(expect (erc-d-t-make-expecter)))
(ert-info ("Connect")
(with-current-buffer (erc :server "127.0.0.1"
:port port
:nick "tester"
:full-name "tester")
(funcall expect 10 "debug mode")))
;; Ensure date stamps are unique per server buffer.
(with-current-buffer "FooNet"
(funcall expect 10 "[Mon Jun 3 2024]")
(funcall expect -0.1 "[Mon Jun 3 2024]") ; no duplicates
(funcall expect 10 "[00:00]")
(funcall expect -0.1 "[00:00]")
(funcall expect 10 "Welcome to the foonet")
(delete-process erc-server-process))))
;;; erc-scenarios-stamp.el ends here