From: F. Jason Park Date: Thu, 25 Apr 2024 12:16:23 +0000 (-0700) Subject: Retain client's own user in erc-server-users X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=979b806cff9e8d1e50975ab2bb255c127085a853;p=emacs.git Retain client's own user in erc-server-users * 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) --- diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index a26cdd50dd7..a1f84ee5165 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -124,6 +124,7 @@ (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) diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el index c01ee6546cb..4ba7990ab98 100644 --- a/lisp/erc/erc-common.el +++ b/lisp/erc/erc-common.el @@ -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) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 773de6e3fea..bf0db894a74 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -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)) diff --git a/test/lisp/erc/erc-scenarios-base-renick.el b/test/lisp/erc/erc-scenarios-base-renick.el index 19cb1ecde1d..c96b0353e55 100644 --- a/test/lisp/erc/erc-scenarios-base-renick.el +++ b/test/lisp/erc/erc-scenarios-base-renick.el @@ -185,21 +185,43 @@ (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "Lal")) (funcall expect 10 " 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