Re: bug#60933: 30.0.50; ERC >5.5: Make buttonizing more extensible

"J.P." <[email protected]>
Newsgroups gmane.emacs.erc.general
Message-ID <[email protected]>
This feature added an internal interface that allows for creating
ephemeral "phantom" users, which help make nickname buttonizing possible
with protocol bridges and chat-history playback. Unfortunately, the
original design cut a major corner that it shouldn't have. Essentially,
I wanted to avoid adding an `erc-channel-user' to accompany every
phantom `erc-server-user' being spoofed, primarily because it's a waste
of space. However, I've come to believe this shortcut won't be worth the
added maintenance burden of having to check for missing objects when
performing related operations. Attached is a patch to fix this.
0001-5.6-Spoof-channel-users-in-erc-button-phantom-users-.patch (text/x-patch, 9.8 KB)
From 784cdaeee6d9b5ca7138b0cde0e251b475414201 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email protected]>
Date: Wed, 6 Sep 2023 19:40:11 -0700
Subject: [PATCH] [5.6] Spoof channel users in erc-button--phantom-users-mode

* lisp/erc/erc-backend.el (erc--cmem-from-nick-function): Update
forward declaration.
(erc-server-PRIVMSG): Use new name for `erc--user-from-nick-function',
now `erc--cmem-from-nick-function'.
* lisp/erc/erc-button.el (erc-button--phantom-users,
erc-button--phantom-cmems): Rename former to latter.
(erc-button--fallback-user-function,
erc-button--fallback-cmem-function): Rename former to latter.
(erc--phantom-channel-user, erc--phantom-server-user): New superficial
`cl-struct' objects subclassing `erc-channel-user' and
`erc-server-user', respectively.
(erc-button--add-phantom-speaker): Look for channel member instead of
server user, creating one if necessary.  Return a made-up
`erc-channel-user' along with the made-up `erc-server-user'.
(erc-button--get-phantom-user, erc-button--get-phantom-cmem): Rename
former to latter.
(erc-button--phantom-users-mode, erc-button--phantom-users-enable,
erc-button--phantom-users-disable): Use updated names for
function-valued interface vars and their implementing functions.
Remove obsolete comment.
(erc-button-add-nickname-buttons): Attempt to query fallback
function, if non-nil, while populating channel member instead of
server user.
* lisp/erc/erc.el (erc--user-from-nick-function,
erc--cmem-from-nick-function): Rename former to latter.
(erc--examine-nick, erc--get-existing-channel-member): Rename former
to latter.  (Bug#60933)
---
 lisp/erc/erc-backend.el |  4 +--
 lisp/erc/erc-button.el  | 66 +++++++++++++++++++++++------------------
 lisp/erc/erc.el         | 13 ++++----
 3 files changed, 46 insertions(+), 37 deletions(-)

diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 9e121ec1e92..fb10ee31c78 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -104,7 +104,7 @@
 (defvar erc--called-as-input-p)
 (defvar erc--display-context)
 (defvar erc--target)
-(defvar erc--user-from-nick-function)
+(defvar erc--cmem-from-nick-function)
 (defvar erc-channel-list)
 (defvar erc-channel-users)
 (defvar erc-default-nicks)
@@ -1944,7 +1944,7 @@ erc--server-determine-join-display-context
             ;; at this point.
             (erc-update-channel-member (if privp nick tgt) nick nick
                                        privp nil nil nil nil nil host login nil nil t)
-            (let ((cdata (funcall erc--user-from-nick-function
+            (let ((cdata (funcall erc--cmem-from-nick-function
                                   (erc-downcase nick) sndr parsed)))
               (setq fnick (funcall erc-format-nick-function
                                    (car cdata) (cdr cdata))))))
diff --git a/lisp/erc/erc-button.el b/lisp/erc/erc-button.el
index 8c1188e64a2..620ee63fa39 100644
--- a/lisp/erc/erc-button.el
+++ b/lisp/erc/erc-button.el
@@ -380,32 +380,39 @@ erc-button--modify-nick-function
 all faces defined in `erc-button' are bound temporarily and can
 be updated at will.")
 
-(defvar-local erc-button--phantom-users nil)
+(defvar-local erc-button--phantom-cmems nil)
 
-(defvar erc-button--fallback-user-function #'ignore
-  "Function to determine `erc-server-user' if not found in the usual places.
+(defvar erc-button--fallback-cmem-function #'ignore
+  "Function to determine channel member if not found in the usual places.
 Called with DOWNCASED-NICK, NICK, and NICK-BOUNDS when
 `erc-button-add-nickname-buttons' cannot find a user object for
 DOWNCASED-NICK in `erc-channel-users' or `erc-server-users'.")
 
+;; Historical or fictitious users.  As long as these two structs
+;; remain superficial subclasses with the exact same slots and
+;; defaults, they can live here instead of in erc-common.el.  Modules
+;; can use the named getters for the superclasses when doing `setf'
+;; modifications without having to `require' this library.
+(cl-defstruct (erc--phantom-channel-user (:include erc-channel-user)))
+(cl-defstruct (erc--phantom-server-user (:include erc-server-user)))
+
 (defun erc-button--add-phantom-speaker (downcased nuh _parsed)
-  "Stash fictitious `erc-server-user' while processing \"PRIVMSG\".
-Expect DOWNCASED to be the downcased nickname, NUH to be a triple
-of (NICK LOGIN HOST), and parsed to be an `erc-response' object."
   (pcase-let* ((`(,nick ,login ,host) nuh)
-               (user (or (gethash downcased erc-button--phantom-users)
-                         (make-erc-server-user
+               (cmem (gethash downcased erc-button--phantom-cmems))
+               (user (or (car cmem)
+                         (make-erc--phantom-server-user
                           :nickname nick
                           :host (and (not (string-empty-p host)) host)
-                          :login (and (not (string-empty-p login)) login)))))
-    (list (puthash downcased user erc-button--phantom-users))))
+                          :login (and (not (string-empty-p login)) login))))
+               (cuser (or (cdr cmem)
+                          (make-erc--phantom-channel-user
+                           :last-message-time (current-time)))))
+    (puthash downcased (cons user cuser) erc-button--phantom-cmems)
+    (cons user cuser)))
 
