(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)))))
: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."))
(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))
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))
(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
;; 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
(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