From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: "J.P." Newsgroups: gmane.emacs.bugs Subject: bug#66578: 30.0.50; ERC 5.6: Retrieve buffer targets sans subscription status Date: Mon, 16 Oct 2023 07:18:35 -0700 Message-ID: <87ttqqirb8.fsf__16373.9196186484$1697466033$gmane$org@neverwas.me> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="24932"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: emacs-erc@gnu.org To: 66578@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Mon Oct 16 16:20:25 2023 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qsOSH-0006Gy-2A for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 16 Oct 2023 16:20:25 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qsORh-0004hI-KN; Mon, 16 Oct 2023 10:19:49 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qsORW-0004dX-Iv; Mon, 16 Oct 2023 10:19:39 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qsORW-0003uI-63; Mon, 16 Oct 2023 10:19:38 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1qsORu-0003OL-9O; Mon, 16 Oct 2023 10:20:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: "J.P." Original-Sender: "Debbugs-submit" Resent-CC: emacs-erc@gnu.org, bug-gnu-emacs@gnu.org Resent-Date: Mon, 16 Oct 2023 14:20:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 66578 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-To: bug-gnu-emacs@gnu.org X-Debbugs-Original-Xcc: emacs-erc@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.169746597612988 (code B ref -1); Mon, 16 Oct 2023 14:20:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 16 Oct 2023 14:19:36 +0000 Original-Received: from localhost ([127.0.0.1]:57472 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qsORS-0003NO-CB for submit@debbugs.gnu.org; Mon, 16 Oct 2023 10:19:36 -0400 Original-Received: from lists.gnu.org ([2001:470:142::17]:46792) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qsORN-0003N6-BY for submit@debbugs.gnu.org; Mon, 16 Oct 2023 10:19:32 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qsOQs-0004U7-Qv for bug-gnu-emacs@gnu.org; Mon, 16 Oct 2023 10:18:59 -0400 Original-Received: from mail-108-mta79.mxroute.com ([136.175.108.79]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qsOQm-0003FS-Uj for bug-gnu-emacs@gnu.org; Mon, 16 Oct 2023 10:18:58 -0400 Original-Received: from mail-111-mta2.mxroute.com ([136.175.111.2] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta79.mxroute.com (ZoneMTA) with ESMTPSA id 18b38da6146000ff68.001 for (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Mon, 16 Oct 2023 14:18:37 +0000 X-Zone-Loop: 81c8216d0df140ba3268e1ff0483ffbb681686499d66 X-Originating-IP: [136.175.111.2] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:Subject:To:From:Sender: Reply-To:Cc:Content-Transfer-Encoding:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=htFZ0EeGLHZZzjln7QVqsePUf2156I1dd6C2UgF2/bw=; b=T+tiSt5gu6TBv48Q78wWaviv7Z +1LxHZfVITSS7P3BHbwzy6AH8XS0ow+Sp/2Pabah78b8pq5AlS17efqC6wefRpnpzGqOHatWlZ77F G+CxJPTAyzwjtj1+38lUFfB24STaJd9QeuURsuCBfRCWjOATizPT83WM3kNGIqsSCYs9yiBVHLfy0 bRuRkDKUQsNXfPnvpt78BwTG9lEESe5/L4ktqz0uLhBIJ0ujraNjMyqKkkCk/kdSy06MYfyrVWUQX vyUZuBvt1/VDaKPA41kllauc/3+9v/QMydwZDctLVaDkI1qPoxmEbmZ8MdHMyK/0h6JOeWeg3bftA UDKpK6Ag==; X-Authenticated-Id: masked@neverwas.me Received-SPF: pass client-ip=136.175.108.79; envelope-from=jp@neverwas.me; helo=mail-108-mta79.mxroute.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:272573 Archived-At: --=-=-= Content-Type: text/plain Tags: patch The go-to public function for determining a buffer's target has always been 'erc-default-target'. However, it doesn't always behave in the most intuitive manner for those unfamiliar with ERC's inner workings. This by itself is not a deal breaker, but things get more complicated when you consider the function's handful of "consumers" in ERC's public API. Take, for example, the function `erc-server-buffer-p'. When called in a PARTed channel, it returns t, just like it has since at least ERC 5.3. What about a more involved dependent function, like `erc-get-buffer'? Its result is pretty straightforward to predict so long as you ignore the same glaring "joinedness" blind spot. I'm proposing we offer a new public function, `erc-target', that simply returns non-nil in any query or channel buffer, regardless of whether the client has left or been kicked, and regardless of network connectivity (logical or otherwise). I'm also proposing that we slowly replace uses of `erc-default-target' in the code base wherever we can comfortably demonstrate that doing so is unlikely to break anything. (We should probably leave a note behind with each replacement mentioning the old behavior.) Attached is an initial sketch of this idea. Please try it if you dare. In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.38, cairo version 1.17.6) of 2023-10-16 built on localhost Repository revision: c3038bf5e1d79c3dfa83717a5a61ecc86116f04a Repository branch: master Windowing system distributor 'The X.Org Foundation', version 11.0.12014000 System Description: Fedora Linux 37 (Workstation Edition) Configured using: 'configure --enable-check-lisp-object-type --enable-checking=yes,glyphs 'CFLAGS=-O0 -g3' PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig' Configured features: ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES NOTIFY INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS WEBP X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB Important settings: value of $LANG: en_US.UTF-8 value of $XMODIFIERS: @im=ibus locale-coding-system: utf-8-unix Major mode: Lisp Interaction Minor modes in effect: tooltip-mode: t global-eldoc-mode: t eldoc-mode: t show-paren-mode: t electric-indent-mode: t mouse-wheel-mode: t tool-bar-mode: t menu-bar-mode: t file-name-shadow-mode: t global-font-lock-mode: t font-lock-mode: t blink-cursor-mode: t minibuffer-regexp-mode: t line-number-mode: t indent-tabs-mode: t transient-mark-mode: t auto-composition-mode: t auto-encryption-mode: t auto-compression-mode: t Load-path shadows: None found. Features: (shadow sort mail-extr emacsbug message mailcap yank-media puny dired dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config gnus-util text-property-search time-date mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils erc auth-source cl-seq eieio eieio-core cl-macs password-cache json subr-x map format-spec cl-loaddefs cl-lib erc-backend erc-networks byte-opt gv bytecomp byte-compile erc-common erc-compat erc-loaddefs rmc iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win term/common-win x-dnd touch-screen tool-bar dnd fontset image regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq simple cl-generic indonesian philippine cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech european ethiopic indian cyrillic chinese composite emoji-zwj charscript charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp files window text-properties overlay sha1 md5 base64 format env code-pages mule custom widget keymap hashtable-print-readable backquote threads dbusbind inotify lcms2 dynamic-setting system-font-setting font-render-setting cairo gtk x-toolkit xinput2 x multi-tty move-toolbar make-network-process emacs) Memory information: ((conses 16 66550 9194) (symbols 48 8704 0) (strings 32 23573 2312) (string-bytes 1 687630) (vectors 16 16105) (vector-slots 8 216057 14988) (floats 8 24 33) (intervals 56 239 0) (buffers 984 11)) --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.6-Rename-erc-server-buffer-p.patch >From 7784c02425d9353294eccff75688326f251ef1dd Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Sun, 15 Oct 2023 07:22:31 -0700 Subject: [PATCH 1/2] [5.6] Rename erc-server-buffer-p * lisp/erc/erc-log.el (erc-log-all-but-server-buffers): Use `erc--server-buffer-p' instead of `erc-server-buffer-p'. This replacement is presumed to be relatively "safe" because this function is unused in the code base and only appears in the doc string for the option `erc-enable-logging'. * lisp/erc/erc-match.el (erc-match-message): Leave comment proposing that `erc--server-buffer-p' should be preferred to `erc-server-buffer-p'. Use preferred alias for `erc-server-buffer-p'. * lisp/erc/erc-notify.el (erc-cmd-NOTIFY): Use preferred alias for `erc-server-buffer-p' and leave FIXME comment. * lisp/erc/erc-speedbar.el (erc-speedbar-buttons): Use `erc--server-buffer-p' instead of `erc-server-buffer-p'. The logic here seems simple enough to justify a change, however the absence of related bug reports is perhaps an argument against this. * lisp/erc/erc-track.el (erc-track-modified-channels): Use preferred alias for `erc-server-buffer-p'. * lisp/erc/erc.el (erc-once-with-server-event): Use `erc--server-buffer-p' instead of `erc-server-buffer-p'. This change seems justified because the function sets local hooks that would otherwise be ignored outside of a server buffer. (erc-server-buffer-p, erc-server-or-unjoined-channel-buffer-p): Make former obsolete alias for latter. (erc--server-buffer-p): New function to replace `erc-server-buffer-p' internally in new code. It returns nil in parted and kicked channels. (erc-open-server-buffer-p): Use `erc--server-buffer-p' instead of `erc-server-buffer-p'. (erc-get-buffer): Mention behavior in doc string regarding parted and kicked-from channels. (erc-cmd-GQUIT): Fix wrong-number-of-arguments bug in timer function. (erc-default-target): Mention behavior regarding unjoined channels. (erc-kill-query-buffers): Don't use `erc-server-buffer-p'. This replacement may break third-party code expecting to leave parted channels behind, but it seems sane when considering only the lone internal use in `erc-cmd-QUIT'. ; * test/lisp/erc/resources/join/network-id/foonet.eld: Timeouts. --- lisp/erc/erc-log.el | 4 +- lisp/erc/erc-match.el | 4 +- lisp/erc/erc-notify.el | 4 +- lisp/erc/erc-speedbar.el | 2 +- lisp/erc/erc-track.el | 4 +- lisp/erc/erc.el | 44 ++++++++++++------- .../erc/resources/join/network-id/foonet.eld | 4 +- 7 files changed, 42 insertions(+), 24 deletions(-) diff --git a/lisp/erc/erc-log.el b/lisp/erc/erc-log.el index 472cc1388a4..79fece5779e 100644 --- a/lisp/erc/erc-log.el +++ b/lisp/erc/erc-log.el @@ -276,11 +276,11 @@ erc-log-disable-logging (defun erc-log-all-but-server-buffers (buffer) "Return t if logging should be enabled in BUFFER. -Returns nil if `erc-server-buffer-p' returns t." +Return nil if BUFFER is a server buffer." (save-excursion (save-window-excursion (set-buffer buffer) - (not (erc-server-buffer-p))))) + (not (erc--server-buffer-p))))) (defun erc-save-query-buffers (process) "Save all buffers of the given PROCESS." diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el index 186717579d7..8644e61106f 100644 --- a/lisp/erc/erc-match.el +++ b/lisp/erc/erc-match.el @@ -491,7 +491,9 @@ erc-match-message (message (buffer-substring message-beg (point-max)))) (when (and vector (not (and erc-match-exclude-server-buffer - (erc-server-buffer-p)))) + ;; FIXME replace with `erc--server-buffer-p' + ;; or explain why that's unwise. + (erc-server-or-unjoined-channel-buffer-p)))) (mapc (lambda (match-type) (goto-char (point-min)) diff --git a/lisp/erc/erc-notify.el b/lisp/erc/erc-notify.el index 55be8976ada..cf7ffbb40d7 100644 --- a/lisp/erc/erc-notify.el +++ b/lisp/erc/erc-notify.el @@ -218,7 +218,9 @@ erc-cmd-NOTIFY ;; from your notify list. (dolist (buf (erc-buffer-list)) (with-current-buffer buf - (if (erc-server-buffer-p) + ;; FIXME replace with `erc--server-buffer-p' or + ;; explain why that's unwise. + (if (erc-server-or-unjoined-channel-buffer-p) (setq erc-last-ison (delete (car args) erc-last-ison)))))) (setq erc-notify-list (cons (erc-string-no-properties (car args)) erc-notify-list))) diff --git a/lisp/erc/erc-speedbar.el b/lisp/erc/erc-speedbar.el index 625d59530b0..bb5fad6f52f 100644 --- a/lisp/erc/erc-speedbar.el +++ b/lisp/erc/erc-speedbar.el @@ -135,7 +135,7 @@ erc-speedbar-buttons (erase-buffer) (let (serverp chanp queryp) (with-current-buffer buffer - (setq serverp (erc-server-buffer-p)) + (setq serverp (erc--server-buffer-p)) (setq chanp (erc-channel-p (erc-default-target))) (setq queryp (erc-query-buffer-p))) (cond (serverp diff --git a/lisp/erc/erc-track.el b/lisp/erc/erc-track.el index 64e59a90047..c8f2e04c3eb 100644 --- a/lisp/erc/erc-track.el +++ b/lisp/erc/erc-track.el @@ -795,7 +795,9 @@ erc-track-modified-channels (if (and (not (erc-buffer-visible (current-buffer))) (not (member this-channel erc-track-exclude)) (not (and erc-track-exclude-server-buffer - (erc-server-buffer-p))) + ;; FIXME either use `erc--server-buffer-p' or + ;; explain why that's unwise. + (erc-server-or-unjoined-channel-buffer-p))) (not (erc-message-type-member (or (erc-find-parsed-property) (point-min)) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 5bf6496e926..7144e81397a 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1417,7 +1417,7 @@ erc-once-with-server-event channel-buffers it may not work at all, as it uses the LOCAL argument of `add-hook' and `remove-hook' to ensure multiserver capabilities." - (unless (erc-server-buffer-p) + (unless (erc--server-buffer-p) (error "You should only run `erc-once-with-server-event' in a server buffer")) (let ((fun (make-symbol "fun")) @@ -1474,19 +1474,30 @@ erc-server-buffer-live-p (and (processp erc-server-process) (buffer-live-p (process-buffer erc-server-process)))) -(defun erc-server-buffer-p (&optional buffer) +(define-obsolete-function-alias + 'erc-server-buffer-p 'erc-server-or-unjoined-channel-buffer-p "30.1") +(defun erc-server-or-unjoined-channel-buffer-p (&optional buffer) "Return non-nil if argument BUFFER is an ERC server buffer. - -If BUFFER is nil, the current buffer is used." +If BUFFER is nil, use the current buffer. For historical +reasons, also return non-nil for channel buffers the client has +parted or from which it's been kicked." (with-current-buffer (or buffer (current-buffer)) (and (eq major-mode 'erc-mode) (null (erc-default-target))))) +(defun erc--server-buffer-p (&optional buffer) + "Return non-nil if BUFFER is an ERC server buffer. +Without BUFFER, use the current buffer." + (if buffer + (with-current-buffer buffer + (and (eq major-mode 'erc-mode) (null erc--target))) + (and (eq major-mode 'erc-mode) (null erc--target)))) + (defun erc-open-server-buffer-p (&optional buffer) "Return non-nil if BUFFER is an ERC server buffer with an open IRC process. If BUFFER is nil, the current buffer is used." - (and (erc-server-buffer-p buffer) + (and (erc--server-buffer-p buffer) (erc-server-process-alive buffer))) (defun erc-query-buffer-p (&optional buffer) @@ -1858,7 +1869,10 @@ erc-member-ignore-case (defun erc-get-buffer (target &optional proc) "Return the buffer matching TARGET in the process PROC. -If PROC is not supplied, all processes are searched." +Without PROC, search all ERC buffers. For historical reasons, +skip buffers for channels the client has \"PART\"ed or from which +it's been \"KICK\"ed. Expect users to use a different function +for finding targets independent of \"JOIN\"edness." (let ((downcased-target (erc-downcase target))) (catch 'buffer (erc-buffer-filter @@ -4584,10 +4598,7 @@ erc-cmd-GQUIT ;; kill them (run-at-time 4 nil - (lambda () - (dolist (buffer (erc-buffer-list (lambda (buf) - (not (erc-server-buffer-p buf))))) - (kill-buffer buffer))))) + #'erc-buffer-do (lambda () (when erc--target (kill-buffer))))) t) (defalias 'erc-cmd-GQ #'erc-cmd-GQUIT) @@ -7027,7 +7038,9 @@ erc--current-buffer-joined-p ;; continue to use `erc-default-target'. (defun erc-default-target () - "Return the current default target (as a character string) or nil if none." + "Return the current channel or query target, if any. +For historical reasons, return nil in channel buffers if not +currently joined." (let ((tgt (car erc-default-recipients))) (cond ((not tgt) nil) @@ -7589,15 +7602,14 @@ erc-directory-writable-p (unless (file-attributes dir) (make-directory dir)) (or (file-accessible-directory-p dir) (error "Cannot access %s" dir))) +;; FIXME make function obsolete or alias to something less confusing. (defun erc-kill-query-buffers (process) - "Kill all buffers of PROCESS. -Does nothing if PROCESS is not a process object." + "Kill all target buffers of PROCESS, including channel buffers. +Do nothing if PROCESS is not a process object." ;; here, we only want to match the channel buffers, to avoid ;; "selecting killed buffers" b0rkage. (when (processp process) - (erc-with-all-buffers-of-server process - (lambda () - (not (erc-server-buffer-p))) + (erc-with-all-buffers-of-server process (lambda () erc--target) (kill-buffer (current-buffer))))) (defun erc-nick-at-point () diff --git a/test/lisp/erc/resources/join/network-id/foonet.eld b/test/lisp/erc/resources/join/network-id/foonet.eld index 7d63f5f0c6c..74a107f8144 100644 --- a/test/lisp/erc/resources/join/network-id/foonet.eld +++ b/test/lisp/erc/resources/join/network-id/foonet.eld @@ -1,8 +1,8 @@ ;; -*- mode: lisp-data; -*- ((pass 10 "PASS :foonet:changeme")) -((nick 1 "NICK tester")) +((nick 10 "NICK tester")) -((user 1 "USER user 0 * :tester") +((user 10 "USER user 0 * :tester") (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") (0 ":irc.foonet.org 003 tester :This server was created Mon, 10 May 2021 00:58:22 UTC") -- 2.41.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-5.6-Add-function-erc-target.patch >From 8ec6095d2f433e505765874c043903d0975a70e7 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Sun, 15 Oct 2023 13:20:07 -0700 Subject: [PATCH 2/2] [5.6] Add function erc-target * etc/ERC-NEWS: Mention `erc-target' and new `erc-server-buffer-p' alias. * lisp/erc/erc-backend.el (erc-process-sentinel): Set `joined-p' slot of `erc--target-channel' object to nil if applicable. (JOIN): Mark `erc--target-channel' object as being joined. * lisp/erc/erc-common.el (erc--target-channel): Add `joined-p' slot. Use hyphenated name so accessor function's name ends in "joined-p" rather than "joinedp". (erc--target): Relocate here from erc.el. (erc-target): New public API function to return the current buffer's target as a string even in channels that have been unjoined. * lisp/erc/erc-networks.el (erc--default-target): Remove forward declaration. (erc-networks--id-reload): Use `erc-target' instead of `erc--default-target' as predicate for visiting target buffers. * lisp/erc/erc.el (erc-remove-channel-users): Set channel "joinedness" to nil in `erc--target-channel' object, when applicable. (erc--target): Move to erc-common. (erc--default-target): Remove, replaced by new function `erc-target'. (erc-query-buffer-p): Use `erc-target'. (erc-after-connect): Revise doc string. (erc-connection-established): Revise doc string and move `erc-unhide-query-prompt' business before hook. (erc--current-buffer-joined-p): Remove comment and use new `joined-p' slot of `erc--target-channel' for determining "joinedness" of channel. (erc-kill-buffer-function): Use `erc--target-channel-p' for detecting whether the buffer is a channel buffer. * test/lisp/erc/erc-networks-tests.el (erc-networks--shrink-ids-and-buffer-names--hook-collapse-target): Remove comment. * test/lisp/erc/erc-scenarios-base-reuse-buffers.el (erc-scenarios-common--base-reuse-buffers-channel-buffers): Clarify invariant. * test/lisp/erc/erc-tests.el (erc-with-all-buffers-of-server): Replace `erc-default-recipients' with `erc--target'. (erc--target-from-string): Fix expected shape of `erc--target-channel' struct. (erc-message): Set `erc--target' in buffer "#chan". --- etc/ERC-NEWS | 18 +++++ lisp/erc/erc-backend.el | 5 +- lisp/erc/erc-common.el | 22 ++++--- lisp/erc/erc-networks.el | 12 ++-- lisp/erc/erc.el | 65 ++++++------------- test/lisp/erc/erc-networks-tests.el | 5 -- .../erc/erc-scenarios-base-reuse-buffers.el | 2 + test/lisp/erc/erc-tests.el | 19 +++--- 8 files changed, 72 insertions(+), 76 deletions(-) diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS index 2e56539f210..4b261254bda 100644 --- a/etc/ERC-NEWS +++ b/etc/ERC-NEWS @@ -402,6 +402,24 @@ use of 'insert-before-markers' instead of 'insert'. As always, users feeling unduly inconvenienced by these changes are encouraged to voice their concerns on the bug list. +*** Introducing new ways to detect ERC buffer types. +The old standby 'erc-default-target' has served ERC well for over two +decades. But a lesser known gotcha affecting its use has always +haunted an unlucky few, that is, the function has always returned +non-nil in "unjoined" channel buffers (those that the client has +parted with or been kicked from). While perhaps not itself a major +footgun, recessive pitfalls rooted in this subtlety continue to affect +dependent functions, like 'erc-get-buffer'. + +To discourage misuse of 'erc-default-target', ERC 5.6 offers an +alternative in the function 'erc-target', which is identical to the +former except for its disregard for "joinedness." As a related bonus, +the dependent function 'erc-server-buffer-p' is being rebranded as +'erc-server-or-unjoined-channel-buffer-p'. Unfortunately, this +release lacks a similar solution for detecting "joinedness" directly, +but users can turn to 'xor'-ing 'erc-default-target' and 'erc-target' +as a makeshift kludge. + *** Miscellaneous changes Two helper macros from GNU ELPA's Compat library are now available to third-party modules as 'erc-compat-call' and 'erc-compat-function'. diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 3d34fc97d00..29b69fad0e6 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -1103,7 +1103,7 @@ erc-process-sentinel (erc--register-connection) ;; assume event is 'failed (erc-with-all-buffers-of-server cproc nil - (setq erc-server-connected nil)) + (setq erc-server-connected nil)) (when erc-server-ping-handler (progn (cancel-timer erc-server-ping-handler) (setq erc-server-ping-handler nil))) @@ -1111,6 +1111,8 @@ erc-process-sentinel (erc-current-nick) (system-name) "") (dolist (buf (erc-buffer-filter (lambda () (boundp 'erc-channel-users)) cproc)) (with-current-buffer buf + (when (erc--target-channel-p erc--target) + (setf (erc--target-channel-joined-p erc--target) nil)) (setq erc-channel-users (make-hash-table :test 'equal)))) ;; Hide the prompt (erc--hide-prompt cproc) @@ -1729,6 +1731,7 @@ erc--server-determine-join-display-context (with-suppressed-warnings ((obsolete erc-add-default-channel)) (erc-add-default-channel chnl)) + (setf (erc--target-channel-joined-p erc--target) t) (erc-server-send (format "MODE %s" chnl))) (erc-with-buffer (chnl proc) (erc-channel-begin-receiving-names)) diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el index 8d896e663b5..930e8032f6d 100644 --- a/lisp/erc/erc-common.el +++ b/lisp/erc/erc-common.el @@ -81,16 +81,13 @@ erc--target (string "" :type string :documentation "Received name of target.") (symbol nil :type symbol :documentation "Case-mapped name as symbol.")) -;; At some point, it may make sense to add a query type with an -;; account field, which may help support reassociation across -;; reconnects and nick changes (likely requires v3 extensions). -;; -;; These channel variants should probably take on a `joined' field to -;; track "joinedness", which `erc-server-JOIN', `erc-server-PART', -;; etc. should toggle. Functions like `erc--current-buffer-joined-p' -;; may find it useful. +;; At some point, it may make sense to add a separate query type, +;; possibly with an account field to help reassociation across +;; reconnects and nick changes. + +(cl-defstruct (erc--target-channel (:include erc--target)) + (joined-p nil :type boolean :documentation "Whether channel is joined.")) -(cl-defstruct (erc--target-channel (:include erc--target))) (cl-defstruct (erc--target-channel-local (:include erc--target-channel))) ;; Beginning in 5.5/29.1, the `tags' field may take on one of two @@ -427,6 +424,13 @@ erc-with-all-buffers-of-server ,@forms)) ,process))) +(defvar-local erc--target nil + "A permanent `erc--target' struct instance in channel and query buffers.") + +(define-inline erc-target () + "Return target of current buffer, if any, as a string." + (inline-quote (and erc--target (erc--target-string erc--target)))) + (defun erc-log-aux (string) "Do the debug logging of STRING." (let ((cb (current-buffer)) diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el index d73d715db2c..dd047243a3c 100644 --- a/lisp/erc/erc-networks.el +++ b/lisp/erc/erc-networks.el @@ -53,7 +53,6 @@ erc-server-parameters (defvar erc-server-process) (defvar erc-session-server) -(declare-function erc--default-target "erc" nil) (declare-function erc--get-isupport-entry "erc-backend" (key &optional single)) (declare-function erc-buffer-filter "erc" (predicate &optional proc)) (declare-function erc-current-nick "erc" nil) @@ -991,12 +990,11 @@ erc-networks--id-reload (erc-networks--id-qualifying-len nid)) (erc-networks--rename-server-buffer (or proc erc-server-process) parsed) (erc-networks--shrink-ids-and-buffer-names-any) - (erc-with-all-buffers-of-server - erc-server-process #'erc--default-target - (when-let* ((new-name (erc-networks--reconcile-buffer-names erc--target - nid)) - ((not (equal (buffer-name) new-name)))) - (rename-buffer new-name 'unique)))) + (erc-with-all-buffers-of-server erc-server-process #'erc-target + (when-let + ((new-name (erc-networks--reconcile-buffer-names erc--target nid)) + ((not (equal (buffer-name) new-name)))) + (rename-buffer new-name 'unique)))) (cl-defgeneric erc-networks--id-ensure-comparable (self other) "Take measures to ensure two net identities are in comparable states.") diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 7144e81397a..26f7f5bf188 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -534,6 +534,8 @@ erc-remove-channel-users Removes all users in the current channel. This is called by `erc-server-PART' and `erc-server-QUIT'." + (when (erc--target-channel-p erc--target) + (setf (erc--target-channel-joined-p erc--target) nil)) (when (and erc-server-connected (erc-server-process-alive) (hash-table-p erc-channel-users)) @@ -1391,16 +1393,6 @@ erc--target-from-string #'make-erc--target) :string string :symbol (intern (erc-downcase string)))) -(defvar-local erc--target nil - "Info about a buffer's target, if any.") - -;; Temporary internal getter to ease transition to `erc--target' -;; everywhere. Will be replaced by updated `erc-default-target'. -(defun erc--default-target () - "Return target string or nil." - (when erc--target - (erc--target-string erc--target))) - (defun erc-once-with-server-event (event f) "Run function F the next time EVENT occurs in the `current-buffer'. @@ -1504,7 +1496,7 @@ erc-query-buffer-p "Return non-nil if BUFFER is an ERC query buffer. If BUFFER is nil, the current buffer is used." (with-current-buffer (or buffer (current-buffer)) - (let ((target (erc-default-target))) + (let ((target (erc-target))) (and (eq major-mode 'erc-mode) target (not (memq (aref target 0) '(?# ?& ?+ ?!))))))) @@ -2480,10 +2472,13 @@ erc-before-connect :type '(repeat function)) (defcustom erc-after-connect nil - "Functions called after connecting to a server. -This functions in this variable gets executed when an end of MOTD -has been received. All functions in here get called with the -parameters SERVER and NICK." + "Abnormal hook run upon establishing a logical IRC connection. +Runs on MOTD's end when `erc-server-connected' becomes non-nil. +ERC calls members with `erc-server-announced-name', falling back +to the 376/422 message's \"sender\", as well as the current nick, +as given by the 376/422 message's \"target\" parameter, which is +typically the same as that reported by `erc-current-nick'." + :package-version '(ERC . "5.6") ; FIXME sync on release :group 'erc-hooks :type '(repeat function)) @@ -5701,9 +5696,7 @@ erc-handle-login (erc-load-script f))))) (defun erc-connection-established (proc parsed) - "Run just after connection. - -Set user modes and run `erc-after-connect' hook." + "Set user mode and run `erc-after-connect' hook in server buffer." (with-current-buffer (process-buffer proc) (unless erc-server-connected ; only once per session (let ((server (or erc-server-announced-name @@ -5722,14 +5715,11 @@ erc-connection-established (erc-update-mode-line) (erc-set-initial-user-mode nick buffer) (erc-server-setup-periodical-ping buffer) - (run-hook-with-args 'erc-after-connect server nick)))) - - (when erc-unhide-query-prompt - (erc-with-all-buffers-of-server proc - nil ; FIXME use `erc--target' after bug#48598 - (when (and (erc-default-target) - (not (erc-channel-p (car erc-default-recipients)))) - (erc--unhide-prompt))))) + (when erc-unhide-query-prompt + (erc-with-all-buffers-of-server erc-server-process nil + (when (and erc--target (not (erc--target-channel-p erc--target))) + (erc--unhide-prompt)))) + (run-hook-with-args 'erc-after-connect server nick))))) (defun erc-set-initial-user-mode (nick buffer) "If `erc-user-mode' is non-nil for NICK, set the user modes. @@ -7017,25 +7007,11 @@ erc-nick-equal-p ;; default target handling (defun erc--current-buffer-joined-p () - "Return whether the current target buffer is joined." - ;; This may be a reliable means of detecting subscription status, - ;; but it's also roundabout and awkward. Perhaps it's worth - ;; discussing adding a joined slot to `erc--target' for this. + "Return non-nil if the current buffer is a channel and is joined." (cl-assert erc--target) (and (erc--target-channel-p erc--target) - (erc-get-channel-user (erc-current-nick)) t)) - -;; While `erc-default-target' happens to return nil in channel buffers -;; you've parted or from which you've been kicked, using it to detect -;; whether a channel is currently joined may become unreliable in the -;; future. For now, third-party code can use -;; -;; (erc-get-channel-user (erc-current-nick)) -;; -;; A predicate may be provided eventually. For retrieving a target's -;; name regardless of subscription or connection status, new library -;; code should use `erc--default-target'. Third-party code should -;; continue to use `erc-default-target'. + (erc--target-channel-joined-p erc--target) + t)) (defun erc-default-target () "Return the current channel or query target, if any. @@ -8267,6 +8243,7 @@ erc-kill-buffer-hook :group 'erc-hooks :type 'hook) +;; FIXME alias and deprecate current *-function suffixed name. (defun erc-kill-buffer-function () "Function to call when an ERC buffer is killed. This function should be on `kill-buffer-hook'. @@ -8280,7 +8257,7 @@ erc-kill-buffer-function (cond ((eq (erc-server-buffer) (current-buffer)) (run-hooks 'erc-kill-server-hook)) - ((erc-channel-p (or (erc-default-target) (buffer-name))) + ((erc--target-channel-p erc--target) (run-hooks 'erc-kill-channel-hook)) (t (run-hooks 'erc-kill-buffer-hook))))) diff --git a/test/lisp/erc/erc-networks-tests.el b/test/lisp/erc/erc-networks-tests.el index e95d99c128f..e5069880bc5 100644 --- a/test/lisp/erc/erc-networks-tests.el +++ b/test/lisp/erc/erc-networks-tests.el @@ -623,11 +623,6 @@ erc-networks--shrink-ids-and-buffer-names--hook-collapse-target :symbol 'foonet/dummy :parts [foonet "dummy"] :len 2) - ;; `erc-kill-buffer-function' uses legacy target detection - ;; but falls back on buffer name, so no need for: - ;; - ;; erc-default-recipients '("#a") - ;; erc--target (erc--target-from-string "#a") erc-server-process (with-temp-buffer (erc-networks-tests--create-dead-proc))) diff --git a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el index 71027a0c138..af483bb1a52 100644 --- a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el +++ b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el @@ -124,6 +124,7 @@ erc-scenarios-common--base-reuse-buffers-channel-buffers (erc-d-t-search-for 1 "shake my sword") (erc-cmd-PART "#chan") (funcall expect 3 "You have left channel #chan") + (should-not (erc-get-channel-user (erc-current-nick))) (erc-cmd-JOIN "#chan"))) (ert-info ("Part #chan@barnet") @@ -139,6 +140,7 @@ erc-scenarios-common--base-reuse-buffers-channel-buffers (get-buffer "#chan/127.0.0.1<3>")) (ert-info ("Activity continues in new, -suffixed #chan@foonet buffer") + ;; The first /JOIN did not cause the same buffer to be reused. (with-current-buffer "#chan/127.0.0.1" (should-not (erc-get-channel-user (erc-current-nick)))) (with-current-buffer "#chan/127.0.0.1<3>" diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index 4f4662f5075..5155a8fc724 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -69,26 +69,25 @@ erc-with-all-buffers-of-server (with-current-buffer (get-buffer-create "#foo") (erc-mode) (setq erc-server-process proc-exnet) - (setq erc-default-recipients '("#foo"))) + (setq erc--target (erc--target-from-string "#foo"))) (with-current-buffer (get-buffer-create "#spam") (erc-mode) (setq erc-server-process proc-onet) - (setq erc-default-recipients '("#spam"))) + (setq erc--target (erc--target-from-string "#spam"))) (with-current-buffer (get-buffer-create "#bar") (erc-mode) (setq erc-server-process proc-onet) - (setq erc-default-recipients '("#bar"))) + (setq erc--target (erc--target-from-string "#bar"))) (with-current-buffer (get-buffer-create "#baz") (erc-mode) (setq erc-server-process proc-exnet) - (setq erc-default-recipients '("#baz"))) + (setq erc--target (erc--target-from-string "#baz"))) (should (eq (get-buffer-process "ExampleNet") proc-exnet)) - (erc-with-all-buffers-of-server (get-buffer-process "ExampleNet") - nil + (erc-with-all-buffers-of-server (get-buffer-process "ExampleNet") nil (kill-buffer)) (should-not (get-buffer "ExampleNet")) @@ -102,8 +101,7 @@ erc-with-all-buffers-of-server (calls 0) (get-test (lambda () (cl-incf calls) test))) - (erc-with-all-buffers-of-server proc-onet - (funcall get-test) + (erc-with-all-buffers-of-server proc-onet (funcall get-test) (kill-buffer)) (should (= calls 1))) @@ -812,7 +810,7 @@ erc--restore-initialize-priors (ert-deftest erc--target-from-string () (should (equal (erc--target-from-string "#chan") - #s(erc--target-channel "#chan" \#chan))) + #s(erc--target-channel "#chan" \#chan nil))) (should (equal (erc--target-from-string "Bob") #s(erc--target "Bob" bob))) @@ -820,7 +818,7 @@ erc--target-from-string (let ((erc--isupport-params (make-hash-table))) (puthash 'CHANTYPES '("&#") erc--isupport-params) (should (equal (erc--target-from-string "&Bitlbee") - #s(erc--target-channel-local "&Bitlbee" &bitlbee))))) + #s(erc--target-channel-local "&Bitlbee" &bitlbee nil))))) (ert-deftest erc--modify-local-map () (when (and (bound-and-true-p erc-irccontrols-mode) @@ -1846,6 +1844,7 @@ erc-message (erc-mode) (setq erc-server-process (buffer-local-value 'erc-server-process (get-buffer "ExampleNet")) + erc--target (erc--target-from-string "#chan") erc-default-recipients '("#chan") erc-channel-users (make-hash-table :test 'equal) erc-network 'ExampleNet) -- 2.41.0 --=-=-=--