]> git.eshelyaron.com Git - emacs.git/commitdiff
Retain client's own user in erc-server-users
authorF. Jason Park <jp@neverwas.me>
Thu, 25 Apr 2024 12:16:23 +0000 (05:16 -0700)
committerEshel Yaron <me@eshelyaron.com>
Wed, 29 May 2024 10:11:52 +0000 (12:11 +0200)
* lisp/erc/erc-backend.el (erc-server-KICK, erc-server-PART): Use new
function `erc--remove-channel-user-but' instead of
`erc-remove-channel-users'.  In `erc-server-KICK', remove sender's
channel membership data after displaying the message so that nicks are
buttonized.  Return nil.  In `erc-server-PART', don't run
`erc-remove-channel-member' when the client itself has parted.
* lisp/erc/erc-common.el (erc--remove-user-from-targets): New
function.
* lisp/erc/erc.el (erc-remove-server-user): Redo doc string.
(erc--forget-server-user-function): New variable.
(erc--forget-server-user): New function.
(erc--forget-server-user-ignoring-queries): New function, the default
value of `erc--forget-server-user-function'.
(erc-remove-channel-user): Defer to `erc--forget-server-user-function'
to do the actual removal.
(erc-remove-user): Defer to `erc--remove-user-from-targets'.
(erc-remove-channel-users): Redo doc
(erc--remove-channel-users-but): New function.  The only use case thus
far is for protecting the client's own `erc-server-users' entry from
removal when draining `erc-channel-members' tables after the client
leaves a target buffer or quits.
(erc-kill-buffer-function): Don't remove own user from
`erc-server-users'.
* test/lisp/erc/erc-scenarios-base-renick.el
(erc-scenarios-base-renick-queries-solo): Assert own client parting
its only channel doesn't remove own user from server.  Also assert
that another user parting their only channel removes them from all
queries.  (Bug#70928)

(cherry picked from commit 5f84213c9802181b4d800615915e3c8dded7b94f)

lisp/erc/erc-backend.el
lisp/erc/erc-common.el
lisp/erc/erc.el
test/lisp/erc/erc-scenarios-base-renick.el

index a26cdd50dd77cdcfc13eff3149df150d1af70a62..a1f84ee5165aa7cc6686cca2dcc4876d6f13eabc 100644 (file)
 (declare-function erc--open-target "erc" (target))
 (declare-function erc--parse-nuh "erc" (string))
 (declare-function erc--query-list "erc" ())
+(declare-function erc--remove-channel-users-but "erc" (nick))
 (declare-function erc--target-from-string "erc" (string))
 (declare-function erc--update-modes "erc" (raw-args))
 (declare-function erc-active-buffer "erc" nil)
@@ -1797,7 +1798,6 @@ add things to `%s' instead."
          (buffer (erc-get-buffer ch proc)))
     (pcase-let ((`(,nick ,login ,host)
                  (erc-parse-user (erc-response.sender parsed))))