-(defun erc-button--get-phantom-user (down _word _bounds)
-  (gethash down erc-button--phantom-users))
+(defun erc-button--get-phantom-cmem (down _word _bounds)
+  (gethash down erc-button--phantom-cmems))
 
-;; In the future, we'll most likely create temporary
-;; `erc-channel-users' tables during BATCH chathistory playback, thus
-;; obviating the need for this mode entirely.
 (define-minor-mode erc-button--phantom-users-mode
   "Minor mode to recognize unknown speakers.
 Expect to be used by module setup code for creating placeholder
@@ -415,22 +422,22 @@ erc-button--phantom-users-mode
 of the channel.  However, don't bother creating an actual
 `erc-channel-user' object because their status prefix is unknown.
 Instead, just spoof an `erc-server-user' and stash it during
-\"PRIVMSG\" handling via `erc--user-from-nick-function' and
+\"PRIVMSG\" handling via `erc--cmem-from-nick-function' and
 retrieve it during buttonizing via
 `erc-button--fallback-user-function'."
   :interactive nil
   (if erc-button--phantom-users-mode
       (progn
-        (add-function :after-until (local 'erc--user-from-nick-function)
-                      #'erc-button--add-phantom-speaker '((depth . -50)))
-        (add-function :after-until (local 'erc-button--fallback-user-function)
-                      #'erc-button--get-phantom-user '((depth . 50)))
-        (setq erc-button--phantom-users (make-hash-table :test #'equal)))
-    (remove-function (local 'erc--user-from-nick-function)
+        (add-function :after-until (local 'erc--cmem-from-nick-function)
+                      #'erc-button--add-phantom-speaker '((depth . 30)))
+        (add-function :after-until (local 'erc-button--fallback-cmem-function)
+                      #'erc-button--get-phantom-cmem '((depth . 50)))
+        (setq erc-button--phantom-cmems (make-hash-table :test #'equal)))
+    (remove-function (local 'erc--cmem-from-nick-function)
                      #'erc-button--add-phantom-speaker)
-    (remove-function (local 'erc-button--fallback-user-function)
-                     #'erc-button--get-phantom-user)
-    (kill-local-variable 'erc-nicks--phantom-users)))
+    (remove-function (local 'erc-button--fallback-cmem-function)
+                     #'erc-button--get-phantom-cmem)
+    (kill-local-variable 'erc-button--phantom-cmems)))
 
 (defun erc-button-add-nickname-buttons (entry)
   "Search through the buffer for nicknames, and add buttons."
@@ -451,11 +458,12 @@ erc-button-add-nickname-buttons
          (down (erc-downcase word)))
       (let* ((erc-button-mouse-face erc-button-mouse-face)
              (erc-button-nickname-face erc-button-nickname-face)
-             (cuser (and erc-channel-users (gethash down erc-channel-users)))
+             (cuser (and erc-channel-users
+                         (or (gethash down erc-channel-users)
+                             (funcall erc-button--fallback-cmem-function
+                                      down word bounds))))
              (user (or (and cuser (car cuser))
-                       (and erc-server-users (gethash down erc-server-users))
-                       (funcall erc-button--fallback-user-function
-                                down word bounds)))
+                       (and erc-server-users (gethash down erc-server-users))))
              (data (list word)))
         (when (or (not (functionp form))
                   (and-let* ((user)
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 07ba32d1cca..ba0733f0ee5 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -5262,14 +5262,15 @@ erc--get-speaker-bounds
                  (next-single-property-change (point-min) 'erc-speaker))))
      (cons beg (next-single-property-change beg 'erc-speaker)))))
 
-(defvar erc--user-from-nick-function #'erc--examine-nick
-  "Function to possibly consider unknown user.
+(defvar erc--cmem-from-nick-function #'erc--get-existing-channel-member
+  "Function returning an `erc-channel-members' object from a nick.
 Must return either nil or a cons of an `erc-server-user' and a
-possibly nil `erc-channel-user' for formatting a server user's
-nick.  Called in the appropriate buffer with the downcased nick,
-the parsed NUH, and the original `erc-response' object.")
+`erc-channel-user' object for formatting a user's nick for
+insertion.  Called in the appropriate target buffer with the
+downcased nick, the parsed NUH, and the current `erc-response'
+object.")
 
-(defun erc--examine-nick (downcased _nuh _parsed)
+(defun erc--get-existing-channel-member (downcased _nuh _parsed)
   (and erc-channel-users (gethash downcased erc-channel-users)))
 
 (defun erc-format-privmessage (nick msg privp msgp)
-- 
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.