-      (erc-remove-channel-member buffer tgt)
       (cond
        ((string= tgt (erc-current-nick))
         (erc-display-message
@@ -1806,17 +1806,20 @@ add things to `%s' instead."
         (run-hook-with-args 'erc-kick-hook buffer)
         (erc-with-buffer
             (buffer)
-          (erc-remove-channel-users))
+          (erc--remove-channel-users-but tgt))
         (with-suppressed-warnings ((obsolete erc-delete-default-channel))
           (erc-delete-default-channel ch buffer))
         (erc-update-mode-line buffer))
        ((string= nick (erc-current-nick))
         (erc-display-message
          parsed 'notice buffer
-         'KICK-by-you ?k tgt ?c ch ?r reason))
+         'KICK-by-you ?k tgt ?c ch ?r reason)
+        (erc-remove-channel-member buffer tgt))
        (t (erc-display-message
-             parsed 'notice buffer
-             'KICK ?k tgt ?n nick ?u login ?h host ?c ch ?r reason))))))
+           parsed 'notice buffer
+           'KICK ?k tgt ?n nick ?u login ?h host ?c ch ?r reason)
+          (erc-remove-channel-member buffer tgt)))))
+  nil)
 
 (define-erc-response-handler (MODE)
   "Handle server mode changes." nil
@@ -1926,15 +1929,15 @@ Return a list of buffers in which to announce the change."
       ;; When `buffer' is nil, `erc-remove-channel-member' and
       ;; `erc-remove-channel-users' do almost nothing, and the message
       ;; is displayed in the server buffer.
-      (erc-remove-channel-member buffer nick)
       (erc-display-message parsed 'notice buffer
                            'PART ?n nick ?u login
                            ?h host ?c chnl ?r (or reason ""))
-      (when (string= nick (erc-current-nick))
+      (cond
+       ((string= nick (erc-current-nick))
         (run-hook-with-args 'erc-part-hook buffer)
         (erc-with-buffer
             (buffer)
-          (erc-remove-channel-users))
+          (erc--remove-channel-users-but nick))
         (with-suppressed-warnings ((obsolete erc-delete-default-channel))
           (erc-delete-default-channel chnl buffer))
         (erc-update-mode-line buffer)
@@ -1942,7 +1945,8 @@ Return a list of buffers in which to announce the change."
         (when (and erc-kill-buffer-on-part buffer)
           (defvar erc-killing-buffer-on-part-p)
           (let ((erc-killing-buffer-on-part-p t))
-            (kill-buffer buffer))))))
+            (kill-buffer buffer))))
+       (t (erc-remove-channel-member buffer nick)))))
   nil)
 
 (define-erc-response-handler (PING)
index c01ee6546cbc994e45b4b518ab7057b425dcdbef..4ba7990ab98504c481c9c64302f70b736cd25e8d 100644 (file)
@@ -560,6 +560,18 @@ Use the CASEMAPPING ISUPPORT parameter to determine the style."
 (defun erc--get-server-user (nick)
   (erc-get-server-user nick))
 
+(define-inline erc--remove-user-from-targets (downcased-nick buffers)
+  "Remove DOWNCASED-NICK from `erc-channel-members' in BUFFERS."
+  (inline-quote
+   (progn
+     (defvar erc-channel-members-changed-hook)
+     (dolist (buffer ,buffers)
+       (when (buffer-live-p buffer)
+         (with-current-buffer buffer
+           (remhash ,downcased-nick erc-channel-users)
+           (when erc-channel-members-changed-hook
+             (run-hooks 'erc-channel-members-changed-hook))))))))
+
 (defmacro erc--with-dependent-type-match (type &rest features)
   "Massage Custom :type TYPE with :match function that pre-loads FEATURES."
   `(backquote-list* ',(car type)
index 773de6e3fea9c7375e9ca2cd1ecd2ded3f21a623..bf0db894a741329ac3749c8ae2c1672caea1cd75 100644 (file)
@@ -581,13 +581,7 @@ Ensure targets with an entry in `erc-server-users' are present in
     erc-server-process))
 
 (defun erc-remove-server-user (nick)
-  "This function is for internal use only.
-
-Removes the user with nickname NICK from the `erc-server-users'
-hash table.  This user is not removed from the
-`erc-channel-users' lists of other buffers.
-
-See also: `erc-remove-user'."
+  "Remove NICK from the session's `erc-server-users' table."
   (erc-with-server-buffer
     (remhash (erc-downcase nick) erc-server-users)))
 
@@ -610,15 +604,29 @@ other buffers are also changed."
               (puthash (erc-downcase new-nick) cdata
                        erc-channel-users)))))))
 
-(defun erc-remove-channel-user (nick)
-  "This function is for internal use only.
-
-Removes the user with nickname NICK from the `erc-channel-users'
-list for this channel.  If this user is not in the
-`erc-channel-users' list of any other buffers, the user is also
-removed from the server's `erc-server-users' list.
+(defvar erc--forget-server-user-function
+  #'erc--forget-server-user-ignoring-queries
+  "Function to conditionally remove a user from `erc-server-users'.
+Called with a nick and its `erc-server-user' object.")
+
+(defun erc--forget-server-user (nick user)
+  "Remove NICK's USER from server table if they're not in any target buffers."
+  (unless (erc-server-user-buffers user)
+    (erc-remove-server-user nick)))
+
+(defun erc--forget-server-user-ignoring-queries (nick user)
+  "Remove NICK's USER from `erc-server-users' if they've parted all channels."
+  (let ((buffers (erc-server-user-buffers user)))
+    (when (or (null buffers)
+              (and (not erc--decouple-query-and-channel-membership-p)
+                   (cl-every #'erc-query-buffer-p buffers)))
+      (when buffers
+        (erc--remove-user-from-targets (erc-downcase nick) buffers))
+      (erc-remove-server-user nick))))
 
-See also: `erc-remove-server-user' and `erc-remove-user'."
+(defun erc-remove-channel-user (nick)
+  "Remove NICK from the current target buffer's `erc-channel-members'.
+If this was their only target, also remove them from `erc-server-users'."
   (let ((channel-data (erc-get-channel-user nick)))
     (when channel-data
       (let ((user (car channel-data)))
@@ -626,32 +634,19 @@ See also: `erc-remove-server-user' and `erc-remove-user'."
               (delq (current-buffer)
                     (erc-server-user-buffers user)))
         (remhash (erc-downcase nick) erc-channel-users)
-        (if (null (erc-server-user-buffers user))
-            (erc-remove-server-user nick))))))
+        (funcall erc--forget-server-user-function nick user)))))
 
 (defun erc-remove-user (nick)
-  "This function is for internal use only.
-
-Removes the user with nickname NICK from the `erc-server-users'
-list as well as from all `erc-channel-users' lists.
-
-See also: `erc-remove-server-user' and
-`erc-remove-channel-user'."
+  "Remove NICK from the server and all relevant channels tables."
   (let ((user (erc-get-server-user nick)))
     (when user
-      (let ((buffers (erc-server-user-buffers user)))
-        (dolist (buf buffers)
-          (if (buffer-live-p buf)
-              (with-current-buffer buf
-                (remhash (erc-downcase nick) erc-channel-users)
-                (run-hooks 'erc-channel-members-changed-hook)))))
+      (erc--remove-user-from-targets (erc-downcase nick)
+                                     (erc-server-user-buffers user))
       (erc-remove-server-user nick))))
 
 (defun erc-remove-channel-users ()
-  "This function is for internal use only.
-
-Removes all users in the current channel.  This is called by
-`erc-server-PART' and `erc-server-QUIT'."
+  "Drain current buffer's `erc-channel-members' table.
+Also remove members from the server table if this was their only buffer."
   (when (erc--target-channel-p erc--target)
     (setf (erc--target-channel-joined-p erc--target) nil))
   (when (and erc-server-connected
@@ -662,6 +657,19 @@ Removes all users in the current channel.  This is called by
              erc-channel-users)
     (clrhash erc-channel-users)))
 
+(defun erc--remove-channel-users-but (nick)
+  "Drain channel users and remove from server, sparing NICK."
+  (when-let ((users (erc-with-server-buffer erc-server-users))
+             (my-user (gethash (erc-downcase nick) users))
+             (original-function erc--forget-server-user-function)
+             (erc--forget-server-user-function
+              (if erc--decouple-query-and-channel-membership-p
+                  erc--forget-server-user-function
+                (lambda (nick user)
+                  (unless (eq user my-user)
+                    (funcall original-function nick user))))))
+    (erc-remove-channel-users)))
+
 (defmacro erc--define-channel-user-status-compat-getter (name c d)
   "Define a gv getter for historical `erc-channel-user' status slot NAME.
 Expect NAME to be a string, C to be its traditionally associated
@@ -9690,7 +9698,9 @@ one of the following hooks:
 `erc-kill-channel-hook' if a channel buffer was killed,
 or `erc-kill-buffer-hook' if any other buffer."
   (when (eq major-mode 'erc-mode)
-    (erc-remove-channel-users)
+    (when-let ((erc--target)
+               (nick (erc-current-nick)))
+      (erc--remove-channel-users-but nick))
     (cond
      ((eq (erc-server-buffer) (current-buffer))
       (run-hooks 'erc-kill-server-hook))
index 19cb1ecde1d82521c71fb3aa1f9f2c303de3ea73..c96b0353e5510452ebd13da7ee3b5c8b1bb75143 100644 (file)
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "Lal"))
         (funcall expect 10 "<Lal> hello")
         (erc-scenarios-common-say "hi")
+        (should-not (erc-get-channel-member "tester"))
         (funcall expect 10 "is now known as Linguo")
         ;; No duplicate message.
         (funcall expect -0.1 "is now known as Linguo")
         ;; No duplicate buffer.
         (erc-d-t-wait-for 1 (equal (buffer-name) "Linguo"))
         (should-not (get-buffer "Lal"))
+        ;; Channel member has been updated
+        (should-not (erc-get-channel-member "Lal"))
+        (should-not (erc-get-server-user "Lal"))
+        (should (erc-get-channel-member "Linguo"))
         (erc-scenarios-common-say "howdy Linguo")))
 
     (with-current-buffer "#foo"
       (funcall expect 10 "is now known as Linguo")
       (funcall expect -0.1 "is now known as Linguo")
+      (funcall expect 10 "has left"))
+
+    ;; User parting a common channel removes them from queries.
+    (with-current-buffer "Linguo"
+      (should-not (erc-get-channel-member "tester"))
+      (erc-d-t-wait-for 10 (null (erc-get-channel-member "Linguo")))
+      (should-not (erc-get-server-user "Linguo")))
+
+    ;; Leaving the client's only channel doesn't remove its user data
+    ;; from the server table (see below, after "get along ...").
+    (with-current-buffer "#foo"
       (erc-scenarios-common-say "/part"))
 
+    ;; Server and "channel" user are *not* (re)created upon receiving
+    ;; a direct message for a user we already have an open query with
+    ;; but with whom we no longer share a channel.
     (with-current-buffer "Linguo"
-      (funcall expect 10 "get along"))))
+      (funcall expect 10 "get along")
+      (should-not (erc-get-channel-member "Linguo"))
+      (should-not (erc-get-channel-member "tester"))
+      (should (erc-get-server-user "tester")))))
 
 ;; Someone you have a query with disconnects and reconnects under a
 ;; new nick (perhaps due to their client appending a backtick or