unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
@ 2023-04-14 13:56 J.P.
  0 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-04-14 13:56 UTC (permalink / raw)
  To: 62833; +Cc: emacs-erc

[-- Attachment #1: Type: text/plain, Size: 7315 bytes --]

Tags: patch

Hi,

I'm hoping this can serve as a unified "omnibug" for all the overlapping
discourse scattered about regarding buffer-display options and the
behavior they produce. The main focus will be those aspects impacting
ERC 5.6 and how they integrate with the upstream display-buffer facility
provided by window.el. In a sense, this is a spiritual successor to

  bug#51753: ERC switches to channel buffer on reconnect

and will inherit the outstanding display-related portions of

  bug#60428: 30.0.50; ERC >5.5: Make M-x erc a more welcoming entry point

the most recent being this proposal:

> This bug's original patch set tried to walk back an austere aspect of
> bug#51753's changes that lumped interactive buffer creation in with
> protocol driven creation. It mainly did so by introducing a new option,
> `erc-interactive-display', that reinstated direct window-buffer
> replacement when running M-x erc. However, as pointed out by Corwin on
> Libera, this did not account for interactive /JOINs.
>
> The attached patch aims to address this oversight as well as simplify
> the display-options picture a bit further by consolidating the roles of
> `erc-interactive-display' and `erc-query-display'. The thinking is that
> both options cover the same basic ground involving buffers created as a
> consequence of issuing slash commands. This patch also adds an interface
> for other such commands to use for detecting when they're being called
> from the command line.

Despite this, I'm still rather unsure about merging our ancient display
options. Aliasing `erc-interactive-display' to `erc-query-display' seems
sane because the latter only appears once, in an interactive command. A
more daring and arguably more meaningful move would be to repurpose
`erc-auto-query' (newly aliased to `erc-receive-query-display') as
something like a more general `erc-receive-display', which could cover
display handling for anything protocol driven (i.e., "non-interactive").
Among the reasons to abstain might be

  1. it would necessarily involve redefining the meaning of the option's
     nil value to mean "fall back to erc-join-buffer" instead of "print
     to the server buffer instead," which has no available alternative

  2. it invites an initial security risk (reminiscent of bug#51753)
     or at least a service interruption

For the first point, redefining the nil value is probably justified in
theory because all other values only concern themselves with where a
buffer is displayed, not where messages are routed for printing or
whether buffers are even created, which is the province of dedicated
switches (see note below re `erc-query-on-unjoined-chan-privmsg'). So I
guess this really comes down to whether providing an alternative
companion option and noting it in the doc string is an acceptable trade
off. Point two is more challenging but could perhaps be addressed by a
one-off warning requiring a click-through, before which
`erc-join-buffer' (now `erc-buffer-display') would remain in effect.

There's also the matter of assigning Custom groups for these options.
It'd be "nice" if we could tag these with multiple groups rather than
confine them to exclusive ownership. They're currently spread over
`erc-buffers', `erc-query', and `erc-display'. All seem to have valid
claims when you consider their respective constituencies.

It's also been casually suggested that we might consider deferring to
`erc-setup-buffer' in areas not directly involved in message handling,
such as in erc-sidebar, to allow the options in question to influence
how buffers are displayed more generally. Not sure I have an opinion on
this quite yet, but if anyone else does, please share.

Lastly, I've tacked on another patch that's somewhat related in the
buffer-creation (but not so much -display) sense. It concerns the
restoration of the option `erc-query-on-unjoined-chan-privmsg', which
was orphaned in 5.5 (by me) both out of ignorance and an abundance of
caution.

Thanks.


In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
 3.24.35, cairo version 1.17.6) of 2023-04-12 built on localhost
Repository revision: 861cf3a5c9d2081d811dcfc2c5ce5357f3dc44d4
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12014000
System Description: Fedora Linux 36 (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
  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 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
move-toolbar gtk x-toolkit xinput2 x multi-tty make-network-process
emacs)

Memory information:
((conses 16 63110 9012)
 (symbols 48 8532 0)
 (strings 32 23158 1921)
 (string-bytes 1 669049)
 (vectors 16 14968)
 (vector-slots 8 206726 6655)
 (floats 8 24 39)
 (intervals 56 231 0)
 (buffers 976 10))


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v1-v2.diff --]
[-- Type: text/x-patch, Size: 19585 bytes --]

From 6b7f66a1eed5d01c312d125f016e48cf4e8ce0c9 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Thu, 13 Apr 2023 00:12:06 -0700
Subject: [PATCH 0/2] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (2):
  [5.6] Revive option erc-query-on-unjoined-chan-privmsg
  [5.6] Extend erc-interactive-display to cover /JOINs

 etc/ERC-NEWS                                  |  24 ++-
 lisp/erc/erc-backend.el                       |   4 +-
 lisp/erc/erc.el                               |  81 +++++----
 test/lisp/erc/erc-scenarios-base-attach.el    | 147 ++++++++++++++++
 .../erc/erc-scenarios-base-buffer-display.el  | 158 ++++++++++++++++++
 test/lisp/erc/erc-scenarios-base-reconnect.el |  89 ----------
 test/lisp/erc/erc-tests.el                    |  13 +-
 .../base/channel-buffer-revival/reattach.eld  |  56 +++++++
 8 files changed, 439 insertions(+), 133 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-attach.el
 create mode 100644 test/lisp/erc/erc-scenarios-base-buffer-display.el
 create mode 100644 test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld

Interdiff:
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 8f1b89f268b..e9de48b2e34 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -37,15 +37,18 @@ decade overdue, this is no longer the case.  Other UX improvements in
 this area aim to make the process of connecting interactively slightly
 more streamlined and less repetitive, even for veteran users.
 
-** New buffer-display option 'erc-interactive-display'.
+** Revised buffer-display handling for interactive commands.
 A point of friction for new users and one only just introduced with
 ERC 5.5 has been the lack of visual feedback when first connecting via
-M-x erc.  As explained below in the news for 5.5, the discovery of a
-security issue led to new ERC buffers being "buried" on creation.  On
-further reflection, this was judged to have been an overcorrection in
-the case of interactive invocations, hence the new option
-'erc-interactive-display', which is set to 'buffer' (i.e., "take me
-there") by default.  Accompanying this addition are "display"-suffixed
+M-x erc or when issuing a "/JOIN" command at the prompt.  As explained
+below, in the news for 5.5, the discovery of a security issue led to
+most new ERC buffers being "buried" on creation.  On further
+reflection, this was judged to have been an overcorrection in the case
+of interactive invocations, hence the borrowing of an old option,
+'erc-query-display', and the bestowing of a new alias,
+'erc-interactive-display', which better describes its expanded role as
+a more general buffer-display knob for interactive commands ("/QUERY"
+still among them).  Accompanying this addition are "display"-suffixed
 aliases for related options 'erc-join-buffer' and 'erc-auto-query',
 which users have reported as being difficult to discover and remember.
 
@@ -108,6 +111,13 @@ other than the symbol 'erc-button-buttonize-nicks' appearing in the
 "FORM" field (third element) of this entry are considered deprecated
 and will incur a warning.
 
+** The option erc-query-on-unjoined-chan-privmsg has been restored.
+In ERC 5.5, this option was removed from the default client code and
+thus prevented from influencing PRIVMSG handling because its precise
+purpose could not be determined with any confidence.  After some
+consideration, it's now been wired back in with a slightly revised
+role contingent on a few assumptions explained in its doc string.
+
 ** Miscellaneous UX changes.
 Some minor quality-of-life niceties have finally made their way to
 ERC.  For example, the function 'erc-echo-timestamp' is now
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index bdf4e2ddca2..5d44f478719 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -123,6 +123,7 @@ erc-nick
 (defvar erc-nick-change-attempt-count)
 (defvar erc-prompt-for-channel-key)
 (defvar erc-prompt-hidden)
+(defvar erc-query-on-unjoined-chan-privmsg)
 (defvar erc-reuse-buffers)
 (defvar erc-verbose-server-ping)
 (defvar erc-whowas-on-nosuchnick)
@@ -1835,7 +1836,8 @@ define-erc-response-handler
                 (let ((erc-join-buffer erc-auto-query))
                   (setq buffer (erc--open-target nick))))
             ;; A channel buffer has been killed but is still joined
-            (setq buffer (erc--open-target tgt))))
+            (when erc-query-on-unjoined-chan-privmsg
+              (setq buffer (erc--open-target tgt)))))
         (when buffer
           (with-current-buffer buffer
             (when privp (erc--unhide-prompt))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index a3a9b03a93c..2088a755da5 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1530,16 +1530,16 @@ erc-buffer-display
 
 (defvaralias 'erc-query-display 'erc-interactive-display)
 (defcustom erc-interactive-display 'window
-  "How to display buffers created by issuing a /SLASH command.
-Examples include /QUERY and /JOIN.  See `erc-buffer-display' for
-a full description of available choices.
-
-Note that this does not affect the behavior of commands like
-`erc-cmd-JOIN' when called from lisp code.  Also, this option
-used to be called `erc-query-display' but now applies to more
-than just /QUERY.  See option `erc-receive-query-display' in the
-Custom group `erc-query' to decide how private messages from
-other people should be displayed."
+  "How to display buffers created from user interaction.
+Examples include the \"slash\" commands /QUERY and /JOIN.  See
+`erc-buffer-display' for a full description of available choices.
+
+Note that this doesn't affect commands like `erc-cmd-JOIN' when
+called from lisp code.  Formerly known as `erc-query-display',
+this option now applies to more than just /QUERY.  See option
+`erc-receive-display' in the same Custom group to decide how
+buffers created as a result of server-initiated activity should
+be displayed."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
   :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
@@ -4757,15 +4757,25 @@ erc-auto-query
                  (const :tag "Use current buffer" buffer)
                  (const :tag "Use current buffer" t)))
 
-;; FIXME either retire this or put it to use after determining how
-;; it's meant to work.  Clearly, the doc string does not describe
-;; current behavior.  It's currently only used by the obsolete
-;; function `erc-auto-query'.
 (defcustom erc-query-on-unjoined-chan-privmsg t
   "If non-nil create query buffer on receiving any PRIVMSG at all.
 This includes PRIVMSGs directed to channels.  If you are using an IRC
 bouncer, such as dircproxy, to keep a log of channels when you are
-disconnected, you should set this option to t."
+disconnected, you should set this option to t.
+
+Note that this option was absent from ERC 5.5 because knowledge
+of its intended role was \"unavailable\" during a major
+refactoring involving buffer management.  The option has since
+been restored in ERC 5.6 with the caveat that its purpose is
+assumed to more or less reflect the following, which can be
+thought of as an updated version of the paragraph above:
+
+If non-nil, create a channel buffer on receiving a PRIVMSG if
+none exists.  This only affects messages targeting a channel
+whose buffer was killed but to which you're still joined or one
+whose buffer never existed, presumably because a bouncer is
+sending playback summoned with a command similar to ZNC's
+PLAYBUFFER."
   :group 'erc-query
   :type 'boolean)
 
diff --git a/test/lisp/erc/erc-scenarios-base-attach.el b/test/lisp/erc/erc-scenarios-base-attach.el
new file mode 100644
index 00000000000..91763842879
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-base-attach.el
@@ -0,0 +1,147 @@
+;;; erc-scenarios-base-attach.el --- Reattach scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See also: `erc-scenarios-base-channel-buffer-revival'.
+;;
+;; ERC 5.5 silently dropped support for the ancient option
+;; `erc-query-on-unjoined-chan-privmsg' because the tangled logic in
+;; and around the function `erc-auto-query' made it difficult to
+;; divine its purpose.
+;;
+;; Based on the name, it was thought this option likely involved
+;; controlling the creation of query buffers for unsolicited messages
+;; from users with whom you don't share a common channel.  However,
+;; additional spelunking has recently revealed that it was instead
+;; meant to service a feature offered by most bouncers that sends
+;; PRIVMSGs directed at a channel you're no longer in and that you
+;; haven't received a(nother) JOIN message for.  IOW, this is meant to
+;; support the following sequence of events:
+;;
+;;   1. /detach #chan
+;;   2. kill buffer #chan or reconnect in new Emacs session
+;;   3. /playbuffer #chan
+;;
+;; Note that the above slash commands are bouncer-specific aliases.
+;;
+;; Interested users can find more info by looking at this change set
+;; from the ancient CVS repo:
+;;
+;;   Author:     Mario Lang <mlang@delysid.org>
+;;   AuthorDate: Mon Nov 26 18:33:19 2001 +0000
+;;
+;;   * new function erc-BBDB-NICK to handle nickname anotation ...
+;;   * Applied antifuchs/mhp patches, the latest on erc-help, unmodified
+;;   * New variable: erc-reuse-buffers default to t.
+;;   * Modified erc-generate-new-buffer-name to use it. it checks if
+;;     server and port are the same, then one can assume thats the same
+;;     channel/query target again.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(ert-deftest erc-scenarios-base-attach--query-on-unjoined-enabled ()
+  :tags '(:expensive-test)
+  (should erc-query-on-unjoined-chan-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/channel-buffer-revival")
+       (dumb-server (erc-d-run "localhost" t 'reattach))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "tester@vanilla/foonet:changeme"
+                                :full-name "tester")
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet"))
+      (erc-cmd-MSG "*status playbuffer #chan"))
+
+    (ert-info ("Playback appears in buffer #chan")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "Was I a child")
+        (funcall expect 10 "Thou counterfeit'st most lively")
+        (funcall expect 10 "Playback Complete")))
+
+    (with-current-buffer "foonet"
+      (erc-cmd-MSG "*status attach #chan"))
+
+    (ert-info ("Live output from #chan after more playback")
+      (with-current-buffer "#chan"
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "With what it loathes")
+        (funcall expect 10 "Not by his breath")
+        (funcall expect 10 "Playback Complete")
+        (funcall expect 10 "Ay, and the captain")
+        (erc-scenarios-common-say "bob: hi")
+        (funcall expect 10 "Pawn me to this")))))
+
+(ert-deftest erc-scenarios-base-attach--query-on-unjoined-disabled ()
+  :tags '(:expensive-test)
+  (should erc-query-on-unjoined-chan-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/channel-buffer-revival")
+       (dumb-server (erc-d-run "localhost" t 'reattach))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (erc-query-on-unjoined-chan-privmsg nil) ; off
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "tester@vanilla/foonet:changeme"
+                                :full-name "tester")
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet"))
+      (erc-cmd-MSG "*status playbuffer #chan")
+      (ert-info ("Playback appears in buffer server buffer")
+        (erc-d-t-ensure-for -1 (not (get-buffer "#chan")))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "Was I a child")
+        (funcall expect 10 "Thou counterfeit'st most lively")
+        (funcall expect 10 "Playback Complete"))
+      (should-not (get-buffer "#chan"))
+      (erc-cmd-MSG "*status attach #chan"))
+
+    (ert-info ("Buffer #chan joined")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "With what it loathes")
+        (funcall expect 10 "Not by his breath")
+        (funcall expect 10 "Playback Complete")
+        (funcall expect 10 "Ay, and the captain")
+        (erc-scenarios-common-say "bob: hi")
+        (funcall expect 10 "Pawn me to this")))))
+
+;;; erc-scenarios-base-attach.el ends here
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index b516b885895..88b9babf206 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1292,6 +1292,7 @@ erc-process-input-line
       (cl-letf (((symbol-function 'erc-cmd-MSG)
                  (lambda (line)
                    (push line calls)
+                   (should erc--called-as-input-p)
                    (funcall orig-erc-cmd-MSG line)))
                 ((symbol-function 'erc-server-buffer)
                  (lambda () (current-buffer)))
diff --git a/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld
new file mode 100644
index 00000000000..c3791ac3d49
--- /dev/null
+++ b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld
@@ -0,0 +1,56 @@
+;; -*- mode: lisp-data; -*-
+((pass 10 "PASS :tester@vanilla/foonet:changeme"))
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
+ (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0.00 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1")
+ (0.00 ":irc.foonet.org 003 tester :This server was created Thu, 13 Apr 2023 05:55:22 UTC")
+ (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server")
+ (0.00 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server")
+ (0.00 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server")
+ (0.00 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0.00 ":irc.foonet.org 254 tester 1 :channels formed")
+ (0.00 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers")
+ (0.00 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0.00 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0.00 ":irc.foonet.org 422 tester :MOTD File is missing"))
+
+((mode 10 "MODE tester +i")
+ (0.01 ":irc.foonet.org 221 tester +Zi"))
+
+((privmsg-play 10 "PRIVMSG *status :playbuffer #chan")
+ (0.05 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:24] alice: Was I a child, to fear I know not what.")
+ (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:29] bob: My lord, I do confess the ring was hers.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:40] alice: My sons would never so dishonour me.")
+ (0.01 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:54] bob: By the hand of a soldier, I will undertake it.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:57] alice: Thou counterfeit'st most lively.")
+ (0.01 ":***!znc@znc.in PRIVMSG #chan :Playback Complete."))
+
+((privmsg-attach 10 "PRIVMSG *status :attach #chan")
+ (0.01 ":tester!~u@78a58pgahbr24.irc JOIN #chan"))
+
+((mode-chan 10 "MODE #chan")
+ (0.01 ":irc.foonet.org 353 tester = #chan :@alice bob tester")
+ (0.00 ":irc.foonet.org 366 tester #chan :End of /NAMES list.")
+ (0.00 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:01] bob: With what it loathes for that which is away.")
+ (0.00 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:30] alice: Ties up my tongue, and will not let me speak.")
+ (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:26] bob: They say he is already in the forest of Arden, and a many merry men with him; and there they live like the old Robin Hood of England. They say many young gentlemen flock to him every day, and fleet the time carelessly, as they did in the golden world.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:29] alice: Not by his breath that is more miserable.")
+ (0.00 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")
+ (0.00 ":*status!znc@znc.in PRIVMSG tester :There was 1 channel matching [#chan]")
+ (0.03 ":*status!znc@znc.in PRIVMSG tester :Attached 1 channel")
+ (0.00 ":irc.foonet.org 324 tester #chan +Cnt")
+ (0.00 ":irc.foonet.org 329 tester #chan 1681365340")
+ (0.03 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Five or six thousand horse, I said,I will say true,or thereabouts, set down, for I'll speak truth.")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Riddling confession finds but riddling shrift.")
+ (0.04 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Ay, and the captain of his horse, Count Rousillon."))
+
+((privmsg-bob 10 "PRIVMSG #chan :bob: hi")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: But thankful even for hate, that is meant love.")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :tester: Come, come, elder brother, you are too young in this.")
+ (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Sir, we have known together in Orleans.")
+ (0.05 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Pawn me to this your honour, she is his."))
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-5.6-Revive-option-erc-query-on-unjoined-chan-privmsg.patch --]
[-- Type: text/x-patch, Size: 16063 bytes --]

From f4da70c6e5d757aecb8d14894951ddf26409fb24 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Thu, 13 Apr 2023 00:00:02 -0700
Subject: [PATCH 1/2] [5.6] Revive option erc-query-on-unjoined-chan-privmsg

* etc/ERC-NEWS: Mention reinstated option
`erc-query-on-unjoined-chan-privmsg'.
* lisp/erc/erc-backend.el (erc-server-PRIVMSG): Only "open" a buffer
for an unknown target when `erc-query-on-unjoined-chan-privmsg' is t.
* lisp/erc/erc.el (erc-query-on-unjoined-chan-privmsg): Revise doc
string.
* test/lisp/erc/erc-scenarios-base-attach.el: New file.
* test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld:
New file.
---
 etc/ERC-NEWS                                  |   7 +
 lisp/erc/erc-backend.el                       |   4 +-
 lisp/erc/erc.el                               |  20 ++-
 test/lisp/erc/erc-scenarios-base-attach.el    | 147 ++++++++++++++++++
 .../base/channel-buffer-revival/reattach.eld  |  56 +++++++
 5 files changed, 228 insertions(+), 6 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-attach.el
 create mode 100644 test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 8f1b89f268b..31f40e9d0d3 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -108,6 +108,13 @@ other than the symbol 'erc-button-buttonize-nicks' appearing in the
 "FORM" field (third element) of this entry are considered deprecated
 and will incur a warning.
 
+** The option erc-query-on-unjoined-chan-privmsg has been restored.
+In ERC 5.5, this option was removed from the default client code and
+thus prevented from influencing PRIVMSG handling because its precise
+purpose could not be determined with any confidence.  After some
+consideration, it's now been wired back in with a slightly revised
+role contingent on a few assumptions explained in its doc string.
+
 ** Miscellaneous UX changes.
 Some minor quality-of-life niceties have finally made their way to
 ERC.  For example, the function 'erc-echo-timestamp' is now
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index bdf4e2ddca2..5d44f478719 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -123,6 +123,7 @@ erc-nick
 (defvar erc-nick-change-attempt-count)
 (defvar erc-prompt-for-channel-key)
 (defvar erc-prompt-hidden)
+(defvar erc-query-on-unjoined-chan-privmsg)
 (defvar erc-reuse-buffers)
 (defvar erc-verbose-server-ping)
 (defvar erc-whowas-on-nosuchnick)
@@ -1835,7 +1836,8 @@ define-erc-response-handler
                 (let ((erc-join-buffer erc-auto-query))
                   (setq buffer (erc--open-target nick))))
             ;; A channel buffer has been killed but is still joined
-            (setq buffer (erc--open-target tgt))))
+            (when erc-query-on-unjoined-chan-privmsg
+              (setq buffer (erc--open-target tgt)))))
         (when buffer
           (with-current-buffer buffer
             (when privp (erc--unhide-prompt))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 284990e2d43..b38ef38e9f7 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -4746,15 +4746,25 @@ erc-auto-query
                  (const :tag "Use current buffer" buffer)
                  (const :tag "Use current buffer" t)))
 
-;; FIXME either retire this or put it to use after determining how
-;; it's meant to work.  Clearly, the doc string does not describe
-;; current behavior.  It's currently only used by the obsolete
-;; function `erc-auto-query'.
 (defcustom erc-query-on-unjoined-chan-privmsg t
   "If non-nil create query buffer on receiving any PRIVMSG at all.
 This includes PRIVMSGs directed to channels.  If you are using an IRC
 bouncer, such as dircproxy, to keep a log of channels when you are
-disconnected, you should set this option to t."
+disconnected, you should set this option to t.
+
+Note that this option was absent from ERC 5.5 because knowledge
+of its intended role was \"unavailable\" during a major
+refactoring involving buffer management.  The option has since
+been restored in ERC 5.6 with the caveat that its purpose is
+assumed to more or less reflect the following, which can be
+thought of as an updated version of the paragraph above:
+
+If non-nil, create a channel buffer on receiving a PRIVMSG if
+none exists.  This only affects messages targeting a channel
+whose buffer was killed but to which you're still joined or one
+whose buffer never existed, presumably because a bouncer is
+sending playback summoned with a command similar to ZNC's
+PLAYBUFFER."
   :group 'erc-query
   :type 'boolean)
 
diff --git a/test/lisp/erc/erc-scenarios-base-attach.el b/test/lisp/erc/erc-scenarios-base-attach.el
new file mode 100644
index 00000000000..91763842879
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-base-attach.el
@@ -0,0 +1,147 @@
+;;; erc-scenarios-base-attach.el --- Reattach scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See also: `erc-scenarios-base-channel-buffer-revival'.
+;;
+;; ERC 5.5 silently dropped support for the ancient option
+;; `erc-query-on-unjoined-chan-privmsg' because the tangled logic in
+;; and around the function `erc-auto-query' made it difficult to
+;; divine its purpose.
+;;
+;; Based on the name, it was thought this option likely involved
+;; controlling the creation of query buffers for unsolicited messages
+;; from users with whom you don't share a common channel.  However,
+;; additional spelunking has recently revealed that it was instead
+;; meant to service a feature offered by most bouncers that sends
+;; PRIVMSGs directed at a channel you're no longer in and that you
+;; haven't received a(nother) JOIN message for.  IOW, this is meant to
+;; support the following sequence of events:
+;;
+;;   1. /detach #chan
+;;   2. kill buffer #chan or reconnect in new Emacs session
+;;   3. /playbuffer #chan
+;;
+;; Note that the above slash commands are bouncer-specific aliases.
+;;
+;; Interested users can find more info by looking at this change set
+;; from the ancient CVS repo:
+;;
+;;   Author:     Mario Lang <mlang@delysid.org>
+;;   AuthorDate: Mon Nov 26 18:33:19 2001 +0000
+;;
+;;   * new function erc-BBDB-NICK to handle nickname anotation ...
+;;   * Applied antifuchs/mhp patches, the latest on erc-help, unmodified
+;;   * New variable: erc-reuse-buffers default to t.
+;;   * Modified erc-generate-new-buffer-name to use it. it checks if
+;;     server and port are the same, then one can assume thats the same
+;;     channel/query target again.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(ert-deftest erc-scenarios-base-attach--query-on-unjoined-enabled ()
+  :tags '(:expensive-test)
+  (should erc-query-on-unjoined-chan-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/channel-buffer-revival")
+       (dumb-server (erc-d-run "localhost" t 'reattach))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "tester@vanilla/foonet:changeme"
+                                :full-name "tester")
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet"))
+      (erc-cmd-MSG "*status playbuffer #chan"))
+
+    (ert-info ("Playback appears in buffer #chan")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "Was I a child")
+        (funcall expect 10 "Thou counterfeit'st most lively")
+        (funcall expect 10 "Playback Complete")))
+
+    (with-current-buffer "foonet"
+      (erc-cmd-MSG "*status attach #chan"))
+
+    (ert-info ("Live output from #chan after more playback")
+      (with-current-buffer "#chan"
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "With what it loathes")
+        (funcall expect 10 "Not by his breath")
+        (funcall expect 10 "Playback Complete")
+        (funcall expect 10 "Ay, and the captain")
+        (erc-scenarios-common-say "bob: hi")
+        (funcall expect 10 "Pawn me to this")))))
+
+(ert-deftest erc-scenarios-base-attach--query-on-unjoined-disabled ()
+  :tags '(:expensive-test)
+  (should erc-query-on-unjoined-chan-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/channel-buffer-revival")
+       (dumb-server (erc-d-run "localhost" t 'reattach))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (erc-query-on-unjoined-chan-privmsg nil) ; off
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "tester@vanilla/foonet:changeme"
+                                :full-name "tester")
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet"))
+      (erc-cmd-MSG "*status playbuffer #chan")
+      (ert-info ("Playback appears in buffer server buffer")
+        (erc-d-t-ensure-for -1 (not (get-buffer "#chan")))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "Was I a child")
+        (funcall expect 10 "Thou counterfeit'st most lively")
+        (funcall expect 10 "Playback Complete"))
+      (should-not (get-buffer "#chan"))
+      (erc-cmd-MSG "*status attach #chan"))
+
+    (ert-info ("Buffer #chan joined")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "With what it loathes")
+        (funcall expect 10 "Not by his breath")
+        (funcall expect 10 "Playback Complete")
+        (funcall expect 10 "Ay, and the captain")
+        (erc-scenarios-common-say "bob: hi")
+        (funcall expect 10 "Pawn me to this")))))
+
+;;; erc-scenarios-base-attach.el ends here
diff --git a/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld
new file mode 100644
index 00000000000..c3791ac3d49
--- /dev/null
+++ b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld
@@ -0,0 +1,56 @@
+;; -*- mode: lisp-data; -*-
+((pass 10 "PASS :tester@vanilla/foonet:changeme"))
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
+ (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0.00 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1")
+ (0.00 ":irc.foonet.org 003 tester :This server was created Thu, 13 Apr 2023 05:55:22 UTC")
+ (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server")
+ (0.00 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server")
+ (0.00 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server")
+ (0.00 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0.00 ":irc.foonet.org 254 tester 1 :channels formed")
+ (0.00 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers")
+ (0.00 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0.00 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0.00 ":irc.foonet.org 422 tester :MOTD File is missing"))
+
+((mode 10 "MODE tester +i")
+ (0.01 ":irc.foonet.org 221 tester +Zi"))
+
+((privmsg-play 10 "PRIVMSG *status :playbuffer #chan")
+ (0.05 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:24] alice: Was I a child, to fear I know not what.")
+ (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:29] bob: My lord, I do confess the ring was hers.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:40] alice: My sons would never so dishonour me.")
+ (0.01 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:54] bob: By the hand of a soldier, I will undertake it.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:57] alice: Thou counterfeit'st most lively.")
+ (0.01 ":***!znc@znc.in PRIVMSG #chan :Playback Complete."))
+
+((privmsg-attach 10 "PRIVMSG *status :attach #chan")
+ (0.01 ":tester!~u@78a58pgahbr24.irc JOIN #chan"))
+
+((mode-chan 10 "MODE #chan")
+ (0.01 ":irc.foonet.org 353 tester = #chan :@alice bob tester")
+ (0.00 ":irc.foonet.org 366 tester #chan :End of /NAMES list.")
+ (0.00 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:01] bob: With what it loathes for that which is away.")
+ (0.00 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:30] alice: Ties up my tongue, and will not let me speak.")
+ (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:26] bob: They say he is already in the forest of Arden, and a many merry men with him; and there they live like the old Robin Hood of England. They say many young gentlemen flock to him every day, and fleet the time carelessly, as they did in the golden world.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:29] alice: Not by his breath that is more miserable.")
+ (0.00 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")
+ (0.00 ":*status!znc@znc.in PRIVMSG tester :There was 1 channel matching [#chan]")
+ (0.03 ":*status!znc@znc.in PRIVMSG tester :Attached 1 channel")
+ (0.00 ":irc.foonet.org 324 tester #chan +Cnt")
+ (0.00 ":irc.foonet.org 329 tester #chan 1681365340")
+ (0.03 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Five or six thousand horse, I said,I will say true,or thereabouts, set down, for I'll speak truth.")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Riddling confession finds but riddling shrift.")
+ (0.04 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Ay, and the captain of his horse, Count Rousillon."))
+
+((privmsg-bob 10 "PRIVMSG #chan :bob: hi")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: But thankful even for hate, that is meant love.")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :tester: Come, come, elder brother, you are too young in this.")
+ (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Sir, we have known together in Orleans.")
+ (0.05 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Pawn me to this your honour, she is his."))
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0002-5.6-Extend-erc-interactive-display-to-cover-JOINs.patch --]
[-- Type: text/x-patch, Size: 22642 bytes --]

From 6b7f66a1eed5d01c312d125f016e48cf4e8ce0c9 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 10 Apr 2023 17:58:05 -0700
Subject: [PATCH 2/2] [5.6] Extend erc-interactive-display to cover /JOINs

* lisp/erc/erc.el (erc-buffer-display, erc-join-buffer): Swap alias
and aliased so that the favored name, `erc-buffer-display', appears in
the definition and in the Customize menu.
(erc-query-display, erc-interactive-display): Make the former an alias
for the latter because their roles were functionally redundant and
thus confusing.  Inherit the default value from `erc-query-display'
because users are more familiar with the pop-up window behavior than a
single-window replacement.
(erc--called-as-input-p): New variable for "slash" commands, like
`erc-cmd-FOO', to detect whether they're being called "interactively"
as a result of input given at ERC's prompt.
(erc-process-input-line): Bind `erc--called-as-input-p' when running
slash commands.
(erc-cmd-JOIN): When called interactively, schedule a callback to wrap
the response handler and control how new buffers are thus displayed.
* test/lisp/erc/erc-scenarios-base-buffer-display.el: New file.
* test/lisp/erc/erc-scenarios-base-reconnect.el
(erc-scenarios-common--base-reconnect-options,
ert-deftest erc-scenarios-base-reconnect-options--buffer,
erc-scenarios-base-reconnect-options--default): Move to new file
erc-scenarios-base-buffer-display.el and rename.
* test/lisp/erc/erc-tests.el (erc-process-input-line,
erc-select-read-args, erc-tls, erc--interactive): Change expected
default value of `erc-interactive-display' from `buffer' to
`window'.  (Bug#60428.)
---
 etc/ERC-NEWS                                  |  17 +-
 lisp/erc/erc.el                               |  61 ++++---
 .../erc/erc-scenarios-base-buffer-display.el  | 158 ++++++++++++++++++
 test/lisp/erc/erc-scenarios-base-reconnect.el |  89 ----------
 test/lisp/erc/erc-tests.el                    |  13 +-
 5 files changed, 211 insertions(+), 127 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-buffer-display.el

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 31f40e9d0d3..e9de48b2e34 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -37,15 +37,18 @@ decade overdue, this is no longer the case.  Other UX improvements in
 this area aim to make the process of connecting interactively slightly
 more streamlined and less repetitive, even for veteran users.
 
-** New buffer-display option 'erc-interactive-display'.
+** Revised buffer-display handling for interactive commands.
 A point of friction for new users and one only just introduced with
 ERC 5.5 has been the lack of visual feedback when first connecting via
-M-x erc.  As explained below in the news for 5.5, the discovery of a
-security issue led to new ERC buffers being "buried" on creation.  On
-further reflection, this was judged to have been an overcorrection in
-the case of interactive invocations, hence the new option
-'erc-interactive-display', which is set to 'buffer' (i.e., "take me
-there") by default.  Accompanying this addition are "display"-suffixed
+M-x erc or when issuing a "/JOIN" command at the prompt.  As explained
+below, in the news for 5.5, the discovery of a security issue led to
+most new ERC buffers being "buried" on creation.  On further
+reflection, this was judged to have been an overcorrection in the case
+of interactive invocations, hence the borrowing of an old option,
+'erc-query-display', and the bestowing of a new alias,
+'erc-interactive-display', which better describes its expanded role as
+a more general buffer-display knob for interactive commands ("/QUERY"
+still among them).  Accompanying this addition are "display"-suffixed
 aliases for related options 'erc-join-buffer' and 'erc-auto-query',
 which users have reported as being difficult to discover and remember.
 
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index b38ef38e9f7..2088a755da5 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1507,8 +1507,8 @@ erc-default-port-tls
   "IRC port to use for encrypted connections if it cannot be \
 detected otherwise.")
 
-(defvaralias 'erc-buffer-display 'erc-join-buffer)
-(defcustom erc-join-buffer 'bury
+(defvaralias 'erc-join-buffer 'erc-buffer-display)
+(defcustom erc-buffer-display 'bury
   "Determines how to display a newly created IRC buffer.
 
 The available choices are:
@@ -1528,13 +1528,21 @@ erc-join-buffer
                  (const :tag "Use current buffer" buffer)
                  (const :tag "Use current buffer" t)))
 
-(defcustom erc-interactive-display 'buffer
-  "How and whether to display server buffers for M-x erc.
-See `erc-buffer-display' and friends for a description of
-possible values."
+(defvaralias 'erc-query-display 'erc-interactive-display)
+(defcustom erc-interactive-display 'window
+  "How to display buffers created from user interaction.
+Examples include the \"slash\" commands /QUERY and /JOIN.  See
+`erc-buffer-display' for a full description of available choices.
+
+Note that this doesn't affect commands like `erc-cmd-JOIN' when
+called from lisp code.  Formerly known as `erc-query-display',
+this option now applies to more than just /QUERY.  See option
+`erc-receive-display' in the same Custom group to decide how
+buffers created as a result of server-initiated activity should
+be displayed."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-join-buffer'" nil)
+  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
@@ -3057,6 +3065,10 @@ erc-message-type-member
   (let ((prop-val (erc-get-parsed-vector position)))
     (and prop-val (member (erc-response.command prop-val) list))))
 
+(defvar erc--called-as-input-p nil
+  "Non-nil when a user types a \"/slash\" command.
+Remains bound until `erc-cmd-SLASH' returns.")
+
 (defvar-local erc-send-input-line-function 'erc-send-input-line
   "Function for sending lines lacking a leading user command.
 When a line typed into a buffer contains an explicit command, like /msg,
@@ -3110,7 +3122,8 @@ erc-process-input-line
     (if (and command-list
              (not no-command))
         (let* ((cmd  (nth 0 command-list))
-               (args (nth 1 command-list)))
+               (args (nth 1 command-list))
+               (erc--called-as-input-p t))
           (condition-case nil
               (if (listp args)
                   (apply cmd args)
@@ -3584,6 +3597,21 @@ erc-cmd-JOIN
                    (erc-get-channel-user (erc-current-nick)))))
           (switch-to-buffer existing)
         (setq erc--server-last-reconnect-count 0)
+        (when-let* ; bind `erc-join-buffer' when /JOIN issued
+            ((erc--called-as-input-p)
+             (fn (lambda (proc parsed)
+                   (when-let* ; `fn' wrapper already removed from hook
+                       (((equal (car (erc-response.command-args parsed))
+                                channel))
+                        (sn (erc-extract-nick (erc-response.sender parsed)))
+                        ((erc-nick-equal-p sn (erc-current-nick)))
+                        (erc-join-buffer (or erc-interactive-display
+                                             erc-join-buffer)))
+                     (run-hook-with-args-until-success
+                      'erc-server-JOIN-functions proc parsed)
+                     t))))
+          (erc-with-server-buffer
+            (erc-once-with-server-event "JOIN" fn)))
         (erc-server-join-channel nil chnl key))))
   t)
 
@@ -3947,23 +3975,6 @@ erc-cmd-QUOTE
    (t nil)))
 (put 'erc-cmd-QUOTE 'do-not-parse-args t)
 
-(defcustom erc-query-display 'window
-  "How to display query buffers when using the /QUERY command to talk to someone.
-
-The default behavior is to display the message in a new window
-and bring it to the front.  See the documentation for
-`erc-join-buffer' for a description of the available choices.
-
-See also `erc-auto-query' to decide how private messages from
-other people should be displayed."
-  :group 'erc-query
-  :type '(choice (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
-
 (defun erc-cmd-QUERY (&optional user)
   "Open a query with USER.
 How the query is displayed (in a new window, frame, etc.) depends
diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
new file mode 100644
index 00000000000..3ed7a83653e
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -0,0 +1,158 @@
+;;; erc-scenarios-base-buffer-display.el --- Buffer display scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(eval-when-compile (require 'erc-join))
+
+;; These first couple `erc-reconnect-display' tests used to live in
+;; erc-scenarios-base-reconnect but have since been renamed.
+
+(defun erc-scenarios-base-buffer-display--reconnect-common (test)
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/reconnect")
+       (dumb-server (erc-d-run "localhost" t 'options 'options-again))
+       (port (process-contact dumb-server :service))
+       (expect (erc-d-t-make-expecter))
+       (erc-server-flood-penalty 0.1)
+       (erc-server-auto-reconnect t)
+       erc-autojoin-channels-alist
+       erc-server-buffer)
+
+    (should (memq 'autojoin erc-modules))
+
+    (ert-info ("Connect to foonet")
+      (setq erc-server-buffer (erc :server "127.0.0.1"
+                                   :port port
+                                   :nick "tester"
+                                   :password "changeme"
+                                   :full-name "tester"))
+      (with-current-buffer erc-server-buffer
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
+        (funcall expect 10 "debug mode")))
+
+    (ert-info ("Wait for some output in channels")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "welcome")))
+
+    (ert-info ("Server buffer shows connection failed")
+      (with-current-buffer erc-server-buffer
+        (funcall expect 10 "Connection failed!  Re-establishing")))
+
+    (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
+
+    (funcall test)
+
+    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
+    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
+
+    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
+      (not (eq (window-buffer) (get-buffer "#spam"))))
+
+    (ert-info ("Wait for auto reconnect")
+      (with-current-buffer erc-server-buffer
+        (funcall expect 10 "still in debug mode")))
+
+    (ert-info ("Wait for activity to recommence in channels")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "forest of Arden"))
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+        (funcall expect 10 "her elves come here anon")))))
+
+(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should-not erc-reconnect-display)
+
+  ;; FooNet (the server buffer) is not switched to because it's
+  ;; already current (but not shown) when `erc-open' is called.  See
+  ;; related conditional guard towards the end of that function.
+
+  (let ((erc-reconnect-display 'buffer))
+    (erc-scenarios-base-buffer-display--reconnect-common
+     (lambda ()
+       (pop-to-buffer-same-window "*Messages*")
+
+       (erc-d-t-ensure-for 1 "Server buffer not shown"
+         (not (eq (window-buffer) (get-buffer "FooNet"))))
+
+       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
+         (eq (window-buffer) (get-buffer "#chan")))))))
+
+(ert-deftest erc-scenarios-base-reconnect-options--default ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should-not erc-reconnect-display)
+
+  (erc-scenarios-base-buffer-display--reconnect-common
+
+   (lambda ()
+     (pop-to-buffer-same-window "*Messages*")
+
+     (erc-d-t-ensure-for 1 "Server buffer not shown"
+       (not (eq (window-buffer) (get-buffer "FooNet"))))
+
+     (erc-d-t-ensure-for 3 "Channel #chan not shown"
+       (not (eq (window-buffer) (get-buffer "#chan"))))
+
+     (should (eq (window-buffer) (messages-buffer))))))
+
+
+;; This shows that the option `erc-interactive-display' overrides
+;; `erc-join-buffer' during cold opens and interactive /JOINs.
+
+(ert-deftest erc-scenarios-base-buffer-display--interactive-default ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should (eq erc-interactive-display 'window))
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "join/legacy")
+       (dumb-server (erc-d-run "localhost" t 'foonet))
+       (port (process-contact dumb-server :service))
+       (url (format "tester:changeme@127.0.0.1:%d\r\r" port))
+       (expect (erc-d-t-make-expecter))
+       (erc-server-flood-penalty 0.1)
+       (erc-server-auto-reconnect t)
+       (erc-user-full-name "tester"))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (let (inhibit-interaction)
+                             (ert-simulate-keys url
+                               (call-interactively #'erc)))
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
+
+        (erc-d-t-wait-for 10 "Server buffer shown"
+          (eq (window-buffer) (current-buffer)))
+        (funcall expect 10 "debug mode")
+        (erc-scenarios-common-say "/JOIN #chan")))
+
+    (ert-info ("Wait for output in #chan")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "welcome")
+        (erc-d-t-ensure-for 3 "Channel #chan shown"
+          (eq (window-buffer) (current-buffer)))
+        (funcall expect 10 "be prosperous")))))
+
+;;; erc-scenarios-base-buffer-display.el ends here
diff --git a/test/lisp/erc/erc-scenarios-base-reconnect.el b/test/lisp/erc/erc-scenarios-base-reconnect.el
index 5b4dc549042..7bd16d1ed14 100644
--- a/test/lisp/erc/erc-scenarios-base-reconnect.el
+++ b/test/lisp/erc/erc-scenarios-base-reconnect.el
@@ -65,95 +65,6 @@ erc-scenarios-base-reconnect-timer
       (should (equal (list (get-buffer (format "127.0.0.1:%d" port)))
                      (erc-scenarios-common-buflist "127.0.0.1"))))))
 
-(defun erc-scenarios-common--base-reconnect-options (test)
-  (erc-scenarios-common-with-cleanup
-      ((erc-scenarios-common-dialog "base/reconnect")
-       (dumb-server (erc-d-run "localhost" t 'options 'options-again))
-       (port (process-contact dumb-server :service))
-       (expect (erc-d-t-make-expecter))
-       (erc-server-flood-penalty 0.1)
-       (erc-server-auto-reconnect t)
-       erc-autojoin-channels-alist
-       erc-server-buffer)
-
-    (should (memq 'autojoin erc-modules))
-
-    (ert-info ("Connect to foonet")
-      (setq erc-server-buffer (erc :server "127.0.0.1"
-                                   :port port
-                                   :nick "tester"
-                                   :password "changeme"
-                                   :full-name "tester"))
-      (with-current-buffer erc-server-buffer
-        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
-        (funcall expect 10 "debug mode")))
-
-    (ert-info ("Wait for some output in channels")
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
-        (funcall expect 10 "welcome")))
-
-    (ert-info ("Server buffer shows connection failed")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "Connection failed!  Re-establishing")))
-
-    (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
-
-    (funcall test)
-
-    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
-    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
-
-    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
-      (not (eq (window-buffer) (get-buffer "#spam"))))
-
-    (ert-info ("Wait for auto reconnect")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "still in debug mode")))
-
-    (ert-info ("Wait for activity to recommence in channels")
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
-        (funcall expect 10 "forest of Arden"))
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
-        (funcall expect 10 "her elves come here anon")))))
-
-(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
-
-  ;; FooNet (the server buffer) is not switched to because it's
-  ;; already current (but not shown) when `erc-open' is called.  See
-  ;; related conditional guard towards the end of that function.
-
-  (let ((erc-reconnect-display 'buffer))
-    (erc-scenarios-common--base-reconnect-options
-     (lambda ()
-       (pop-to-buffer-same-window "*Messages*")
-
-       (erc-d-t-ensure-for 1 "Server buffer not shown"
-         (not (eq (window-buffer) (get-buffer "FooNet"))))
-
-       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
-         (eq (window-buffer) (get-buffer "#chan")))))))
-
-(ert-deftest erc-scenarios-base-reconnect-options--default ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
-
-  (erc-scenarios-common--base-reconnect-options
-
-   (lambda ()
-     (pop-to-buffer-same-window "*Messages*")
-
-     (erc-d-t-ensure-for 1 "Server buffer not shown"
-       (not (eq (window-buffer) (get-buffer "FooNet"))))
-
-     (erc-d-t-ensure-for 3 "Channel #chan not shown"
-       (not (eq (window-buffer) (get-buffer "#chan"))))
-
-     (eq (window-buffer) (messages-buffer)))))
-
 ;; Upon reconnecting, playback for channel and target buffers is
 ;; routed correctly.  Autojoin is irrelevant here, but for the
 ;; skeptical, see `erc-scenarios-common--join-network-id', which
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 29bda7e742d..88b9babf206 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1292,6 +1292,7 @@ erc-process-input-line
       (cl-letf (((symbol-function 'erc-cmd-MSG)
                  (lambda (line)
                    (push line calls)
+                   (should erc--called-as-input-p)
                    (funcall orig-erc-cmd-MSG line)))
                 ((symbol-function 'erc-server-buffer)
                  (lambda () (current-buffer)))
@@ -1469,7 +1470,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when port matches default TLS port")
     (should (equal (ert-simulate-keys "irc.gnu.org\r6697\r\r\r"
@@ -1479,7 +1480,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when URL is ircs://")
     (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
@@ -1489,7 +1490,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (setq-local erc-interactive-display nil) ; cheat to save space
 
@@ -1625,7 +1626,7 @@ erc-tls
                        '("localhost" 6667 "nick" "unknown" t "sesame"
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer)
+                       '((erc-join-buffer window)
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Custom connect function")
@@ -1686,7 +1687,7 @@ erc--interactive
                        '("irc.libera.chat" 6697 "tester" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer) (erc-server-connect-function
+                       '((erc-join-buffer window) (erc-server-connect-function
                                                    erc-open-tls-stream)))))
 
       (ert-info ("Nick supplied, decline TLS upgrade")
@@ -1696,7 +1697,7 @@ erc--interactive
                        '("irc.libera.chat" 6667 "dummy" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer)
+                       '((erc-join-buffer window)
                          (erc-server-connect-function
                           erc-open-network-stream))))))))
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] <87leiuy3cv.fsf@neverwas.me>
@ 2023-04-21 14:03 ` J.P.
       [not found] ` <87354tcoyk.fsf@neverwas.me>
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-04-21 14:03 UTC (permalink / raw)
  To: 62833; +Cc: emacs-erc

[-- Attachment #1: Type: text/plain, Size: 3998 bytes --]

"J.P." <jp@neverwas.me> writes:

> I'm hoping this can serve as a unified "omnibug" for all the overlapping
> discourse scattered about regarding buffer-display options and the
> behavior they produce. The main focus will be those aspects impacting
> ERC 5.6 and how they integrate with the upstream display-buffer facility
> provided by window.el. In a sense, this is a spiritual successor to
>
>   bug#51753: ERC switches to channel buffer on reconnect

A troubling discovery has come to light regarding the option
`erc-reconnect-display' (new in 5.5 and Emacs 29), which was the main
product of bug#51753 before it pivoted to that frame-isolation feature.
(This bug thread exists in part to move past that confusion.) The issue
here concerns the time interval during which `erc-reconnect-display'
takes precedence over its fellow buffer-display options, like
`erc-join-buffer'. As things stand, this interval only ends for a
session when `erc-cmd-JOIN' runs in the server buffer. Without that
specific intervention, the option remains in effect for the remainder of
the session.

For example, suppose (following a successful automatic reconnection) you
get back to chatting in a channel and receive a query from someone you
haven't spoken with yet. Instead of appealing to the rightful option, in
this case, `erc-auto-query', ERC instead displays the person's buffer
using `erc-reconnect-display'. The same goes for any JOIN initiated by a
/JOIN command issued in a channel buffer: `erc-join-buffer' should be
consulted but isn't. IMO, this constitutes an alarming enough problem to
warrant adding a warning to the option's doc string on the release
branch. It should say something to the effect of "bugged, do not use."

In the end, this all comes down to sheer sloppiness on my part. I had
intended to add the cancellation logic to `erc-process-input-line', but
for whatever reason (dimness of wit being the likeliest culprit),
neglected to do so and instead just stuck it in `erc-cmd-JOIN'.
(Shocking not shocking.) But even without that particular act of
bone-headedness, the problem would still be with us (albeit in a lesser
form). And while future request-tracking extensions will certainly help
for both autojoins and detecting unsolicited chathistory BATCHes, at
present, we simply can't tell when server-initiated playback ends (even
using heuristics, like comparing time stamps, because we don't even have
server-time).

IMO, as thing stand, the least bad (and only) "solution" is to use a
cancellation timer paired with a user option to designate a hard
timeout. See attached.

> A more daring and arguably more meaningful move would be to repurpose
> `erc-auto-query' (newly aliased to `erc-receive-query-display') as
> something like a more general `erc-receive-display', which could cover
> display handling for anything protocol driven (i.e.,
> "non-interactive").

Given the more pressing concerns noted above, I haven't yet devoted any
thought to this but promise to eventually.

> There's also the matter of assigning Custom groups for these options.
> It'd be "nice" if we could tag these with multiple groups rather than
> confine them to exclusive ownership. They're currently spread over
> `erc-buffers', `erc-query', and `erc-display'. All seem to have valid
> claims when you consider their respective constituencies.

Actually, that's slightly untrue: the `erc-display' group doesn't
include any buffer-display options. Regardless, for this iteration, I've
stuck with the current group assignments, which are `erc-buffers' for
all but `erc-receive-query-display', which lives in `erc-query'.

> It's also been casually suggested that we might consider deferring to
> `erc-setup-buffer' in areas not directly involved in message handling,
> such as in erc-sidebar, to allow the options in question to influence
> how buffers are displayed more generally. Not sure I have an opinion on
> this quite yet, but if anyone else does, please share.

This is also still to do.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v2-v3.diff --]
[-- Type: text/x-patch, Size: 20698 bytes --]

From c7ed508ef36fcb2f0c6731b588b953c77b2eb0db Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Fri, 21 Apr 2023 05:59:21 -0700
Subject: [PATCH 0/4] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (4):
  [5.6] Revive option erc-query-on-unjoined-chan-privmsg
  [5.6] Move ERC's buffer-display tests to separate file
  [5.6] Extend erc-interactive-display to cover /JOINs
  [5.6] Ignore erc-reconnect-display after a timeout

 etc/ERC-NEWS                                  |  24 +-
 lisp/erc/erc-backend.el                       |  26 +-
 lisp/erc/erc.el                               | 134 ++++++----
 test/lisp/erc/erc-scenarios-base-attach.el    | 147 +++++++++++
 .../erc/erc-scenarios-base-buffer-display.el  | 236 ++++++++++++++++++
 test/lisp/erc/erc-scenarios-base-reconnect.el |  89 -------
 test/lisp/erc/erc-tests.el                    |  13 +-
 .../base/channel-buffer-revival/reattach.eld  |  56 +++++
 8 files changed, 573 insertions(+), 152 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-attach.el
 create mode 100644 test/lisp/erc/erc-scenarios-base-buffer-display.el
 create mode 100644 test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld

Interdiff:
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 5d44f478719..c72c769f2e1 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -298,6 +298,12 @@ erc-server-connected
 (defvar-local erc-server-reconnect-count 0
   "Number of times we have failed to reconnect to the current server.")
 
+(defvar-local erc--server-reconnect-display-timer nil
+  "Timer that resets `erc--server-last-reconnect-count' to zero.
+Becomes non-nil in all server buffers when an IRC connection is
+first \"established\" and carries out its duties
+`erc-reconnect-display-timeout' seconds later.")
+
 (defvar-local erc--server-last-reconnect-count 0
   "Snapshot of reconnect count when the connection was established.")
 
@@ -902,6 +908,22 @@ erc-server-reconnect-p
         erc-server-reconnecting)
       (erc--server-reconnect-p event)))
 
+(defun erc--server-last-reconnect-on-disconnect (&rest _)
+  (remove-hook 'erc-disconnected-hook
+               #'erc--server-last-reconnect-on-disconnect t)
+  (erc--server-last-reconnect-display-reset (current-buffer)))
+
+(defun erc--server-last-reconnect-display-reset (buffer)
+  "Deactivate `erc-reconnect-display'."
+  (when (buffer-live-p buffer)
+    (with-current-buffer buffer
+      (when erc--server-reconnect-display-timer
+        (cancel-timer erc--server-reconnect-display-timer)
+        (remove-hook 'erc-disconnected-hook
+                     #'erc--server-last-reconnect-display-reset t)
+        (setq erc--server-reconnect-display-timer nil
+              erc--server-last-reconnect-count 0)))))
+
 (defconst erc--mode-line-process-reconnecting
   '(:eval (erc-with-server-buffer
             (and erc--server-reconnect-timer
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 2088a755da5..85b3d8bb650 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -98,7 +98,9 @@ erc-buffers
   :group 'erc)
 
 (defgroup erc-display nil
-  "Settings for how various things are displayed."
+  "Settings controlling how various things are displayed.
+See the customization group `erc-buffers' for display options
+concerning buffers."
   :group 'erc)
 
 (defgroup erc-mode-line-and-header nil
@@ -1509,7 +1511,7 @@ erc-default-port-tls
 
 (defvaralias 'erc-join-buffer 'erc-buffer-display)
 (defcustom erc-buffer-display 'bury
-  "Determines how to display a newly created IRC buffer.
+  "How to display a newly created or reassociated ERC buffer.
 
 The available choices are:
 
@@ -1518,28 +1520,26 @@ erc-buffer-display
   `frame'           - in another frame,
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
-  any other value  - in place of the current buffer."
+
+See related options `erc-interactive-display' and
+`erc-reconnect-display' as well as `erc-receive-query-display',
+which resides in the customization group `erc-query'."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type '(choice (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
                  (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+                 (const :tag "Use current buffer" buffer)))
 
 (defvaralias 'erc-query-display 'erc-interactive-display)
 (defcustom erc-interactive-display 'window
-  "How to display buffers created from user interaction.
-Examples include the \"slash\" commands /QUERY and /JOIN.  See
-`erc-buffer-display' for a full description of available choices.
-
-Note that this doesn't affect commands like `erc-cmd-JOIN' when
-called from lisp code.  Formerly known as `erc-query-display',
-this option now applies to more than just /QUERY.  See option
-`erc-receive-display' in the same Custom group to decide how
-buffers created as a result of server-initiated activity should
-be displayed."
+  "How to display buffers as a result of user interaction.
+This affects commands like /QUERY and /JOIN when issued
+interactively at the prompt.  It does not apply when calling a
+handler for such a command, like `erc-cmd-JOIN', from lisp code.
+See `erc-buffer-display' for a full description of available
+values."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
   :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
@@ -1550,12 +1550,11 @@ erc-interactive-display
                  (const :tag "Use current buffer" buffer)))
 
 (defcustom erc-reconnect-display nil
-  "How (and whether) to display a channel buffer upon reconnecting.
-
-This only affects automatic reconnections and is ignored when
-issuing a /reconnect command or reinvoking `erc-tls' with the
-same args (assuming success, of course).  See `erc-join-buffer'
-for a description of possible values."
+  "How and whether to display a channel buffer when auto-reconnecting.
+This only affects automatic reconnections and is ignored, like
+all other buffer-display options, when issuing a /RECONNECT or
+successfully reinvoking `erc-tls' with similar arguments.  See
+`erc-buffer-display' for a description of possible values."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type '(choice (const :tag "Use value of `erc-join-buffer'" nil)
@@ -1565,6 +1564,13 @@ erc-reconnect-display
                  (const :tag "Bury in new buffer" bury)
                  (const :tag "Use current buffer" buffer)))
 
+(defcustom erc-reconnect-display-timeout 10
+  "Duration `erc-reconnect-display' remains active.
+The countdown starts on MOTD and is canceled early by any
+\"slash\" command."
+  :type 'integer
+  :group 'erc-buffer)
+
 (defcustom erc-frame-alist nil
   "Alist of frame parameters for creating erc frames.
 A value of nil means to use `default-frame-alist'."
@@ -3124,6 +3130,7 @@ erc-process-input-line
         (let* ((cmd  (nth 0 command-list))
                (args (nth 1 command-list))
                (erc--called-as-input-p t))
+          (erc--server-last-reconnect-display-reset (erc-server-buffer))
           (condition-case nil
               (if (listp args)
                   (apply cmd args)
@@ -3596,7 +3603,6 @@ erc-cmd-JOIN
                 ((with-current-buffer existing
                    (erc-get-channel-user (erc-current-nick)))))
           (switch-to-buffer existing)
-        (setq erc--server-last-reconnect-count 0)
         (when-let* ; bind `erc-join-buffer' when /JOIN issued
             ((erc--called-as-input-p)
              (fn (lambda (proc parsed)
@@ -4733,29 +4739,32 @@ erc-query
   "Open a query buffer on TARGET using SERVER-BUFFER.
 To change how this query window is displayed, use `let' to bind
 `erc-join-buffer' before calling this."
-  (declare (obsolete "bind `erc-cmd-query' and call `erc-cmd-QUERY'" "29.1"))
+  (declare (obsolete "call `erc-open' in a live server buffer" "29.1"))
   (unless (buffer-live-p server-buffer)
     (error "Couldn't switch to server buffer"))
   (with-current-buffer server-buffer
     (erc--open-target target)))
 
-(defvaralias 'erc-receive-query-display 'erc-auto-query)
-(defcustom erc-auto-query 'window-noselect
-  "If non-nil, create a query buffer each time you receive a private message.
+(defvaralias 'erc-auto-query 'erc-receive-query-display)
+(defcustom erc-receive-query-display 'window-noselect
+  "How to display a query buffer when you receive a private message.
 If the buffer doesn't already exist, it is created.
 
 This can be set to a symbol, to control how the new query window
 should appear.  The default behavior is to display the buffer in
 a new window, but not to select it.  See the documentation for
-`erc-join-buffer' for a description of the available choices."
+`erc-buffer-display' for a description of available values.  Note
+that setting this option to nil forgoes buffer creation entirely.
+It does not mean \"fall back on `erc-buffer-display'\", like it
+does for buffer-display options, like `erc-interactive-display',
+that reside in the customization group `erc-buffers'."
   :group 'erc-query
   :type '(choice (const :tag "Don't create query window" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
                  (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+                 (const :tag "Use current buffer" buffer)))
 
 (defcustom erc-query-on-unjoined-chan-privmsg t
   "If non-nil create query buffer on receiving any PRIVMSG at all.
@@ -5182,6 +5191,12 @@ erc-connection-established
         (setq erc-server-connected t)
         (setq erc--server-last-reconnect-count erc-server-reconnect-count
               erc-server-reconnect-count 0)
+        (setq erc--server-reconnect-display-timer
+              (run-at-time erc-reconnect-display-timeout nil
+                           #'erc--server-last-reconnect-display-reset
+                           (current-buffer)))
+        (add-hook 'erc-disconnected-hook
+                  #'erc--server-last-reconnect-on-disconnect nil t)
         (erc-update-mode-line)
         (erc-set-initial-user-mode nick buffer)
         (erc-server-setup-periodical-ping buffer)
diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
index 3ed7a83653e..53a3d7e8ef7 100644
--- a/test/lisp/erc/erc-scenarios-base-buffer-display.el
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -29,7 +29,8 @@
 ;; These first couple `erc-reconnect-display' tests used to live in
 ;; erc-scenarios-base-reconnect but have since been renamed.
 
-(defun erc-scenarios-base-buffer-display--reconnect-common (test)
+(defun erc-scenarios-base-buffer-display--reconnect-common
+    (assert-server assert-chan assert-rest)
   (erc-scenarios-common-with-cleanup
       ((erc-scenarios-common-dialog "base/reconnect")
        (dumb-server (erc-d-run "localhost" t 'options 'options-again))
@@ -37,87 +38,164 @@ erc-scenarios-base-buffer-display--reconnect-common
        (expect (erc-d-t-make-expecter))
        (erc-server-flood-penalty 0.1)
        (erc-server-auto-reconnect t)
-       erc-autojoin-channels-alist
-       erc-server-buffer)
+       erc-autojoin-channels-alist)
 
     (should (memq 'autojoin erc-modules))
 
     (ert-info ("Connect to foonet")
-      (setq erc-server-buffer (erc :server "127.0.0.1"
-                                   :port port
-                                   :nick "tester"
-                                   :password "changeme"
-                                   :full-name "tester"))
-      (with-current-buffer erc-server-buffer
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "changeme"
+                                :full-name "tester")
+        (funcall assert-server expect)
         (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
         (funcall expect 10 "debug mode")))
 
     (ert-info ("Wait for some output in channels")
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall assert-chan expect)
         (funcall expect 10 "welcome")))
 
     (ert-info ("Server buffer shows connection failed")
-      (with-current-buffer erc-server-buffer
+      (with-current-buffer "FooNet"
         (funcall expect 10 "Connection failed!  Re-establishing")))
 
     (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
-
-    (funcall test)
-
-    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
-    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
-
-    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
-      (not (eq (window-buffer) (get-buffer "#spam"))))
+    (delete-other-windows)
+    (pop-to-buffer-same-window "*Messages*")
 
     (ert-info ("Wait for auto reconnect")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "still in debug mode")))
+      (with-current-buffer "FooNet" (funcall expect 10 "still in debug mode")))
 
-    (ert-info ("Wait for activity to recommence in channels")
+    (funcall assert-rest expect)
+
+    (ert-info ("Wait for activity to recommence in both channels")
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
         (funcall expect 10 "forest of Arden"))
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
         (funcall expect 10 "her elves come here anon")))))
 
-(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
+(ert-deftest erc-scenarios-base-buffer-display--defwin-recbury-intbuf ()
   :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
   (should-not erc-reconnect-display)
 
-  ;; FooNet (the server buffer) is not switched to because it's
-  ;; already current (but not shown) when `erc-open' is called.  See
-  ;; related conditional guard towards the end of that function.
+  (let ((erc-buffer-display 'window)
+        (erc-interactive-display 'buffer)
+        (erc-reconnect-display 'bury))
 
-  (let ((erc-reconnect-display 'buffer))
     (erc-scenarios-base-buffer-display--reconnect-common
-     (lambda ()
-       (pop-to-buffer-same-window "*Messages*")
 
-       (erc-d-t-ensure-for 1 "Server buffer not shown"
-         (not (eq (window-buffer) (get-buffer "FooNet"))))
+     (lambda (_)
+       (should (eq (window-buffer) (current-buffer)))
+       (should-not (frame-root-window-p (selected-window))))
 
-       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
-         (eq (window-buffer) (get-buffer "#chan")))))))
+     (lambda (_)
+       (should (eq (window-buffer) (current-buffer)))
+       (should (equal (get-buffer "FooNet") (window-buffer (next-window)))))
 
-(ert-deftest erc-scenarios-base-reconnect-options--default ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
 
-  (erc-scenarios-base-buffer-display--reconnect-common
+       ;; A manual /JOIN command tells ERC we're done auto-reconnecting
+       (with-current-buffer "FooNet" (erc-scenarios-common-say "/JOIN #spam"))
 
-   (lambda ()
-     (pop-to-buffer-same-window "*Messages*")
+       (ert-info ("#spam ignores `erc-reconnect-display'")
+         ;; Uses `erc-interactive-display' instead.
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (get-buffer "#spam")))
+           ;; Option `buffer' replaces entire window (no split)
+           (should (frame-root-window-p (selected-window)))))))))
 
-     (erc-d-t-ensure-for 1 "Server buffer not shown"
-       (not (eq (window-buffer) (get-buffer "FooNet"))))
+(ert-deftest erc-scenarios-base-buffer-display--defwino-recbury-intbuf ()
+  :tags '(:expensive-test)
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
+  (should-not erc-reconnect-display)
 
-     (erc-d-t-ensure-for 3 "Channel #chan not shown"
-       (not (eq (window-buffer) (get-buffer "#chan"))))
+  (let ((erc-buffer-display 'window-noselect)
+        (erc-reconnect-display 'bury)
+        (erc-interactive-display 'buffer))
+    (erc-scenarios-base-buffer-display--reconnect-common
 
-     (should (eq (window-buffer) (messages-buffer))))))
+     (lambda (_)
+       ;; Selected window shows some non-ERC buffer.  New server
+       ;; buffer appears in another window (other side of split).
+       (should-not (frame-root-window-p (selected-window)))
+       (should-not (eq (window-buffer) (current-buffer)))
+       (with-current-buffer (window-buffer)
+         (should-not (derived-mode-p 'erc-mode)))
+       (should (eq (current-buffer) (window-buffer (next-window)))))
+
+     (lambda (_)
+       (should-not (frame-root-window-p (selected-window)))
+       ;; Current split likely shows scratch.
+       (with-current-buffer (window-buffer)
+         (should-not (derived-mode-p 'erc-mode)))
+       (should (eq (current-buffer) (window-buffer (next-window)))))
+
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
+
+       ;; A non-interactive JOIN command doesn't signal that we're
+       ;; done auto-reconnecting, and `erc-interactive-display' is
+       ;; ignored, so `erc-buffer-display' is again in charge (here,
+       ;; that means `window-noselect').
+       (ert-info ("Join chan noninteractively and open a /QUERY")
+         (with-current-buffer "FooNet"
+           (erc-cmd-JOIN "#spam")
+           ;; However this will reset the option.
+           (erc-scenarios-common-say "/QUERY bob")
+           (should (eq (window-buffer) (get-buffer "bob")))
+           (should (frame-root-window-p (selected-window)))))
+
+       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (get-buffer "bob")))
+           (should-not (frame-root-window-p (selected-window)))
+           (should (eq (current-buffer) (window-buffer (next-window))))))))))
+
+(ert-deftest erc-scenarios-base-buffer-display--count-reset-timeout ()
+  :tags '(:expensive-test)
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
+  (should (eq erc-reconnect-display-timeout 10))
+  (should-not erc-reconnect-display)
 
+  (let ((erc-buffer-display 'window-noselect)
+        (erc-reconnect-display 'bury)
+        (erc-interactive-display 'buffer)
+        ;; Try changing this value to 1.  The last `ert-info' block
+        ;; should fail.
+        (erc-reconnect-display-timeout 0.1))
+    (erc-scenarios-base-buffer-display--reconnect-common
+     #'ignore #'ignore ; These two are identical to the previous test.
+
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
+
+       ;; A non-interactive JOIN command doesn't signal that we're
+       ;; done auto-reconnecting
+       (ert-info ("Join chan noninteractively")
+         (with-current-buffer "FooNet"
+           (sit-for 0.1)
+           (erc-cmd-JOIN "#spam")))
+
+       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (messages-buffer)))
+           ;; If `erc-reconnect-display-timeout' were left alone, this
+           ;; would be (frame-root-window-p #<window 1 on *scratch*>).
+           (should-not (frame-root-window-p (selected-window)))
+           (should (eq (current-buffer) (window-buffer (next-window))))))))))
 
 ;; This shows that the option `erc-interactive-display' overrides
 ;; `erc-join-buffer' during cold opens and interactive /JOINs.
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-5.6-Revive-option-erc-query-on-unjoined-chan-privmsg.patch --]
[-- Type: text/x-patch, Size: 16076 bytes --]

From b20fa22ce57aec074a0019c467a088cb4d5f0056 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Thu, 13 Apr 2023 00:00:02 -0700
Subject: [PATCH 1/4] [5.6] Revive option erc-query-on-unjoined-chan-privmsg

* etc/ERC-NEWS: Mention reinstated option
`erc-query-on-unjoined-chan-privmsg'.
* lisp/erc/erc-backend.el (erc-server-PRIVMSG): Only "open" a buffer
for an unknown target when `erc-query-on-unjoined-chan-privmsg' is t.
* lisp/erc/erc.el (erc-query-on-unjoined-chan-privmsg): Revise doc
string.
* test/lisp/erc/erc-scenarios-base-attach.el: New file.
* test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld:
New file.  (Bug#62833)
---
 etc/ERC-NEWS                                  |   7 +
 lisp/erc/erc-backend.el                       |   4 +-
 lisp/erc/erc.el                               |  20 ++-
 test/lisp/erc/erc-scenarios-base-attach.el    | 147 ++++++++++++++++++
 .../base/channel-buffer-revival/reattach.eld  |  56 +++++++
 5 files changed, 228 insertions(+), 6 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-attach.el
 create mode 100644 test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 8f1b89f268b..31f40e9d0d3 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -108,6 +108,13 @@ other than the symbol 'erc-button-buttonize-nicks' appearing in the
 "FORM" field (third element) of this entry are considered deprecated
 and will incur a warning.
 
+** The option erc-query-on-unjoined-chan-privmsg has been restored.
+In ERC 5.5, this option was removed from the default client code and
+thus prevented from influencing PRIVMSG handling because its precise
+purpose could not be determined with any confidence.  After some
+consideration, it's now been wired back in with a slightly revised
+role contingent on a few assumptions explained in its doc string.
+
 ** Miscellaneous UX changes.
 Some minor quality-of-life niceties have finally made their way to
 ERC.  For example, the function 'erc-echo-timestamp' is now
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index bdf4e2ddca2..5d44f478719 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -123,6 +123,7 @@ erc-nick
 (defvar erc-nick-change-attempt-count)
 (defvar erc-prompt-for-channel-key)
 (defvar erc-prompt-hidden)
+(defvar erc-query-on-unjoined-chan-privmsg)
 (defvar erc-reuse-buffers)
 (defvar erc-verbose-server-ping)
 (defvar erc-whowas-on-nosuchnick)
@@ -1835,7 +1836,8 @@ define-erc-response-handler
                 (let ((erc-join-buffer erc-auto-query))
                   (setq buffer (erc--open-target nick))))
             ;; A channel buffer has been killed but is still joined
-            (setq buffer (erc--open-target tgt))))
+            (when erc-query-on-unjoined-chan-privmsg
+              (setq buffer (erc--open-target tgt)))))
         (when buffer
           (with-current-buffer buffer
             (when privp (erc--unhide-prompt))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 284990e2d43..b38ef38e9f7 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -4746,15 +4746,25 @@ erc-auto-query
                  (const :tag "Use current buffer" buffer)
                  (const :tag "Use current buffer" t)))
 
-;; FIXME either retire this or put it to use after determining how
-;; it's meant to work.  Clearly, the doc string does not describe
-;; current behavior.  It's currently only used by the obsolete
-;; function `erc-auto-query'.
 (defcustom erc-query-on-unjoined-chan-privmsg t
   "If non-nil create query buffer on receiving any PRIVMSG at all.
 This includes PRIVMSGs directed to channels.  If you are using an IRC
 bouncer, such as dircproxy, to keep a log of channels when you are
-disconnected, you should set this option to t."
+disconnected, you should set this option to t.
+
+Note that this option was absent from ERC 5.5 because knowledge
+of its intended role was \"unavailable\" during a major
+refactoring involving buffer management.  The option has since
+been restored in ERC 5.6 with the caveat that its purpose is
+assumed to more or less reflect the following, which can be
+thought of as an updated version of the paragraph above:
+
+If non-nil, create a channel buffer on receiving a PRIVMSG if
+none exists.  This only affects messages targeting a channel
+whose buffer was killed but to which you're still joined or one
+whose buffer never existed, presumably because a bouncer is
+sending playback summoned with a command similar to ZNC's
+PLAYBUFFER."
   :group 'erc-query
   :type 'boolean)
 
diff --git a/test/lisp/erc/erc-scenarios-base-attach.el b/test/lisp/erc/erc-scenarios-base-attach.el
new file mode 100644
index 00000000000..91763842879
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-base-attach.el
@@ -0,0 +1,147 @@
+;;; erc-scenarios-base-attach.el --- Reattach scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See also: `erc-scenarios-base-channel-buffer-revival'.
+;;
+;; ERC 5.5 silently dropped support for the ancient option
+;; `erc-query-on-unjoined-chan-privmsg' because the tangled logic in
+;; and around the function `erc-auto-query' made it difficult to
+;; divine its purpose.
+;;
+;; Based on the name, it was thought this option likely involved
+;; controlling the creation of query buffers for unsolicited messages
+;; from users with whom you don't share a common channel.  However,
+;; additional spelunking has recently revealed that it was instead
+;; meant to service a feature offered by most bouncers that sends
+;; PRIVMSGs directed at a channel you're no longer in and that you
+;; haven't received a(nother) JOIN message for.  IOW, this is meant to
+;; support the following sequence of events:
+;;
+;;   1. /detach #chan
+;;   2. kill buffer #chan or reconnect in new Emacs session
+;;   3. /playbuffer #chan
+;;
+;; Note that the above slash commands are bouncer-specific aliases.
+;;
+;; Interested users can find more info by looking at this change set
+;; from the ancient CVS repo:
+;;
+;;   Author:     Mario Lang <mlang@delysid.org>
+;;   AuthorDate: Mon Nov 26 18:33:19 2001 +0000
+;;
+;;   * new function erc-BBDB-NICK to handle nickname anotation ...
+;;   * Applied antifuchs/mhp patches, the latest on erc-help, unmodified
+;;   * New variable: erc-reuse-buffers default to t.
+;;   * Modified erc-generate-new-buffer-name to use it. it checks if
+;;     server and port are the same, then one can assume thats the same
+;;     channel/query target again.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(ert-deftest erc-scenarios-base-attach--query-on-unjoined-enabled ()
+  :tags '(:expensive-test)
+  (should erc-query-on-unjoined-chan-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/channel-buffer-revival")
+       (dumb-server (erc-d-run "localhost" t 'reattach))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "tester@vanilla/foonet:changeme"
+                                :full-name "tester")
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet"))
+      (erc-cmd-MSG "*status playbuffer #chan"))
+
+    (ert-info ("Playback appears in buffer #chan")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "Was I a child")
+        (funcall expect 10 "Thou counterfeit'st most lively")
+        (funcall expect 10 "Playback Complete")))
+
+    (with-current-buffer "foonet"
+      (erc-cmd-MSG "*status attach #chan"))
+
+    (ert-info ("Live output from #chan after more playback")
+      (with-current-buffer "#chan"
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "With what it loathes")
+        (funcall expect 10 "Not by his breath")
+        (funcall expect 10 "Playback Complete")
+        (funcall expect 10 "Ay, and the captain")
+        (erc-scenarios-common-say "bob: hi")
+        (funcall expect 10 "Pawn me to this")))))
+
+(ert-deftest erc-scenarios-base-attach--query-on-unjoined-disabled ()
+  :tags '(:expensive-test)
+  (should erc-query-on-unjoined-chan-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/channel-buffer-revival")
+       (dumb-server (erc-d-run "localhost" t 'reattach))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (erc-query-on-unjoined-chan-privmsg nil) ; off
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "tester@vanilla/foonet:changeme"
+                                :full-name "tester")
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet"))
+      (erc-cmd-MSG "*status playbuffer #chan")
+      (ert-info ("Playback appears in buffer server buffer")
+        (erc-d-t-ensure-for -1 (not (get-buffer "#chan")))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "Was I a child")
+        (funcall expect 10 "Thou counterfeit'st most lively")
+        (funcall expect 10 "Playback Complete"))
+      (should-not (get-buffer "#chan"))
+      (erc-cmd-MSG "*status attach #chan"))
+
+    (ert-info ("Buffer #chan joined")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "With what it loathes")
+        (funcall expect 10 "Not by his breath")
+        (funcall expect 10 "Playback Complete")
+        (funcall expect 10 "Ay, and the captain")
+        (erc-scenarios-common-say "bob: hi")
+        (funcall expect 10 "Pawn me to this")))))
+
+;;; erc-scenarios-base-attach.el ends here
diff --git a/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld
new file mode 100644
index 00000000000..c3791ac3d49
--- /dev/null
+++ b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld
@@ -0,0 +1,56 @@
+;; -*- mode: lisp-data; -*-
+((pass 10 "PASS :tester@vanilla/foonet:changeme"))
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
+ (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0.00 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1")
+ (0.00 ":irc.foonet.org 003 tester :This server was created Thu, 13 Apr 2023 05:55:22 UTC")
+ (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server")
+ (0.00 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server")
+ (0.00 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server")
+ (0.00 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0.00 ":irc.foonet.org 254 tester 1 :channels formed")
+ (0.00 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers")
+ (0.00 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0.00 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0.00 ":irc.foonet.org 422 tester :MOTD File is missing"))
+
+((mode 10 "MODE tester +i")
+ (0.01 ":irc.foonet.org 221 tester +Zi"))
+
+((privmsg-play 10 "PRIVMSG *status :playbuffer #chan")
+ (0.05 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:24] alice: Was I a child, to fear I know not what.")
+ (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:29] bob: My lord, I do confess the ring was hers.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:40] alice: My sons would never so dishonour me.")
+ (0.01 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:54] bob: By the hand of a soldier, I will undertake it.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:57] alice: Thou counterfeit'st most lively.")
+ (0.01 ":***!znc@znc.in PRIVMSG #chan :Playback Complete."))
+
+((privmsg-attach 10 "PRIVMSG *status :attach #chan")
+ (0.01 ":tester!~u@78a58pgahbr24.irc JOIN #chan"))
+
+((mode-chan 10 "MODE #chan")
+ (0.01 ":irc.foonet.org 353 tester = #chan :@alice bob tester")
+ (0.00 ":irc.foonet.org 366 tester #chan :End of /NAMES list.")
+ (0.00 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:01] bob: With what it loathes for that which is away.")
+ (0.00 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:30] alice: Ties up my tongue, and will not let me speak.")
+ (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:26] bob: They say he is already in the forest of Arden, and a many merry men with him; and there they live like the old Robin Hood of England. They say many young gentlemen flock to him every day, and fleet the time carelessly, as they did in the golden world.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:29] alice: Not by his breath that is more miserable.")
+ (0.00 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")
+ (0.00 ":*status!znc@znc.in PRIVMSG tester :There was 1 channel matching [#chan]")
+ (0.03 ":*status!znc@znc.in PRIVMSG tester :Attached 1 channel")
+ (0.00 ":irc.foonet.org 324 tester #chan +Cnt")
+ (0.00 ":irc.foonet.org 329 tester #chan 1681365340")
+ (0.03 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Five or six thousand horse, I said,I will say true,or thereabouts, set down, for I'll speak truth.")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Riddling confession finds but riddling shrift.")
+ (0.04 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Ay, and the captain of his horse, Count Rousillon."))
+
+((privmsg-bob 10 "PRIVMSG #chan :bob: hi")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: But thankful even for hate, that is meant love.")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :tester: Come, come, elder brother, you are too young in this.")
+ (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Sir, we have known together in Orleans.")
+ (0.05 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Pawn me to this your honour, she is his."))
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0002-5.6-Move-ERC-s-buffer-display-tests-to-separate-file.patch --]
[-- Type: text/x-patch, Size: 9905 bytes --]

From 0e4b778f1a613743b5f806fdb749de7f7580fb53 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Thu, 20 Apr 2023 19:23:54 -0700
Subject: [PATCH 2/4] [5.6] Move ERC's buffer-display tests to separate file

* test/lisp/erc/erc-scenarios-base-buffer-display.el: New file.
* test/lisp/erc/erc-scenarios-base-reconnect.el
(erc-scenarios-common--base-reconnect-options,
erc-scenarios-base-reconnect-options--buffer,
erc-scenarios-base-reconnect-options--default): Move to new file and
rename.  (Bug#62833)
---
 .../erc/erc-scenarios-base-buffer-display.el  | 121 ++++++++++++++++++
 test/lisp/erc/erc-scenarios-base-reconnect.el |  89 -------------
 2 files changed, 121 insertions(+), 89 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-buffer-display.el

diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
new file mode 100644
index 00000000000..d511c8ff738
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -0,0 +1,121 @@
+;;; erc-scenarios-base-buffer-display.el --- Buffer display scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(eval-when-compile (require 'erc-join))
+
+;; These first couple `erc-reconnect-display' tests used to live in
+;; erc-scenarios-base-reconnect but have since been renamed.
+
+(defun erc-scenarios-base-buffer-display--reconnect-common (test)
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/reconnect")
+       (dumb-server (erc-d-run "localhost" t 'options 'options-again))
+       (port (process-contact dumb-server :service))
+       (expect (erc-d-t-make-expecter))
+       (erc-server-flood-penalty 0.1)
+       (erc-server-auto-reconnect t)
+       erc-autojoin-channels-alist
+       erc-server-buffer)
+
+    (should (memq 'autojoin erc-modules))
+
+    (ert-info ("Connect to foonet")
+      (setq erc-server-buffer (erc :server "127.0.0.1"
+                                   :port port
+                                   :nick "tester"
+                                   :password "changeme"
+                                   :full-name "tester"))
+      (with-current-buffer erc-server-buffer
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
+        (funcall expect 10 "debug mode")))
+
+    (ert-info ("Wait for some output in channels")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "welcome")))
+
+    (ert-info ("Server buffer shows connection failed")
+      (with-current-buffer erc-server-buffer
+        (funcall expect 10 "Connection failed!  Re-establishing")))
+
+    (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
+
+    (funcall test)
+
+    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
+    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
+
+    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
+      (not (eq (window-buffer) (get-buffer "#spam"))))
+
+    (ert-info ("Wait for auto reconnect")
+      (with-current-buffer erc-server-buffer
+        (funcall expect 10 "still in debug mode")))
+
+    (ert-info ("Wait for activity to recommence in channels")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "forest of Arden"))
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+        (funcall expect 10 "her elves come here anon")))))
+
+(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should-not erc-reconnect-display)
+
+  ;; FooNet (the server buffer) is not switched to because it's
+  ;; already current (but not shown) when `erc-open' is called.  See
+  ;; related conditional guard towards the end of that function.
+
+  (let ((erc-reconnect-display 'buffer))
+    (erc-scenarios-base-buffer-display--reconnect-common
+     (lambda ()
+       (pop-to-buffer-same-window "*Messages*")
+
+       (erc-d-t-ensure-for 1 "Server buffer not shown"
+         (not (eq (window-buffer) (get-buffer "FooNet"))))
+
+       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
+         (eq (window-buffer) (get-buffer "#chan")))))))
+
+(ert-deftest erc-scenarios-base-reconnect-options--default ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should-not erc-reconnect-display)
+
+  (erc-scenarios-base-buffer-display--reconnect-common
+
+   (lambda ()
+     (pop-to-buffer-same-window "*Messages*")
+
+     (erc-d-t-ensure-for 1 "Server buffer not shown"
+       (not (eq (window-buffer) (get-buffer "FooNet"))))
+
+     (erc-d-t-ensure-for 3 "Channel #chan not shown"
+       (not (eq (window-buffer) (get-buffer "#chan"))))
+
+     (should (eq (window-buffer) (messages-buffer))))))
+
+;;; erc-scenarios-base-buffer-display.el ends here
diff --git a/test/lisp/erc/erc-scenarios-base-reconnect.el b/test/lisp/erc/erc-scenarios-base-reconnect.el
index 5b4dc549042..7bd16d1ed14 100644
--- a/test/lisp/erc/erc-scenarios-base-reconnect.el
+++ b/test/lisp/erc/erc-scenarios-base-reconnect.el
@@ -65,95 +65,6 @@ erc-scenarios-base-reconnect-timer
       (should (equal (list (get-buffer (format "127.0.0.1:%d" port)))
                      (erc-scenarios-common-buflist "127.0.0.1"))))))
 
-(defun erc-scenarios-common--base-reconnect-options (test)
-  (erc-scenarios-common-with-cleanup
-      ((erc-scenarios-common-dialog "base/reconnect")
-       (dumb-server (erc-d-run "localhost" t 'options 'options-again))
-       (port (process-contact dumb-server :service))
-       (expect (erc-d-t-make-expecter))
-       (erc-server-flood-penalty 0.1)
-       (erc-server-auto-reconnect t)
-       erc-autojoin-channels-alist
-       erc-server-buffer)
-
-    (should (memq 'autojoin erc-modules))
-
-    (ert-info ("Connect to foonet")
-      (setq erc-server-buffer (erc :server "127.0.0.1"
-                                   :port port
-                                   :nick "tester"
-                                   :password "changeme"
-                                   :full-name "tester"))
-      (with-current-buffer erc-server-buffer
-        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
-        (funcall expect 10 "debug mode")))
-
-    (ert-info ("Wait for some output in channels")
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
-        (funcall expect 10 "welcome")))
-
-    (ert-info ("Server buffer shows connection failed")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "Connection failed!  Re-establishing")))
-
-    (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
-
-    (funcall test)
-
-    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
-    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
-
-    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
-      (not (eq (window-buffer) (get-buffer "#spam"))))
-
-    (ert-info ("Wait for auto reconnect")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "still in debug mode")))
-
-    (ert-info ("Wait for activity to recommence in channels")
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
-        (funcall expect 10 "forest of Arden"))
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
-        (funcall expect 10 "her elves come here anon")))))
-
-(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
-
-  ;; FooNet (the server buffer) is not switched to because it's
-  ;; already current (but not shown) when `erc-open' is called.  See
-  ;; related conditional guard towards the end of that function.
-
-  (let ((erc-reconnect-display 'buffer))
-    (erc-scenarios-common--base-reconnect-options
-     (lambda ()
-       (pop-to-buffer-same-window "*Messages*")
-
-       (erc-d-t-ensure-for 1 "Server buffer not shown"
-         (not (eq (window-buffer) (get-buffer "FooNet"))))
-
-       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
-         (eq (window-buffer) (get-buffer "#chan")))))))
-
-(ert-deftest erc-scenarios-base-reconnect-options--default ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
-
-  (erc-scenarios-common--base-reconnect-options
-
-   (lambda ()
-     (pop-to-buffer-same-window "*Messages*")
-
-     (erc-d-t-ensure-for 1 "Server buffer not shown"
-       (not (eq (window-buffer) (get-buffer "FooNet"))))
-
-     (erc-d-t-ensure-for 3 "Channel #chan not shown"
-       (not (eq (window-buffer) (get-buffer "#chan"))))
-
-     (eq (window-buffer) (messages-buffer)))))
-
 ;; Upon reconnecting, playback for channel and target buffers is
 ;; routed correctly.  Autojoin is irrelevant here, but for the
 ;; skeptical, see `erc-scenarios-common--join-network-id', which
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: 0003-5.6-Extend-erc-interactive-display-to-cover-JOINs.patch --]
[-- Type: text/x-patch, Size: 18193 bytes --]

From 1d7e1b82fa36746b5a1ff8d5c9376a4a93ffc090 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 10 Apr 2023 17:58:05 -0700
Subject: [PATCH 3/4] [5.6] Extend erc-interactive-display to cover /JOINs

* lisp/erc/erc.el (erc-display): Mention that buffer-related display
options live in the customization group `erc-buffers'.
(erc-buffer-display, erc-join-buffer): Swap alias and aliased so that
the favored name, `erc-buffer-display', appears in the definition and
in the Customize menu.  Also note related buffer-display options in
the doc string.  Remove redundant `t' from list of values.
(erc-query-display, erc-interactive-display): Make the former an alias
for the latter because their roles were functionally redundant and
thus confusing.  Inherit the default value from `erc-query-display'
because users are more familiar with the pop-up window behavior than a
single-window replacement.
(erc-reconnect-display): Use preferred name for cross-referencing
fallback option `erc-buffer-display' in doc string and explain how
/reconnect handling differs.
(erc--called-as-input-p): New variable for "slash" commands, like
`erc-cmd-FOO', to detect whether they're being called "interactively"
as a result of input given at ERC's prompt.
(erc-process-input-line): Bind `erc--called-as-input-p' when running
slash commands.
(erc-cmd-JOIN): When called interactively, schedule a callback to wrap
the response handler and control how new buffers are thus displayed.
(erc-query): Fix deprecation message.
(erc-auto-query, erc-receive-query-display): Swap alias and aliased so
the preferred name appears in customization buffers.  Mention the
nonstandard meaning of nil for this option.
* test/lisp/erc/erc-scenarios-base-buffer-display.el:
(erc-scenarios-base-buffer-display--interactive-default): New test.
* test/lisp/erc/erc-tests.el (erc-process-input-line,
erc-select-read-args, erc-tls, erc--interactive): Change expected
default value of `erc-interactive-display' from `buffer' to
`window'.  (Bug#62833)
---
 etc/ERC-NEWS                                  | 17 ++--
 lisp/erc/erc.el                               | 99 +++++++++++--------
 .../erc/erc-scenarios-base-buffer-display.el  | 37 +++++++
 test/lisp/erc/erc-tests.el                    | 13 +--
 4 files changed, 110 insertions(+), 56 deletions(-)

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 31f40e9d0d3..e9de48b2e34 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -37,15 +37,18 @@ decade overdue, this is no longer the case.  Other UX improvements in
 this area aim to make the process of connecting interactively slightly
 more streamlined and less repetitive, even for veteran users.
 
-** New buffer-display option 'erc-interactive-display'.
+** Revised buffer-display handling for interactive commands.
 A point of friction for new users and one only just introduced with
 ERC 5.5 has been the lack of visual feedback when first connecting via
-M-x erc.  As explained below in the news for 5.5, the discovery of a
-security issue led to new ERC buffers being "buried" on creation.  On
-further reflection, this was judged to have been an overcorrection in
-the case of interactive invocations, hence the new option
-'erc-interactive-display', which is set to 'buffer' (i.e., "take me
-there") by default.  Accompanying this addition are "display"-suffixed
+M-x erc or when issuing a "/JOIN" command at the prompt.  As explained
+below, in the news for 5.5, the discovery of a security issue led to
+most new ERC buffers being "buried" on creation.  On further
+reflection, this was judged to have been an overcorrection in the case
+of interactive invocations, hence the borrowing of an old option,
+'erc-query-display', and the bestowing of a new alias,
+'erc-interactive-display', which better describes its expanded role as
+a more general buffer-display knob for interactive commands ("/QUERY"
+still among them).  Accompanying this addition are "display"-suffixed
 aliases for related options 'erc-join-buffer' and 'erc-auto-query',
 which users have reported as being difficult to discover and remember.
 
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index b38ef38e9f7..5f1aaaafe49 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -98,7 +98,9 @@ erc-buffers
   :group 'erc)
 
 (defgroup erc-display nil
-  "Settings for how various things are displayed."
+  "Settings controlling how various things are displayed.
+See the customization group `erc-buffers' for display options
+concerning buffers."
   :group 'erc)
 
 (defgroup erc-mode-line-and-header nil
@@ -1507,9 +1509,9 @@ erc-default-port-tls
   "IRC port to use for encrypted connections if it cannot be \
 detected otherwise.")
 
-(defvaralias 'erc-buffer-display 'erc-join-buffer)
-(defcustom erc-join-buffer 'bury
-  "Determines how to display a newly created IRC buffer.
+(defvaralias 'erc-join-buffer 'erc-buffer-display)
+(defcustom erc-buffer-display 'bury
+  "How to display a newly created or reassociated ERC buffer.
 
 The available choices are:
 
@@ -1518,23 +1520,29 @@ erc-join-buffer
   `frame'           - in another frame,
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
-  any other value  - in place of the current buffer."
+
+See related options `erc-interactive-display' and
+`erc-reconnect-display' as well as `erc-receive-query-display',
+which resides in the customization group `erc-query'."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type '(choice (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
                  (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+                 (const :tag "Use current buffer" buffer)))
 
-(defcustom erc-interactive-display 'buffer
-  "How and whether to display server buffers for M-x erc.
-See `erc-buffer-display' and friends for a description of
-possible values."
+(defvaralias 'erc-query-display 'erc-interactive-display)
+(defcustom erc-interactive-display 'window
+  "How to display buffers as a result of user interaction.
+This affects commands like /QUERY and /JOIN when issued
+interactively at the prompt.  It does not apply when calling a
+handler for such a command, like `erc-cmd-JOIN', from lisp code.
+See `erc-buffer-display' for a full description of available
+values."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-join-buffer'" nil)
+  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
@@ -1542,12 +1550,11 @@ erc-interactive-display
                  (const :tag "Use current buffer" buffer)))
 
 (defcustom erc-reconnect-display nil
-  "How (and whether) to display a channel buffer upon reconnecting.
-
-This only affects automatic reconnections and is ignored when
-issuing a /reconnect command or reinvoking `erc-tls' with the
-same args (assuming success, of course).  See `erc-join-buffer'
-for a description of possible values."
+  "How and whether to display a channel buffer when auto-reconnecting.
+This only affects automatic reconnections and is ignored, like
+all other buffer-display options, when issuing a /RECONNECT or
+successfully reinvoking `erc-tls' with similar arguments.  See
+`erc-buffer-display' for a description of possible values."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type '(choice (const :tag "Use value of `erc-join-buffer'" nil)
@@ -3057,6 +3064,10 @@ erc-message-type-member
   (let ((prop-val (erc-get-parsed-vector position)))
     (and prop-val (member (erc-response.command prop-val) list))))
 
+(defvar erc--called-as-input-p nil
+  "Non-nil when a user types a \"/slash\" command.
+Remains bound until `erc-cmd-SLASH' returns.")
+
 (defvar-local erc-send-input-line-function 'erc-send-input-line
   "Function for sending lines lacking a leading user command.
 When a line typed into a buffer contains an explicit command, like /msg,
@@ -3110,7 +3121,8 @@ erc-process-input-line
     (if (and command-list
              (not no-command))
         (let* ((cmd  (nth 0 command-list))
-               (args (nth 1 command-list)))
+               (args (nth 1 command-list))
+               (erc--called-as-input-p t))
           (condition-case nil
               (if (listp args)
                   (apply cmd args)
@@ -3584,6 +3596,21 @@ erc-cmd-JOIN
                    (erc-get-channel-user (erc-current-nick)))))
           (switch-to-buffer existing)
         (setq erc--server-last-reconnect-count 0)
+        (when-let* ; bind `erc-join-buffer' when /JOIN issued
+            ((erc--called-as-input-p)
+             (fn (lambda (proc parsed)
+                   (when-let* ; `fn' wrapper already removed from hook
+                       (((equal (car (erc-response.command-args parsed))
+                                channel))
+                        (sn (erc-extract-nick (erc-response.sender parsed)))
+                        ((erc-nick-equal-p sn (erc-current-nick)))
+                        (erc-join-buffer (or erc-interactive-display
+                                             erc-join-buffer)))
+                     (run-hook-with-args-until-success
+                      'erc-server-JOIN-functions proc parsed)
+                     t))))
+          (erc-with-server-buffer
+            (erc-once-with-server-event "JOIN" fn)))
         (erc-server-join-channel nil chnl key))))
   t)
 
@@ -3947,23 +3974,6 @@ erc-cmd-QUOTE
    (t nil)))
 (put 'erc-cmd-QUOTE 'do-not-parse-args t)
 
-(defcustom erc-query-display 'window
-  "How to display query buffers when using the /QUERY command to talk to someone.
-
-The default behavior is to display the message in a new window
-and bring it to the front.  See the documentation for
-`erc-join-buffer' for a description of the available choices.
-
-See also `erc-auto-query' to decide how private messages from
-other people should be displayed."
-  :group 'erc-query
-  :type '(choice (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
-
 (defun erc-cmd-QUERY (&optional user)
   "Open a query with USER.
 How the query is displayed (in a new window, frame, etc.) depends
@@ -4722,29 +4732,32 @@ erc-query
   "Open a query buffer on TARGET using SERVER-BUFFER.
 To change how this query window is displayed, use `let' to bind
 `erc-join-buffer' before calling this."
-  (declare (obsolete "bind `erc-cmd-query' and call `erc-cmd-QUERY'" "29.1"))
+  (declare (obsolete "call `erc-open' in a live server buffer" "29.1"))
   (unless (buffer-live-p server-buffer)
     (error "Couldn't switch to server buffer"))
   (with-current-buffer server-buffer
     (erc--open-target target)))
 
-(defvaralias 'erc-receive-query-display 'erc-auto-query)
-(defcustom erc-auto-query 'window-noselect
-  "If non-nil, create a query buffer each time you receive a private message.
+(defvaralias 'erc-auto-query 'erc-receive-query-display)
+(defcustom erc-receive-query-display 'window-noselect
+  "How to display a query buffer when you receive a private message.
 If the buffer doesn't already exist, it is created.
 
 This can be set to a symbol, to control how the new query window
 should appear.  The default behavior is to display the buffer in
 a new window, but not to select it.  See the documentation for
-`erc-join-buffer' for a description of the available choices."
+`erc-buffer-display' for a description of available values.  Note
+that setting this option to nil forgoes buffer creation entirely.
+It does not mean \"fall back on `erc-buffer-display'\", like it
+does for buffer-display options, like `erc-interactive-display',
+that reside in the customization group `erc-buffers'."
   :group 'erc-query
   :type '(choice (const :tag "Don't create query window" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
                  (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+                 (const :tag "Use current buffer" buffer)))
 
 (defcustom erc-query-on-unjoined-chan-privmsg t
   "If non-nil create query buffer on receiving any PRIVMSG at all.
diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
index d511c8ff738..3ed7a83653e 100644
--- a/test/lisp/erc/erc-scenarios-base-buffer-display.el
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -118,4 +118,41 @@ erc-scenarios-base-reconnect-options--default
 
      (should (eq (window-buffer) (messages-buffer))))))
 
+
+;; This shows that the option `erc-interactive-display' overrides
+;; `erc-join-buffer' during cold opens and interactive /JOINs.
+
+(ert-deftest erc-scenarios-base-buffer-display--interactive-default ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should (eq erc-interactive-display 'window))
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "join/legacy")
+       (dumb-server (erc-d-run "localhost" t 'foonet))
+       (port (process-contact dumb-server :service))
+       (url (format "tester:changeme@127.0.0.1:%d\r\r" port))
+       (expect (erc-d-t-make-expecter))
+       (erc-server-flood-penalty 0.1)
+       (erc-server-auto-reconnect t)
+       (erc-user-full-name "tester"))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (let (inhibit-interaction)
+                             (ert-simulate-keys url
+                               (call-interactively #'erc)))
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
+
+        (erc-d-t-wait-for 10 "Server buffer shown"
+          (eq (window-buffer) (current-buffer)))
+        (funcall expect 10 "debug mode")
+        (erc-scenarios-common-say "/JOIN #chan")))
+
+    (ert-info ("Wait for output in #chan")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "welcome")
+        (erc-d-t-ensure-for 3 "Channel #chan shown"
+          (eq (window-buffer) (current-buffer)))
+        (funcall expect 10 "be prosperous")))))
+
 ;;; erc-scenarios-base-buffer-display.el ends here
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 29bda7e742d..88b9babf206 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1292,6 +1292,7 @@ erc-process-input-line
       (cl-letf (((symbol-function 'erc-cmd-MSG)
                  (lambda (line)
                    (push line calls)
+                   (should erc--called-as-input-p)
                    (funcall orig-erc-cmd-MSG line)))
                 ((symbol-function 'erc-server-buffer)
                  (lambda () (current-buffer)))
@@ -1469,7 +1470,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when port matches default TLS port")
     (should (equal (ert-simulate-keys "irc.gnu.org\r6697\r\r\r"
@@ -1479,7 +1480,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when URL is ircs://")
     (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
@@ -1489,7 +1490,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (setq-local erc-interactive-display nil) ; cheat to save space
 
@@ -1625,7 +1626,7 @@ erc-tls
                        '("localhost" 6667 "nick" "unknown" t "sesame"
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer)
+                       '((erc-join-buffer window)
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Custom connect function")
@@ -1686,7 +1687,7 @@ erc--interactive
                        '("irc.libera.chat" 6697 "tester" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer) (erc-server-connect-function
+                       '((erc-join-buffer window) (erc-server-connect-function
                                                    erc-open-tls-stream)))))
 
       (ert-info ("Nick supplied, decline TLS upgrade")
@@ -1696,7 +1697,7 @@ erc--interactive
                        '("irc.libera.chat" 6667 "dummy" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer)
+                       '((erc-join-buffer window)
                          (erc-server-connect-function
                           erc-open-network-stream))))))))
 
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: 0004-5.6-Ignore-erc-reconnect-display-after-a-timeout.patch --]
[-- Type: text/x-patch, Size: 15857 bytes --]

From c7ed508ef36fcb2f0c6731b588b953c77b2eb0db Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Thu, 20 Apr 2023 19:20:59 -0700
Subject: [PATCH 4/4] [5.6] Ignore erc-reconnect-display after a timeout

* lisp/erc/erc-backend.el (erc--server-reconnect-display-timer): New
variable to store active timer that, upon firing, zeroes out
`erc--server-last-reconnect-count'.
(erc--server-last-reconnect-on-disconnect): New function to run on
`erc-disconnected-hook'.
(erc--server-last-reconnect-display-reset): New function that ensures
the reconnect-display window is closed.
* lisp/erc/erc.el (erc-reconnect-display-timeout): New option to
control how long `erc-reconnect-display' affects the displaying of new
or reassociated buffers following an automatic reconnection.
(erc-process-input-line): Ensure user input marks the end of the
reconnect-display window.
(erc-cmd-JOIN): Don't bother resetting
`erc--server-last-reconnect-count' because it's now handled by the
caller, `erc-process-input-line'.
(erc-connection-established): Schedule timer and register hook to
reset last-reconnect count and close the reconnect-display window.
* test/lisp/erc/erc-scenarios-base-buffer-display.el:
(erc-scenarios-base-buffer-display--reconnect-common): Add new args to
test fixture.
(erc-scenarios-base-reconnect-options--buffer,
erc-scenarios-base-buffer-display--defwin-recbury-intbuf): Rename
former to latter and rework to better reflect realistic settings
for the relevant display options.
(erc-scenarios-base-reconnect-options--default,
erc-scenarios-base-buffer-display--defwino-recbury-intbuf): Rename
former to latter and rework to be more realistic.
(erc-scenarios-base-buffer-display--count-reset-timeout): New
test for new option `erc-reconnect-display-timeout'.  (Bug#62833)
---
 lisp/erc/erc-backend.el                       |  22 +++
 lisp/erc/erc.el                               |  15 +-
 .../erc/erc-scenarios-base-buffer-display.el  | 168 +++++++++++++-----
 3 files changed, 159 insertions(+), 46 deletions(-)

diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 5d44f478719..c72c769f2e1 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -298,6 +298,12 @@ erc-server-connected
 (defvar-local erc-server-reconnect-count 0
   "Number of times we have failed to reconnect to the current server.")
 
+(defvar-local erc--server-reconnect-display-timer nil
+  "Timer that resets `erc--server-last-reconnect-count' to zero.
+Becomes non-nil in all server buffers when an IRC connection is
+first \"established\" and carries out its duties
+`erc-reconnect-display-timeout' seconds later.")
+
 (defvar-local erc--server-last-reconnect-count 0
   "Snapshot of reconnect count when the connection was established.")
 
@@ -902,6 +908,22 @@ erc-server-reconnect-p
         erc-server-reconnecting)
       (erc--server-reconnect-p event)))
 
+(defun erc--server-last-reconnect-on-disconnect (&rest _)
+  (remove-hook 'erc-disconnected-hook
+               #'erc--server-last-reconnect-on-disconnect t)
+  (erc--server-last-reconnect-display-reset (current-buffer)))
+
+(defun erc--server-last-reconnect-display-reset (buffer)
+  "Deactivate `erc-reconnect-display'."
+  (when (buffer-live-p buffer)
+    (with-current-buffer buffer
+      (when erc--server-reconnect-display-timer
+        (cancel-timer erc--server-reconnect-display-timer)
+        (remove-hook 'erc-disconnected-hook
+                     #'erc--server-last-reconnect-display-reset t)
+        (setq erc--server-reconnect-display-timer nil
+              erc--server-last-reconnect-count 0)))))
+
 (defconst erc--mode-line-process-reconnecting
   '(:eval (erc-with-server-buffer
             (and erc--server-reconnect-timer
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 5f1aaaafe49..85b3d8bb650 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1564,6 +1564,13 @@ erc-reconnect-display
                  (const :tag "Bury in new buffer" bury)
                  (const :tag "Use current buffer" buffer)))
 
+(defcustom erc-reconnect-display-timeout 10
+  "Duration `erc-reconnect-display' remains active.
+The countdown starts on MOTD and is canceled early by any
+\"slash\" command."
+  :type 'integer
+  :group 'erc-buffer)
+
 (defcustom erc-frame-alist nil
   "Alist of frame parameters for creating erc frames.
 A value of nil means to use `default-frame-alist'."
@@ -3123,6 +3130,7 @@ erc-process-input-line
         (let* ((cmd  (nth 0 command-list))
                (args (nth 1 command-list))
                (erc--called-as-input-p t))
+          (erc--server-last-reconnect-display-reset (erc-server-buffer))
           (condition-case nil
               (if (listp args)
                   (apply cmd args)
@@ -3595,7 +3603,6 @@ erc-cmd-JOIN
                 ((with-current-buffer existing
                    (erc-get-channel-user (erc-current-nick)))))
           (switch-to-buffer existing)
-        (setq erc--server-last-reconnect-count 0)
         (when-let* ; bind `erc-join-buffer' when /JOIN issued
             ((erc--called-as-input-p)
              (fn (lambda (proc parsed)
@@ -5184,6 +5191,12 @@ erc-connection-established
         (setq erc-server-connected t)
         (setq erc--server-last-reconnect-count erc-server-reconnect-count
               erc-server-reconnect-count 0)
+        (setq erc--server-reconnect-display-timer
+              (run-at-time erc-reconnect-display-timeout nil
+                           #'erc--server-last-reconnect-display-reset
+                           (current-buffer)))
+        (add-hook 'erc-disconnected-hook
+                  #'erc--server-last-reconnect-on-disconnect nil t)
         (erc-update-mode-line)
         (erc-set-initial-user-mode nick buffer)
         (erc-server-setup-periodical-ping buffer)
diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
index 3ed7a83653e..53a3d7e8ef7 100644
--- a/test/lisp/erc/erc-scenarios-base-buffer-display.el
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -29,7 +29,8 @@
 ;; These first couple `erc-reconnect-display' tests used to live in
 ;; erc-scenarios-base-reconnect but have since been renamed.
 
-(defun erc-scenarios-base-buffer-display--reconnect-common (test)
+(defun erc-scenarios-base-buffer-display--reconnect-common
+    (assert-server assert-chan assert-rest)
   (erc-scenarios-common-with-cleanup
       ((erc-scenarios-common-dialog "base/reconnect")
        (dumb-server (erc-d-run "localhost" t 'options 'options-again))
@@ -37,87 +38,164 @@ erc-scenarios-base-buffer-display--reconnect-common
        (expect (erc-d-t-make-expecter))
        (erc-server-flood-penalty 0.1)
        (erc-server-auto-reconnect t)
-       erc-autojoin-channels-alist
-       erc-server-buffer)
+       erc-autojoin-channels-alist)
 
     (should (memq 'autojoin erc-modules))
 
     (ert-info ("Connect to foonet")
-      (setq erc-server-buffer (erc :server "127.0.0.1"
-                                   :port port
-                                   :nick "tester"
-                                   :password "changeme"
-                                   :full-name "tester"))
-      (with-current-buffer erc-server-buffer
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "changeme"
+                                :full-name "tester")
+        (funcall assert-server expect)
         (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
         (funcall expect 10 "debug mode")))
 
     (ert-info ("Wait for some output in channels")
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall assert-chan expect)
         (funcall expect 10 "welcome")))
 
     (ert-info ("Server buffer shows connection failed")
-      (with-current-buffer erc-server-buffer
+      (with-current-buffer "FooNet"
         (funcall expect 10 "Connection failed!  Re-establishing")))
 
     (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
-
-    (funcall test)
-
-    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
-    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
-
-    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
-      (not (eq (window-buffer) (get-buffer "#spam"))))
+    (delete-other-windows)
+    (pop-to-buffer-same-window "*Messages*")
 
     (ert-info ("Wait for auto reconnect")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "still in debug mode")))
+      (with-current-buffer "FooNet" (funcall expect 10 "still in debug mode")))
 
-    (ert-info ("Wait for activity to recommence in channels")
+    (funcall assert-rest expect)
+
+    (ert-info ("Wait for activity to recommence in both channels")
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
         (funcall expect 10 "forest of Arden"))
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
         (funcall expect 10 "her elves come here anon")))))
 
-(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
+(ert-deftest erc-scenarios-base-buffer-display--defwin-recbury-intbuf ()
   :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
   (should-not erc-reconnect-display)
 
-  ;; FooNet (the server buffer) is not switched to because it's
-  ;; already current (but not shown) when `erc-open' is called.  See
-  ;; related conditional guard towards the end of that function.
+  (let ((erc-buffer-display 'window)
+        (erc-interactive-display 'buffer)
+        (erc-reconnect-display 'bury))
 
-  (let ((erc-reconnect-display 'buffer))
     (erc-scenarios-base-buffer-display--reconnect-common
-     (lambda ()
-       (pop-to-buffer-same-window "*Messages*")
 
-       (erc-d-t-ensure-for 1 "Server buffer not shown"
-         (not (eq (window-buffer) (get-buffer "FooNet"))))
+     (lambda (_)
+       (should (eq (window-buffer) (current-buffer)))
+       (should-not (frame-root-window-p (selected-window))))
 
-       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
-         (eq (window-buffer) (get-buffer "#chan")))))))
+     (lambda (_)
+       (should (eq (window-buffer) (current-buffer)))
+       (should (equal (get-buffer "FooNet") (window-buffer (next-window)))))
 
-(ert-deftest erc-scenarios-base-reconnect-options--default ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
 
-  (erc-scenarios-base-buffer-display--reconnect-common
+       ;; A manual /JOIN command tells ERC we're done auto-reconnecting
+       (with-current-buffer "FooNet" (erc-scenarios-common-say "/JOIN #spam"))
 
-   (lambda ()
-     (pop-to-buffer-same-window "*Messages*")
+       (ert-info ("#spam ignores `erc-reconnect-display'")
+         ;; Uses `erc-interactive-display' instead.
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (get-buffer "#spam")))
+           ;; Option `buffer' replaces entire window (no split)
+           (should (frame-root-window-p (selected-window)))))))))
 
-     (erc-d-t-ensure-for 1 "Server buffer not shown"
-       (not (eq (window-buffer) (get-buffer "FooNet"))))
+(ert-deftest erc-scenarios-base-buffer-display--defwino-recbury-intbuf ()
+  :tags '(:expensive-test)
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
+  (should-not erc-reconnect-display)
 
-     (erc-d-t-ensure-for 3 "Channel #chan not shown"
-       (not (eq (window-buffer) (get-buffer "#chan"))))
+  (let ((erc-buffer-display 'window-noselect)
+        (erc-reconnect-display 'bury)
+        (erc-interactive-display 'buffer))
+    (erc-scenarios-base-buffer-display--reconnect-common
 
-     (should (eq (window-buffer) (messages-buffer))))))
+     (lambda (_)
+       ;; Selected window shows some non-ERC buffer.  New server
+       ;; buffer appears in another window (other side of split).
+       (should-not (frame-root-window-p (selected-window)))
+       (should-not (eq (window-buffer) (current-buffer)))
+       (with-current-buffer (window-buffer)
+         (should-not (derived-mode-p 'erc-mode)))
+       (should (eq (current-buffer) (window-buffer (next-window)))))
+
+     (lambda (_)
+       (should-not (frame-root-window-p (selected-window)))
+       ;; Current split likely shows scratch.
+       (with-current-buffer (window-buffer)
+         (should-not (derived-mode-p 'erc-mode)))
+       (should (eq (current-buffer) (window-buffer (next-window)))))
+
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
+
+       ;; A non-interactive JOIN command doesn't signal that we're
+       ;; done auto-reconnecting, and `erc-interactive-display' is
+       ;; ignored, so `erc-buffer-display' is again in charge (here,
+       ;; that means `window-noselect').
+       (ert-info ("Join chan noninteractively and open a /QUERY")
+         (with-current-buffer "FooNet"
+           (erc-cmd-JOIN "#spam")
+           ;; However this will reset the option.
+           (erc-scenarios-common-say "/QUERY bob")
+           (should (eq (window-buffer) (get-buffer "bob")))
+           (should (frame-root-window-p (selected-window)))))
+
+       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (get-buffer "bob")))
+           (should-not (frame-root-window-p (selected-window)))
+           (should (eq (current-buffer) (window-buffer (next-window))))))))))
+
+(ert-deftest erc-scenarios-base-buffer-display--count-reset-timeout ()
+  :tags '(:expensive-test)
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
+  (should (eq erc-reconnect-display-timeout 10))
+  (should-not erc-reconnect-display)
 
+  (let ((erc-buffer-display 'window-noselect)
+        (erc-reconnect-display 'bury)
+        (erc-interactive-display 'buffer)
+        ;; Try changing this value to 1.  The last `ert-info' block
+        ;; should fail.
+        (erc-reconnect-display-timeout 0.1))
+    (erc-scenarios-base-buffer-display--reconnect-common
+     #'ignore #'ignore ; These two are identical to the previous test.
+
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
+
+       ;; A non-interactive JOIN command doesn't signal that we're
+       ;; done auto-reconnecting
+       (ert-info ("Join chan noninteractively")
+         (with-current-buffer "FooNet"
+           (sit-for 0.1)
+           (erc-cmd-JOIN "#spam")))
+
+       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (messages-buffer)))
+           ;; If `erc-reconnect-display-timeout' were left alone, this
+           ;; would be (frame-root-window-p #<window 1 on *scratch*>).
+           (should-not (frame-root-window-p (selected-window)))
+           (should (eq (current-buffer) (window-buffer (next-window))))))))))
 
 ;; This shows that the option `erc-interactive-display' overrides
 ;; `erc-join-buffer' during cold opens and interactive /JOINs.
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] ` <87354tcoyk.fsf@neverwas.me>
@ 2023-04-24 14:34   ` J.P.
  0 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-04-24 14:34 UTC (permalink / raw)
  To: 62833; +Cc: emacs-erc

[-- Attachment #1: Type: text/plain, Size: 3710 bytes --]

"J.P." <jp@neverwas.me> writes:

> A troubling discovery has come to light regarding the option
> `erc-reconnect-display' (new in 5.5 and Emacs 29), which was the main
> product of bug#51753 before it pivoted to that frame-isolation feature.
> (This bug thread exists in part to move past that confusion.) The issue
> here concerns the time interval during which `erc-reconnect-display'
> takes precedence over its fellow buffer-display options, like
> `erc-join-buffer'. As things stand, this interval only ends for a
> session when `erc-cmd-JOIN' runs in the server buffer. Without that
> specific intervention, the option remains in effect for the remainder of
> the session.

This issue will be noted in the option's doc string in Emacs 29.

>> A more daring and arguably more meaningful move would be to repurpose
>> `erc-auto-query' (newly aliased to `erc-receive-query-display') as
>> something like a more general `erc-receive-display', which could cover
>> display handling for anything protocol driven (i.e.,
>> "non-interactive").
>
> Given the more pressing concerns noted above, I haven't yet devoted any
> thought to this but promise to eventually.

I'm pretty much convinced that there's no way to generalize this option
without wading back into bug#51753 territory. If a user already has it
customized to `buffer' or `window', the same risk of accidental sharing
exists, and rigging up warnings to intercept them on the first such
happening doesn't seem worth it. So, for this latest iteration, I've
abandoned this notion and have instead focused on making good on the
claims put forth by `erc-query-on-unjoined-chan-privmsg' and its doc
string decades ago.

Basically, this involves changing the current meaning of
`erc-auto-query' when nil to mean "defer to `erc-join-buffer'," which
brings it in line with all the other buffer-display options. An
obligatory compatibility flag for users to access the old behavior
rounds out the change. The basic wager we're making here is that the
chances of someone having `erc-join-buffer' set to 'buffer' or 'window'
while also having this option set to nil is negligible. The upside to be
gained is having `erc-query-on-unjoined-chan-privmsg', which I've
renamed `erc-ensure-target-buffer-on-privmsg', affect query buffers as
well as channels, which is a minor but solid UX win, IMO.

>> There's also the matter of assigning Custom groups for these options.
>> It'd be "nice" if we could tag these with multiple groups rather than
>> confine them to exclusive ownership. They're currently spread over
>> `erc-buffers', `erc-query', and `erc-display'. All seem to have valid
>> claims when you consider their respective constituencies.
>
> Actually, that's slightly untrue: the `erc-display' group doesn't
> include any buffer-display options. Regardless, for this iteration, I've
> stuck with the current group assignments, which are `erc-buffers' for
> all but `erc-receive-query-display', which lives in `erc-query'.

I've since learned (from Corwin) that options can indeed belong to
multiple groups, which is explained clearly enough in the manual. Armed
with this fact, I'm now thinking it would behoove us to assign
`erc-auto-query' and `erc-query-on-unjoined-chan-privmsg' to
`erc-buffers' but keep them in `erc-query' as well, for compatibility.

>> It's also been casually suggested that we might consider deferring to
>> `erc-setup-buffer' in areas not directly involved in message handling,
>> such as in erc-sidebar, to allow the options in question to influence
>> how buffers are displayed more generally. Not sure I have an opinion on
>> this quite yet, but if anyone else does, please share.
>
> This is also still to do.

And still.



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v3-v4.diff --]
[-- Type: text/x-patch, Size: 15786 bytes --]

From dd9f78ea159958ccef4eabf3f782d4f29d4d24e7 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 24 Apr 2023 06:29:49 -0700
Subject: [PATCH 0/4] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (4):
  [5.6] Revive option erc-query-on-unjoined-chan-privmsg
  [5.6] Move ERC's buffer-display tests to separate file
  [5.6] Extend erc-interactive-display to cover /JOINs
  [5.6] Ignore erc-reconnect-display after a timeout

 etc/ERC-NEWS                                  |  36 ++-
 lisp/erc/erc-backend.el                       |  41 ++-
 lisp/erc/erc.el                               | 160 ++++++++----
 test/lisp/erc/erc-scenarios-base-attach.el    | 191 ++++++++++++++
 .../erc/erc-scenarios-base-buffer-display.el  | 236 ++++++++++++++++++
 test/lisp/erc/erc-scenarios-base-reconnect.el |  89 -------
 test/lisp/erc/erc-tests.el                    |  13 +-
 .../base/channel-buffer-revival/reattach.eld  |  56 +++++
 8 files changed, 660 insertions(+), 162 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-attach.el
 create mode 100644 test/lisp/erc/erc-scenarios-base-buffer-display.el
 create mode 100644 test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld

Interdiff:
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index e9de48b2e34..f1fac8e5baa 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -48,9 +48,16 @@ of interactive invocations, hence the borrowing of an old option,
 'erc-query-display', and the bestowing of a new alias,
 'erc-interactive-display', which better describes its expanded role as
 a more general buffer-display knob for interactive commands ("/QUERY"
-still among them).  Accompanying this addition are "display"-suffixed
-aliases for related options 'erc-join-buffer' and 'erc-auto-query',
-which users have reported as being difficult to discover and remember.
+still among them).
+
+Accompanying this addition are "display"-suffixed aliases for related
+options 'erc-join-buffer' and 'erc-auto-query', which users have
+reported as being difficult to discover and remember.  As for the
+latter option specifically (now known as 'erc-receive-query-display'),
+assigning it a nil value now tells ERC to use 'erc-join-buffer' in its
+place, much like with related buffer-display options, like
+'erc-interactive-display'.  The old nil behavior can still be gotten
+via a new compatibility flag, 'erc-receive-query-display-defer'.
 
 ** Setting a module's mode variable via Customize earns a warning.
 Trying and failing to activate a module via its minor mode's Custom
@@ -111,7 +118,8 @@ other than the symbol 'erc-button-buttonize-nicks' appearing in the
 "FORM" field (third element) of this entry are considered deprecated
 and will incur a warning.
 
-** The option erc-query-on-unjoined-chan-privmsg has been restored.
+** The option 'erc-query-on-unjoined-chan-privmsg' has been restored.
+(Although it's now called 'erc-ensure-target-buffer-on-privmsg'.)
 In ERC 5.5, this option was removed from the default client code and
 thus prevented from influencing PRIVMSG handling because its precise
 purpose could not be determined with any confidence.  After some
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index c72c769f2e1..d14640e798d 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -102,11 +102,11 @@
 (require 'erc-common)
 
 (defvar erc--target)
-(defvar erc-auto-query)
 (defvar erc-channel-list)
 (defvar erc-channel-users)
 (defvar erc-default-nicks)
 (defvar erc-default-recipients)
+(defvar erc-ensure-target-buffer-on-privmsg)
 (defvar erc-format-nick-function)
 (defvar erc-format-query-as-channel-p)
 (defvar erc-hide-prompt)
@@ -123,7 +123,8 @@ erc-nick
 (defvar erc-nick-change-attempt-count)
 (defvar erc-prompt-for-channel-key)
 (defvar erc-prompt-hidden)
-(defvar erc-query-on-unjoined-chan-privmsg)
+(defvar erc-receive-query-display)
+(defvar erc-receive-query-display-defer)
 (defvar erc-reuse-buffers)
 (defvar erc-verbose-server-ping)
 (defvar erc-whowas-on-nosuchnick)
@@ -1854,11 +1855,15 @@ define-erc-response-handler
         (unless (or buffer noticep (string-empty-p tgt) (eq ?$ (aref tgt 0))
                     (erc-is-message-ctcp-and-not-action-p msg))
           (if privp
-              (when erc-auto-query
-                (let ((erc-join-buffer erc-auto-query))
-                  (setq buffer (erc--open-target nick))))
-            ;; A channel buffer has been killed but is still joined
-            (when erc-query-on-unjoined-chan-privmsg
+              (when-let ((erc-join-buffer
+                          (or (and (not erc-receive-query-display-defer)
+                                   erc-receive-query-display)
+                              (and erc-ensure-target-buffer-on-privmsg
+                                   (or erc-receive-query-display
+                                       erc-join-buffer)))))
+                (setq buffer (erc--open-target nick)))
+            ;; A channel buffer has been killed but is still joined.
+            (when erc-ensure-target-buffer-on-privmsg
               (setq buffer (erc--open-target tgt)))))
         (when buffer
           (with-current-buffer buffer
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 85b3d8bb650..847e27904b1 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1521,16 +1521,16 @@ erc-buffer-display
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
 
-See related options `erc-interactive-display' and
-`erc-reconnect-display' as well as `erc-receive-query-display',
-which resides in the customization group `erc-query'."
+See related options `erc-interactive-display',
+`erc-reconnect-display', and `erc-receive-query-display'."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type '(choice (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
                  (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)))
+                 (const :tag "Use current buffer" buffer)
+                 (const :tag "Use current buffer" t)))
 
 (defvaralias 'erc-query-display 'erc-interactive-display)
 (defcustom erc-interactive-display 'window
@@ -1557,7 +1557,7 @@ erc-reconnect-display
 `erc-buffer-display' for a description of possible values."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-join-buffer'" nil)
+  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
@@ -1569,7 +1569,7 @@ erc-reconnect-display-timeout
 The countdown starts on MOTD and is canceled early by any
 \"slash\" command."
   :type 'integer
-  :group 'erc-buffer)
+  :group 'erc-buffers)
 
 (defcustom erc-frame-alist nil
   "Alist of frame parameters for creating erc frames.
@@ -3984,7 +3984,7 @@ erc-cmd-QUOTE
 (defun erc-cmd-QUERY (&optional user)
   "Open a query with USER.
 How the query is displayed (in a new window, frame, etc.) depends
-on the value of `erc-query-display'."
+on the value of `erc-interactive-display'."
   ;; FIXME: The doc string used to say at the end:
   ;; "If USER is omitted, close the current query buffer if one exists
   ;; - except this is broken now ;-)"
@@ -3995,8 +3995,8 @@ erc-cmd-QUERY
   (unless user
       ;; currently broken, evil hack to display help anyway
                                         ;(erc-delete-query))))
-    (signal 'wrong-number-of-arguments ""))
-  (let ((erc-join-buffer erc-query-display))
+    (signal 'wrong-number-of-arguments '(erc-cmd-QUERY 0)))
+  (let ((erc-join-buffer erc-interactive-display))
     (erc-with-server-buffer
      (erc--open-target user))))
 
@@ -4747,44 +4747,64 @@ erc-query
 
 (defvaralias 'erc-auto-query 'erc-receive-query-display)
 (defcustom erc-receive-query-display 'window-noselect
-  "How to display a query buffer when you receive a private message.
+  "If non-nil, create a query buffer each time you receive a private message.
 If the buffer doesn't already exist, it is created.
 
 This can be set to a symbol, to control how the new query window
 should appear.  The default behavior is to display the buffer in
-a new window, but not to select it.  See the documentation for
-`erc-buffer-display' for a description of available values.  Note
-that setting this option to nil forgoes buffer creation entirely.
-It does not mean \"fall back on `erc-buffer-display'\", like it
-does for buffer-display options, like `erc-interactive-display',
-that reside in the customization group `erc-buffers'."
+a new window but not to select it.  See the documentation for
+`erc-buffer-display' for a description of available values.
+
+Note that the legacy behavior of forgoing buffer creation
+entirely when this option is nil requires setting the
+compatibility flag `erc-receive-query-display-defer' to nil.  Use
+`erc-ensure-target-buffer-on-privmsg' to achieve the same effect."
+  :package-version '(ERC . "5.6")
+  :group 'erc-buffers
   :group 'erc-query
-  :type '(choice (const :tag "Don't create query window" nil)
+  :type '(choice (const :tag "Defer to value of `erc-buffer-display'" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
                  (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)))
+                 (const :tag "Use current buffer" buffer)
+                 (const :tag "Use current buffer" t)))
+
+(defcustom erc-receive-query-display-defer t
+  "How to interpret a null `erc-receive-query-display'.
+When this option is non-nil, ERC defers to `erc-buffer-display'
+upon seeing a nil value for `erc-receive-query-display', much
+like it does with other buffer-display options, like
+`erc-interactive-display'.  Otherwise, when this option is nil,
+ERC retains the legacy behavior of not creating a new query
+buffer."
+  :package-version '(ERC . "5.6")
+  :group 'erc-buffers
+  :group 'erc-query
+  :type 'boolean)
 
-(defcustom erc-query-on-unjoined-chan-privmsg t
-  "If non-nil create query buffer on receiving any PRIVMSG at all.
+(defvaralias 'erc-query-on-unjoined-chan-privmsg
+  'erc-ensure-target-buffer-on-privmsg)
+(defcustom erc-ensure-target-buffer-on-privmsg t
+  "When non-nil, create a target buffer upon receiving a PRIVMSG.
 This includes PRIVMSGs directed to channels.  If you are using an IRC
 bouncer, such as dircproxy, to keep a log of channels when you are
 disconnected, you should set this option to t.
 
+For queries (direct messages), this option's non-nil meaning is
+straightforward: if a buffer doesn't exist for the sender, create
+one.  For channels, the use case is more niche and usually
+involves receiving playback (via commands like ZNC's
+\"PLAYBUFFER\") for channels to which your bouncer is joined but
+from which you've \"detached\".
+
 Note that this option was absent from ERC 5.5 because knowledge
 of its intended role was \"unavailable\" during a major
 refactoring involving buffer management.  The option has since
-been restored in ERC 5.6 with the caveat that its purpose is
-assumed to more or less reflect the following, which can be
-thought of as an updated version of the paragraph above:
-
-If non-nil, create a channel buffer on receiving a PRIVMSG if
-none exists.  This only affects messages targeting a channel
-whose buffer was killed but to which you're still joined or one
-whose buffer never existed, presumably because a bouncer is
-sending playback summoned with a command similar to ZNC's
-PLAYBUFFER."
+been restored in ERC 5.6 but now also affects queries in the
+manner implied above, which was lost sometime before ERC 5.4."
+  :package-version '(ERC . "5.6") ; revived
+  :group 'erc-buffers
   :group 'erc-query
   :type 'boolean)
 
diff --git a/test/lisp/erc/erc-scenarios-base-attach.el b/test/lisp/erc/erc-scenarios-base-attach.el
index 91763842879..ccf5d1f9582 100644
--- a/test/lisp/erc/erc-scenarios-base-attach.el
+++ b/test/lisp/erc/erc-scenarios-base-attach.el
@@ -61,9 +61,9 @@
   (let ((load-path (cons (ert-resource-directory) load-path)))
     (require 'erc-scenarios-common)))
 
-(ert-deftest erc-scenarios-base-attach--query-on-unjoined-enabled ()
+(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--enabled ()
   :tags '(:expensive-test)
-  (should erc-query-on-unjoined-chan-privmsg)
+  (should erc-ensure-target-buffer-on-privmsg)
 
   (erc-scenarios-common-with-cleanup
       ((erc-scenarios-common-dialog "base/channel-buffer-revival")
@@ -103,16 +103,16 @@ erc-scenarios-base-attach--query-on-unjoined-enabled
         (erc-scenarios-common-say "bob: hi")
         (funcall expect 10 "Pawn me to this")))))
 
-(ert-deftest erc-scenarios-base-attach--query-on-unjoined-disabled ()
+(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--disabled ()
   :tags '(:expensive-test)
-  (should erc-query-on-unjoined-chan-privmsg)
+  (should erc-ensure-target-buffer-on-privmsg)
 
   (erc-scenarios-common-with-cleanup
       ((erc-scenarios-common-dialog "base/channel-buffer-revival")
        (dumb-server (erc-d-run "localhost" t 'reattach))
        (port (process-contact dumb-server :service))
        (erc-server-flood-penalty 0.1)
-       (erc-query-on-unjoined-chan-privmsg nil) ; off
+       (erc-ensure-target-buffer-on-privmsg nil) ; off
        (expect (erc-d-t-make-expecter)))
 
     (ert-info ("Connect to foonet")
@@ -144,4 +144,48 @@ erc-scenarios-base-attach--query-on-unjoined-disabled
         (erc-scenarios-common-say "bob: hi")
         (funcall expect 10 "Pawn me to this")))))
 
+
+;; We omit the `enabled' case for queries because it's the default for
+;; this option and already covered many times over by other tests in
+;; this directory.
+
+(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--disabled-query ()
+  :tags '(:expensive-test)
+  (should erc-ensure-target-buffer-on-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/assoc/queries")
+       (dumb-server (erc-d-run "localhost" t 'non-erc))
+       (port (process-contact dumb-server :service))
+       (expect (erc-d-t-make-expecter))
+       (erc-ensure-target-buffer-on-privmsg nil)
+       (erc-server-flood-penalty 0.1))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :user "tester"
+                                :full-name "tester")
+        (erc-scenarios-common-assert-initial-buf-name nil port)
+        (erc-d-t-wait-for 5 (eq erc-network 'foonet))
+        (funcall expect 15 "debug mode")))
+
+    (ert-info ("User dummy's greeting appears in server buffer")
+      (erc-d-t-wait-for -1 (get-buffer "dummy"))
+      (with-current-buffer "foonet"
+        (funcall expect 5 "hi")
+
+        (ert-info ("Option being nil doesn't queries we create")
+          (with-current-buffer (erc-cmd-QUERY "nitwit")
+            (should (equal (buffer-name) "nitwit"))
+            (erc-scenarios-common-say "hola")
+            (funcall expect 5 "ciao")))
+
+        (erc-scenarios-common-say "howdy")
+        (funcall expect 5 "no target")
+        (erc-cmd-MSG "dummy howdy")
+        (funcall expect 5 "bye")
+        (erc-cmd-QUIT "")))))
+
 ;;; erc-scenarios-base-attach.el ends here
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-5.6-Revive-option-erc-query-on-unjoined-chan-privmsg.patch --]
[-- Type: text/x-patch, Size: 23811 bytes --]

From 71f41ca13c61d8f8ba5f2d824e9cb1ae89e8e6ef Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Thu, 13 Apr 2023 00:00:02 -0700
Subject: [PATCH 1/4] [5.6] Revive option erc-query-on-unjoined-chan-privmsg

* etc/ERC-NEWS: Mention reinstated option
`erc-query-on-unjoined-chan-privmsg'.
* lisp/erc/erc-backend.el (erc-server-PRIVMSG): Consider new option
`erc-receive-query-display-defer' and revived option
`erc-query-unjoined-chan-privmsg' when deciding whether to create a
new query buffer.  And only "open" a buffer for an unknown target when
`erc-query-on-unjoined-chan-privmsg' is non-nil.
* lisp/erc/erc.el (erc-query): Fix deprecation message.
(erc-auto-query, erc-receive-query-display): Swap alias and aliased
and add option to `erc-buffers' group.  Mention the nonstandard
meaning of nil for this option.
(erc-receive-query-display-defer): Add compatibility switch to access
legacy behavior for `erc-auto-query'.
(erc-query-on-unjoined-chan-privmsg): Revise doc string.
* test/lisp/erc/erc-scenarios-base-attach.el: New file.
* test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld:
New file.  (Bug#62833)
---
 etc/ERC-NEWS                                  |  21 +-
 lisp/erc/erc-backend.el                       |  19 +-
 lisp/erc/erc.el                               |  63 ++++--
 test/lisp/erc/erc-scenarios-base-attach.el    | 191 ++++++++++++++++++
 .../base/channel-buffer-revival/reattach.eld  |  56 +++++
 5 files changed, 326 insertions(+), 24 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-attach.el
 create mode 100644 test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 8f1b89f268b..2ee88a80351 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -45,9 +45,16 @@ security issue led to new ERC buffers being "buried" on creation.  On
 further reflection, this was judged to have been an overcorrection in
 the case of interactive invocations, hence the new option
 'erc-interactive-display', which is set to 'buffer' (i.e., "take me
-there") by default.  Accompanying this addition are "display"-suffixed
-aliases for related options 'erc-join-buffer' and 'erc-auto-query',
-which users have reported as being difficult to discover and remember.
+there") by default.
+
+Accompanying this addition are "display"-suffixed aliases for related
+options 'erc-join-buffer' and 'erc-auto-query', which users have
+reported as being difficult to discover and remember.  As for the
+latter option specifically (now known as 'erc-receive-query-display'),
+assigning it a nil value now tells ERC to use 'erc-join-buffer' in its
+place, much like with related buffer-display options, like
+'erc-interactive-display'.  The old nil behavior can still be gotten
+via a new compatibility flag, 'erc-receive-query-display-defer'.
 
 ** Setting a module's mode variable via Customize earns a warning.
 Trying and failing to activate a module via its minor mode's Custom
@@ -108,6 +115,14 @@ other than the symbol 'erc-button-buttonize-nicks' appearing in the
 "FORM" field (third element) of this entry are considered deprecated
 and will incur a warning.
 
+** The option 'erc-query-on-unjoined-chan-privmsg' has been restored.
+(Although it's now called 'erc-ensure-target-buffer-on-privmsg'.)
+In ERC 5.5, this option was removed from the default client code and
+thus prevented from influencing PRIVMSG handling because its precise
+purpose could not be determined with any confidence.  After some
+consideration, it's now been wired back in with a slightly revised
+role contingent on a few assumptions explained in its doc string.
+
 ** Miscellaneous UX changes.
 Some minor quality-of-life niceties have finally made their way to
 ERC.  For example, the function 'erc-echo-timestamp' is now
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index bdf4e2ddca2..98a1c117cfa 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -102,11 +102,11 @@
 (require 'erc-common)
 
 (defvar erc--target)
-(defvar erc-auto-query)
 (defvar erc-channel-list)
 (defvar erc-channel-users)
 (defvar erc-default-nicks)
 (defvar erc-default-recipients)
+(defvar erc-ensure-target-buffer-on-privmsg)
 (defvar erc-format-nick-function)
 (defvar erc-format-query-as-channel-p)
 (defvar erc-hide-prompt)
@@ -123,6 +123,8 @@ erc-nick
 (defvar erc-nick-change-attempt-count)
 (defvar erc-prompt-for-channel-key)
 (defvar erc-prompt-hidden)
+(defvar erc-receive-query-display)
+(defvar erc-receive-query-display-defer)
 (defvar erc-reuse-buffers)
 (defvar erc-verbose-server-ping)
 (defvar erc-whowas-on-nosuchnick)
@@ -1831,11 +1833,16 @@ define-erc-response-handler
         (unless (or buffer noticep (string-empty-p tgt) (eq ?$ (aref tgt 0))
                     (erc-is-message-ctcp-and-not-action-p msg))
           (if privp
-              (when erc-auto-query
-                (let ((erc-join-buffer erc-auto-query))
-                  (setq buffer (erc--open-target nick))))
-            ;; A channel buffer has been killed but is still joined
-            (setq buffer (erc--open-target tgt))))
+              (when-let ((erc-join-buffer
+                          (or (and (not erc-receive-query-display-defer)
+                                   erc-receive-query-display)
+                              (and erc-ensure-target-buffer-on-privmsg
+                                   (or erc-receive-query-display
+                                       erc-join-buffer)))))
+                (setq buffer (erc--open-target nick)))
+            ;; A channel buffer has been killed but is still joined.
+            (when erc-ensure-target-buffer-on-privmsg
+              (setq buffer (erc--open-target tgt)))))
         (when buffer
           (with-current-buffer buffer
             (when privp (erc--unhide-prompt))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 284990e2d43..1e15f3d4280 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -3978,8 +3978,8 @@ erc-cmd-QUERY
   (unless user
       ;; currently broken, evil hack to display help anyway
                                         ;(erc-delete-query))))
-    (signal 'wrong-number-of-arguments ""))
-  (let ((erc-join-buffer erc-query-display))
+    (signal 'wrong-number-of-arguments '(erc-cmd-QUERY 0)))
+  (let ((erc-join-buffer erc-interactive-display))
     (erc-with-server-buffer
      (erc--open-target user))))
 
@@ -4722,23 +4722,30 @@ erc-query
   "Open a query buffer on TARGET using SERVER-BUFFER.
 To change how this query window is displayed, use `let' to bind
 `erc-join-buffer' before calling this."
-  (declare (obsolete "bind `erc-cmd-query' and call `erc-cmd-QUERY'" "29.1"))
+  (declare (obsolete "call `erc-open' in a live server buffer" "29.1"))
   (unless (buffer-live-p server-buffer)
     (error "Couldn't switch to server buffer"))
   (with-current-buffer server-buffer
     (erc--open-target target)))
 
-(defvaralias 'erc-receive-query-display 'erc-auto-query)
-(defcustom erc-auto-query 'window-noselect
+(defvaralias 'erc-auto-query 'erc-receive-query-display)
+(defcustom erc-receive-query-display 'window-noselect
   "If non-nil, create a query buffer each time you receive a private message.
 If the buffer doesn't already exist, it is created.
 
 This can be set to a symbol, to control how the new query window
 should appear.  The default behavior is to display the buffer in
-a new window, but not to select it.  See the documentation for
-`erc-join-buffer' for a description of the available choices."
+a new window but not to select it.  See the documentation for
+`erc-buffer-display' for a description of available values.
+
+Note that the legacy behavior of forgoing buffer creation
+entirely when this option is nil requires setting the
+compatibility flag `erc-receive-query-display-defer' to nil.  Use
+`erc-ensure-target-buffer-on-privmsg' to achieve the same effect."
+  :package-version '(ERC . "5.6")
+  :group 'erc-buffers
   :group 'erc-query
-  :type '(choice (const :tag "Don't create query window" nil)
+  :type '(choice (const :tag "Defer to value of `erc-buffer-display'" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
@@ -4746,15 +4753,41 @@ erc-auto-query
                  (const :tag "Use current buffer" buffer)
                  (const :tag "Use current buffer" t)))
 
-;; FIXME either retire this or put it to use after determining how
-;; it's meant to work.  Clearly, the doc string does not describe
-;; current behavior.  It's currently only used by the obsolete
-;; function `erc-auto-query'.
-(defcustom erc-query-on-unjoined-chan-privmsg t
-  "If non-nil create query buffer on receiving any PRIVMSG at all.
+(defcustom erc-receive-query-display-defer t
+  "How to interpret a null `erc-receive-query-display'.
+When this option is non-nil, ERC defers to `erc-buffer-display'
+upon seeing a nil value for `erc-receive-query-display', much
+like it does with other buffer-display options, like
+`erc-interactive-display'.  Otherwise, when this option is nil,
+ERC retains the legacy behavior of not creating a new query
+buffer."
+  :package-version '(ERC . "5.6")
+  :group 'erc-buffers
+  :group 'erc-query
+  :type 'boolean)
+
+(defvaralias 'erc-query-on-unjoined-chan-privmsg
+  'erc-ensure-target-buffer-on-privmsg)
+(defcustom erc-ensure-target-buffer-on-privmsg t
+  "When non-nil, create a target buffer upon receiving a PRIVMSG.
 This includes PRIVMSGs directed to channels.  If you are using an IRC
 bouncer, such as dircproxy, to keep a log of channels when you are
-disconnected, you should set this option to t."
+disconnected, you should set this option to t.
+
+For queries (direct messages), this option's non-nil meaning is
+straightforward: if a buffer doesn't exist for the sender, create
+one.  For channels, the use case is more niche and usually
+involves receiving playback (via commands like ZNC's
+\"PLAYBUFFER\") for channels to which your bouncer is joined but
+from which you've \"detached\".
+
+Note that this option was absent from ERC 5.5 because knowledge
+of its intended role was \"unavailable\" during a major
+refactoring involving buffer management.  The option has since
+been restored in ERC 5.6 but now also affects queries in the
+manner implied above, which was lost sometime before ERC 5.4."
+  :package-version '(ERC . "5.6") ; revived
+  :group 'erc-buffers
   :group 'erc-query
   :type 'boolean)
 
diff --git a/test/lisp/erc/erc-scenarios-base-attach.el b/test/lisp/erc/erc-scenarios-base-attach.el
new file mode 100644
index 00000000000..ccf5d1f9582
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-base-attach.el
@@ -0,0 +1,191 @@
+;;; erc-scenarios-base-attach.el --- Reattach scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See also: `erc-scenarios-base-channel-buffer-revival'.
+;;
+;; ERC 5.5 silently dropped support for the ancient option
+;; `erc-query-on-unjoined-chan-privmsg' because the tangled logic in
+;; and around the function `erc-auto-query' made it difficult to
+;; divine its purpose.
+;;
+;; Based on the name, it was thought this option likely involved
+;; controlling the creation of query buffers for unsolicited messages
+;; from users with whom you don't share a common channel.  However,
+;; additional spelunking has recently revealed that it was instead
+;; meant to service a feature offered by most bouncers that sends
+;; PRIVMSGs directed at a channel you're no longer in and that you
+;; haven't received a(nother) JOIN message for.  IOW, this is meant to
+;; support the following sequence of events:
+;;
+;;   1. /detach #chan
+;;   2. kill buffer #chan or reconnect in new Emacs session
+;;   3. /playbuffer #chan
+;;
+;; Note that the above slash commands are bouncer-specific aliases.
+;;
+;; Interested users can find more info by looking at this change set
+;; from the ancient CVS repo:
+;;
+;;   Author:     Mario Lang <mlang@delysid.org>
+;;   AuthorDate: Mon Nov 26 18:33:19 2001 +0000
+;;
+;;   * new function erc-BBDB-NICK to handle nickname anotation ...
+;;   * Applied antifuchs/mhp patches, the latest on erc-help, unmodified
+;;   * New variable: erc-reuse-buffers default to t.
+;;   * Modified erc-generate-new-buffer-name to use it. it checks if
+;;     server and port are the same, then one can assume thats the same
+;;     channel/query target again.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--enabled ()
+  :tags '(:expensive-test)
+  (should erc-ensure-target-buffer-on-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/channel-buffer-revival")
+       (dumb-server (erc-d-run "localhost" t 'reattach))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "tester@vanilla/foonet:changeme"
+                                :full-name "tester")
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet"))
+      (erc-cmd-MSG "*status playbuffer #chan"))
+
+    (ert-info ("Playback appears in buffer #chan")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "Was I a child")
+        (funcall expect 10 "Thou counterfeit'st most lively")
+        (funcall expect 10 "Playback Complete")))
+
+    (with-current-buffer "foonet"
+      (erc-cmd-MSG "*status attach #chan"))
+
+    (ert-info ("Live output from #chan after more playback")
+      (with-current-buffer "#chan"
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "With what it loathes")
+        (funcall expect 10 "Not by his breath")
+        (funcall expect 10 "Playback Complete")
+        (funcall expect 10 "Ay, and the captain")
+        (erc-scenarios-common-say "bob: hi")
+        (funcall expect 10 "Pawn me to this")))))
+
+(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--disabled ()
+  :tags '(:expensive-test)
+  (should erc-ensure-target-buffer-on-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/channel-buffer-revival")
+       (dumb-server (erc-d-run "localhost" t 'reattach))
+       (port (process-contact dumb-server :service))
+       (erc-server-flood-penalty 0.1)
+       (erc-ensure-target-buffer-on-privmsg nil) ; off
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "tester@vanilla/foonet:changeme"
+                                :full-name "tester")
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet"))
+      (erc-cmd-MSG "*status playbuffer #chan")
+      (ert-info ("Playback appears in buffer server buffer")
+        (erc-d-t-ensure-for -1 (not (get-buffer "#chan")))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "Was I a child")
+        (funcall expect 10 "Thou counterfeit'st most lively")
+        (funcall expect 10 "Playback Complete"))
+      (should-not (get-buffer "#chan"))
+      (erc-cmd-MSG "*status attach #chan"))
+
+    (ert-info ("Buffer #chan joined")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "Buffer Playback...")
+        (funcall expect 10 "With what it loathes")
+        (funcall expect 10 "Not by his breath")
+        (funcall expect 10 "Playback Complete")
+        (funcall expect 10 "Ay, and the captain")
+        (erc-scenarios-common-say "bob: hi")
+        (funcall expect 10 "Pawn me to this")))))
+
+
+;; We omit the `enabled' case for queries because it's the default for
+;; this option and already covered many times over by other tests in
+;; this directory.
+
+(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--disabled-query ()
+  :tags '(:expensive-test)
+  (should erc-ensure-target-buffer-on-privmsg)
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/assoc/queries")
+       (dumb-server (erc-d-run "localhost" t 'non-erc))
+       (port (process-contact dumb-server :service))
+       (expect (erc-d-t-make-expecter))
+       (erc-ensure-target-buffer-on-privmsg nil)
+       (erc-server-flood-penalty 0.1))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :user "tester"
+                                :full-name "tester")
+        (erc-scenarios-common-assert-initial-buf-name nil port)
+        (erc-d-t-wait-for 5 (eq erc-network 'foonet))
+        (funcall expect 15 "debug mode")))
+
+    (ert-info ("User dummy's greeting appears in server buffer")
+      (erc-d-t-wait-for -1 (get-buffer "dummy"))
+      (with-current-buffer "foonet"
+        (funcall expect 5 "hi")
+
+        (ert-info ("Option being nil doesn't queries we create")
+          (with-current-buffer (erc-cmd-QUERY "nitwit")
+            (should (equal (buffer-name) "nitwit"))
+            (erc-scenarios-common-say "hola")
+            (funcall expect 5 "ciao")))
+
+        (erc-scenarios-common-say "howdy")
+        (funcall expect 5 "no target")
+        (erc-cmd-MSG "dummy howdy")
+        (funcall expect 5 "bye")
+        (erc-cmd-QUIT "")))))
+
+;;; erc-scenarios-base-attach.el ends here
diff --git a/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld
new file mode 100644
index 00000000000..c3791ac3d49
--- /dev/null
+++ b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld
@@ -0,0 +1,56 @@
+;; -*- mode: lisp-data; -*-
+((pass 10 "PASS :tester@vanilla/foonet:changeme"))
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
+ (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0.00 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1")
+ (0.00 ":irc.foonet.org 003 tester :This server was created Thu, 13 Apr 2023 05:55:22 UTC")
+ (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server")
+ (0.00 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server")
+ (0.00 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server")
+ (0.00 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)")
+ (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0.00 ":irc.foonet.org 254 tester 1 :channels formed")
+ (0.00 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers")
+ (0.00 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3")
+ (0.00 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
+ (0.00 ":irc.foonet.org 422 tester :MOTD File is missing"))
+
+((mode 10 "MODE tester +i")
+ (0.01 ":irc.foonet.org 221 tester +Zi"))
+
+((privmsg-play 10 "PRIVMSG *status :playbuffer #chan")
+ (0.05 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:24] alice: Was I a child, to fear I know not what.")
+ (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:29] bob: My lord, I do confess the ring was hers.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:40] alice: My sons would never so dishonour me.")
+ (0.01 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:54] bob: By the hand of a soldier, I will undertake it.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:57] alice: Thou counterfeit'st most lively.")
+ (0.01 ":***!znc@znc.in PRIVMSG #chan :Playback Complete."))
+
+((privmsg-attach 10 "PRIVMSG *status :attach #chan")
+ (0.01 ":tester!~u@78a58pgahbr24.irc JOIN #chan"))
+
+((mode-chan 10 "MODE #chan")
+ (0.01 ":irc.foonet.org 353 tester = #chan :@alice bob tester")
+ (0.00 ":irc.foonet.org 366 tester #chan :End of /NAMES list.")
+ (0.00 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...")
+ (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:01] bob: With what it loathes for that which is away.")
+ (0.00 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:30] alice: Ties up my tongue, and will not let me speak.")
+ (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:26] bob: They say he is already in the forest of Arden, and a many merry men with him; and there they live like the old Robin Hood of England. They say many young gentlemen flock to him every day, and fleet the time carelessly, as they did in the golden world.")
+ (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:29] alice: Not by his breath that is more miserable.")
+ (0.00 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")
+ (0.00 ":*status!znc@znc.in PRIVMSG tester :There was 1 channel matching [#chan]")
+ (0.03 ":*status!znc@znc.in PRIVMSG tester :Attached 1 channel")
+ (0.00 ":irc.foonet.org 324 tester #chan +Cnt")
+ (0.00 ":irc.foonet.org 329 tester #chan 1681365340")
+ (0.03 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Five or six thousand horse, I said,I will say true,or thereabouts, set down, for I'll speak truth.")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Riddling confession finds but riddling shrift.")
+ (0.04 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Ay, and the captain of his horse, Count Rousillon."))
+
+((privmsg-bob 10 "PRIVMSG #chan :bob: hi")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: But thankful even for hate, that is meant love.")
+ (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :tester: Come, come, elder brother, you are too young in this.")
+ (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Sir, we have known together in Orleans.")
+ (0.05 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Pawn me to this your honour, she is his."))
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0002-5.6-Move-ERC-s-buffer-display-tests-to-separate-file.patch --]
[-- Type: text/x-patch, Size: 9905 bytes --]

From 186ee38aca3da61419a8e7daa9b089ec4db100d0 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Thu, 20 Apr 2023 19:23:54 -0700
Subject: [PATCH 2/4] [5.6] Move ERC's buffer-display tests to separate file

* test/lisp/erc/erc-scenarios-base-buffer-display.el: New file.
* test/lisp/erc/erc-scenarios-base-reconnect.el
(erc-scenarios-common--base-reconnect-options,
erc-scenarios-base-reconnect-options--buffer,
erc-scenarios-base-reconnect-options--default): Move to new file and
rename.  (Bug#62833)
---
 .../erc/erc-scenarios-base-buffer-display.el  | 121 ++++++++++++++++++
 test/lisp/erc/erc-scenarios-base-reconnect.el |  89 -------------
 2 files changed, 121 insertions(+), 89 deletions(-)
 create mode 100644 test/lisp/erc/erc-scenarios-base-buffer-display.el

diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
new file mode 100644
index 00000000000..d511c8ff738
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -0,0 +1,121 @@
+;;; erc-scenarios-base-buffer-display.el --- Buffer display scenarios -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(eval-when-compile (require 'erc-join))
+
+;; These first couple `erc-reconnect-display' tests used to live in
+;; erc-scenarios-base-reconnect but have since been renamed.
+
+(defun erc-scenarios-base-buffer-display--reconnect-common (test)
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/reconnect")
+       (dumb-server (erc-d-run "localhost" t 'options 'options-again))
+       (port (process-contact dumb-server :service))
+       (expect (erc-d-t-make-expecter))
+       (erc-server-flood-penalty 0.1)
+       (erc-server-auto-reconnect t)
+       erc-autojoin-channels-alist
+       erc-server-buffer)
+
+    (should (memq 'autojoin erc-modules))
+
+    (ert-info ("Connect to foonet")
+      (setq erc-server-buffer (erc :server "127.0.0.1"
+                                   :port port
+                                   :nick "tester"
+                                   :password "changeme"
+                                   :full-name "tester"))
+      (with-current-buffer erc-server-buffer
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
+        (funcall expect 10 "debug mode")))
+
+    (ert-info ("Wait for some output in channels")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "welcome")))
+
+    (ert-info ("Server buffer shows connection failed")
+      (with-current-buffer erc-server-buffer
+        (funcall expect 10 "Connection failed!  Re-establishing")))
+
+    (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
+
+    (funcall test)
+
+    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
+    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
+
+    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
+      (not (eq (window-buffer) (get-buffer "#spam"))))
+
+    (ert-info ("Wait for auto reconnect")
+      (with-current-buffer erc-server-buffer
+        (funcall expect 10 "still in debug mode")))
+
+    (ert-info ("Wait for activity to recommence in channels")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "forest of Arden"))
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+        (funcall expect 10 "her elves come here anon")))))
+
+(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should-not erc-reconnect-display)
+
+  ;; FooNet (the server buffer) is not switched to because it's
+  ;; already current (but not shown) when `erc-open' is called.  See
+  ;; related conditional guard towards the end of that function.
+
+  (let ((erc-reconnect-display 'buffer))
+    (erc-scenarios-base-buffer-display--reconnect-common
+     (lambda ()
+       (pop-to-buffer-same-window "*Messages*")
+
+       (erc-d-t-ensure-for 1 "Server buffer not shown"
+         (not (eq (window-buffer) (get-buffer "FooNet"))))
+
+       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
+         (eq (window-buffer) (get-buffer "#chan")))))))
+
+(ert-deftest erc-scenarios-base-reconnect-options--default ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should-not erc-reconnect-display)
+
+  (erc-scenarios-base-buffer-display--reconnect-common
+
+   (lambda ()
+     (pop-to-buffer-same-window "*Messages*")
+
+     (erc-d-t-ensure-for 1 "Server buffer not shown"
+       (not (eq (window-buffer) (get-buffer "FooNet"))))
+
+     (erc-d-t-ensure-for 3 "Channel #chan not shown"
+       (not (eq (window-buffer) (get-buffer "#chan"))))
+
+     (should (eq (window-buffer) (messages-buffer))))))
+
+;;; erc-scenarios-base-buffer-display.el ends here
diff --git a/test/lisp/erc/erc-scenarios-base-reconnect.el b/test/lisp/erc/erc-scenarios-base-reconnect.el
index 5b4dc549042..7bd16d1ed14 100644
--- a/test/lisp/erc/erc-scenarios-base-reconnect.el
+++ b/test/lisp/erc/erc-scenarios-base-reconnect.el
@@ -65,95 +65,6 @@ erc-scenarios-base-reconnect-timer
       (should (equal (list (get-buffer (format "127.0.0.1:%d" port)))
                      (erc-scenarios-common-buflist "127.0.0.1"))))))
 
-(defun erc-scenarios-common--base-reconnect-options (test)
-  (erc-scenarios-common-with-cleanup
-      ((erc-scenarios-common-dialog "base/reconnect")
-       (dumb-server (erc-d-run "localhost" t 'options 'options-again))
-       (port (process-contact dumb-server :service))
-       (expect (erc-d-t-make-expecter))
-       (erc-server-flood-penalty 0.1)
-       (erc-server-auto-reconnect t)
-       erc-autojoin-channels-alist
-       erc-server-buffer)
-
-    (should (memq 'autojoin erc-modules))
-
-    (ert-info ("Connect to foonet")
-      (setq erc-server-buffer (erc :server "127.0.0.1"
-                                   :port port
-                                   :nick "tester"
-                                   :password "changeme"
-                                   :full-name "tester"))
-      (with-current-buffer erc-server-buffer
-        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
-        (funcall expect 10 "debug mode")))
-
-    (ert-info ("Wait for some output in channels")
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
-        (funcall expect 10 "welcome")))
-
-    (ert-info ("Server buffer shows connection failed")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "Connection failed!  Re-establishing")))
-
-    (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
-
-    (funcall test)
-
-    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
-    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
-
-    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
-      (not (eq (window-buffer) (get-buffer "#spam"))))
-
-    (ert-info ("Wait for auto reconnect")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "still in debug mode")))
-
-    (ert-info ("Wait for activity to recommence in channels")
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
-        (funcall expect 10 "forest of Arden"))
-      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
-        (funcall expect 10 "her elves come here anon")))))
-
-(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
-
-  ;; FooNet (the server buffer) is not switched to because it's
-  ;; already current (but not shown) when `erc-open' is called.  See
-  ;; related conditional guard towards the end of that function.
-
-  (let ((erc-reconnect-display 'buffer))
-    (erc-scenarios-common--base-reconnect-options
-     (lambda ()
-       (pop-to-buffer-same-window "*Messages*")
-
-       (erc-d-t-ensure-for 1 "Server buffer not shown"
-         (not (eq (window-buffer) (get-buffer "FooNet"))))
-
-       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
-         (eq (window-buffer) (get-buffer "#chan")))))))
-
-(ert-deftest erc-scenarios-base-reconnect-options--default ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
-
-  (erc-scenarios-common--base-reconnect-options
-
-   (lambda ()
-     (pop-to-buffer-same-window "*Messages*")
-
-     (erc-d-t-ensure-for 1 "Server buffer not shown"
-       (not (eq (window-buffer) (get-buffer "FooNet"))))
-
-     (erc-d-t-ensure-for 3 "Channel #chan not shown"
-       (not (eq (window-buffer) (get-buffer "#chan"))))
-
-     (eq (window-buffer) (messages-buffer)))))
-
 ;; Upon reconnecting, playback for channel and target buffers is
 ;; routed correctly.  Autojoin is irrelevant here, but for the
 ;; skeptical, see `erc-scenarios-common--join-network-id', which
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: 0003-5.6-Extend-erc-interactive-display-to-cover-JOINs.patch --]
[-- Type: text/x-patch, Size: 15993 bytes --]

From 6995cc5aab16280d0b4a00ec60277531d81591dc Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 10 Apr 2023 17:58:05 -0700
Subject: [PATCH 3/4] [5.6] Extend erc-interactive-display to cover /JOINs

* lisp/erc/erc.el (erc-display): Mention that buffer-related display
options live in the customization group `erc-buffers'.
(erc-buffer-display, erc-join-buffer): Swap alias and aliased so that
the favored name, `erc-buffer-display', appears in the definition and
in the Customize menu.  Also note related buffer-display options in
the doc string.
(erc-query-display, erc-interactive-display): Make the former an alias
for the latter because their roles were functionally redundant and
thus confusing.  Inherit the default value from `erc-query-display'
because users are more familiar with the pop-up window behavior than a
single-window replacement.
(erc-reconnect-display): Use preferred name for cross-referencing
fallback option `erc-buffer-display' in doc string and explain how
/reconnect handling differs.
(erc--called-as-input-p): New variable for "slash" commands, like
`erc-cmd-FOO', to detect whether they're being called "interactively"
as a result of input given at ERC's prompt.
(erc-process-input-line): Bind `erc--called-as-input-p' when running
slash commands.
(erc-cmd-JOIN): When called interactively, schedule a callback to wrap
the response handler and control how new buffers are thus displayed.
(erc-cmd-QUERY): Use preferred alias for `erc-query-display'.
* test/lisp/erc/erc-scenarios-base-buffer-display.el:
(erc-scenarios-base-buffer-display--interactive-default): New test.
* test/lisp/erc/erc-tests.el (erc-process-input-line,
erc-select-read-args, erc-tls, erc--interactive): Change expected
default value of `erc-interactive-display' from `buffer' to
`window'.  (Bug#62833)
---
 etc/ERC-NEWS                                  | 17 ++--
 lisp/erc/erc.el                               | 82 +++++++++++--------
 .../erc/erc-scenarios-base-buffer-display.el  | 37 +++++++++
 test/lisp/erc/erc-tests.el                    | 13 +--
 4 files changed, 100 insertions(+), 49 deletions(-)

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 2ee88a80351..f1fac8e5baa 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -37,15 +37,18 @@ decade overdue, this is no longer the case.  Other UX improvements in
 this area aim to make the process of connecting interactively slightly
 more streamlined and less repetitive, even for veteran users.
 
-** New buffer-display option 'erc-interactive-display'.
+** Revised buffer-display handling for interactive commands.
 A point of friction for new users and one only just introduced with
 ERC 5.5 has been the lack of visual feedback when first connecting via
-M-x erc.  As explained below in the news for 5.5, the discovery of a
-security issue led to new ERC buffers being "buried" on creation.  On
-further reflection, this was judged to have been an overcorrection in
-the case of interactive invocations, hence the new option
-'erc-interactive-display', which is set to 'buffer' (i.e., "take me
-there") by default.
+M-x erc or when issuing a "/JOIN" command at the prompt.  As explained
+below, in the news for 5.5, the discovery of a security issue led to
+most new ERC buffers being "buried" on creation.  On further
+reflection, this was judged to have been an overcorrection in the case
+of interactive invocations, hence the borrowing of an old option,
+'erc-query-display', and the bestowing of a new alias,
+'erc-interactive-display', which better describes its expanded role as
+a more general buffer-display knob for interactive commands ("/QUERY"
+still among them).
 
 Accompanying this addition are "display"-suffixed aliases for related
 options 'erc-join-buffer' and 'erc-auto-query', which users have
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 1e15f3d4280..aee4f983beb 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -98,7 +98,9 @@ erc-buffers
   :group 'erc)
 
 (defgroup erc-display nil
-  "Settings for how various things are displayed."
+  "Settings controlling how various things are displayed.
+See the customization group `erc-buffers' for display options
+concerning buffers."
   :group 'erc)
 
 (defgroup erc-mode-line-and-header nil
@@ -1507,9 +1509,9 @@ erc-default-port-tls
   "IRC port to use for encrypted connections if it cannot be \
 detected otherwise.")
 
-(defvaralias 'erc-buffer-display 'erc-join-buffer)
-(defcustom erc-join-buffer 'bury
-  "Determines how to display a newly created IRC buffer.
+(defvaralias 'erc-join-buffer 'erc-buffer-display)
+(defcustom erc-buffer-display 'bury
+  "How to display a newly created or reassociated ERC buffer.
 
 The available choices are:
 
@@ -1518,7 +1520,9 @@ erc-join-buffer
   `frame'           - in another frame,
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
-  any other value  - in place of the current buffer."
+
+See related options `erc-interactive-display',
+`erc-reconnect-display', and `erc-receive-query-display'."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type '(choice (const :tag "Split window and select" window)
@@ -1528,13 +1532,17 @@ erc-join-buffer
                  (const :tag "Use current buffer" buffer)
                  (const :tag "Use current buffer" t)))
 
-(defcustom erc-interactive-display 'buffer
-  "How and whether to display server buffers for M-x erc.
-See `erc-buffer-display' and friends for a description of
-possible values."
+(defvaralias 'erc-query-display 'erc-interactive-display)
+(defcustom erc-interactive-display 'window
+  "How to display buffers as a result of user interaction.
+This affects commands like /QUERY and /JOIN when issued
+interactively at the prompt.  It does not apply when calling a
+handler for such a command, like `erc-cmd-JOIN', from lisp code.
+See `erc-buffer-display' for a full description of available
+values."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-join-buffer'" nil)
+  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
@@ -1542,15 +1550,14 @@ erc-interactive-display
                  (const :tag "Use current buffer" buffer)))
 
 (defcustom erc-reconnect-display nil
-  "How (and whether) to display a channel buffer upon reconnecting.
-
-This only affects automatic reconnections and is ignored when
-issuing a /reconnect command or reinvoking `erc-tls' with the
-same args (assuming success, of course).  See `erc-join-buffer'
-for a description of possible values."
+  "How and whether to display a channel buffer when auto-reconnecting.
+This only affects automatic reconnections and is ignored, like
+all other buffer-display options, when issuing a /RECONNECT or
+successfully reinvoking `erc-tls' with similar arguments.  See
+`erc-buffer-display' for a description of possible values."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-join-buffer'" nil)
+  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
                  (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
                  (const :tag "New frame" frame)
@@ -3057,6 +3064,10 @@ erc-message-type-member
   (let ((prop-val (erc-get-parsed-vector position)))
     (and prop-val (member (erc-response.command prop-val) list))))
 
+(defvar erc--called-as-input-p nil
+  "Non-nil when a user types a \"/slash\" command.
+Remains bound until `erc-cmd-SLASH' returns.")
+
 (defvar-local erc-send-input-line-function 'erc-send-input-line
   "Function for sending lines lacking a leading user command.
 When a line typed into a buffer contains an explicit command, like /msg,
@@ -3110,7 +3121,8 @@ erc-process-input-line
     (if (and command-list
              (not no-command))
         (let* ((cmd  (nth 0 command-list))
-               (args (nth 1 command-list)))
+               (args (nth 1 command-list))
+               (erc--called-as-input-p t))
           (condition-case nil
               (if (listp args)
                   (apply cmd args)
@@ -3584,6 +3596,21 @@ erc-cmd-JOIN
                    (erc-get-channel-user (erc-current-nick)))))
           (switch-to-buffer existing)
         (setq erc--server-last-reconnect-count 0)
+        (when-let* ; bind `erc-join-buffer' when /JOIN issued
+            ((erc--called-as-input-p)
+             (fn (lambda (proc parsed)
+                   (when-let* ; `fn' wrapper already removed from hook
+                       (((equal (car (erc-response.command-args parsed))
+                                channel))
+                        (sn (erc-extract-nick (erc-response.sender parsed)))
+                        ((erc-nick-equal-p sn (erc-current-nick)))
+                        (erc-join-buffer (or erc-interactive-display
+                                             erc-join-buffer)))
+                     (run-hook-with-args-until-success
+                      'erc-server-JOIN-functions proc parsed)
+                     t))))
+          (erc-with-server-buffer
+            (erc-once-with-server-event "JOIN" fn)))
         (erc-server-join-channel nil chnl key))))
   t)
 
@@ -3947,27 +3974,10 @@ erc-cmd-QUOTE
    (t nil)))
 (put 'erc-cmd-QUOTE 'do-not-parse-args t)
 
-(defcustom erc-query-display 'window
-  "How to display query buffers when using the /QUERY command to talk to someone.
-
-The default behavior is to display the message in a new window
-and bring it to the front.  See the documentation for
-`erc-join-buffer' for a description of the available choices.
-
-See also `erc-auto-query' to decide how private messages from
-other people should be displayed."
-  :group 'erc-query
-  :type '(choice (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
-
 (defun erc-cmd-QUERY (&optional user)
   "Open a query with USER.
 How the query is displayed (in a new window, frame, etc.) depends
-on the value of `erc-query-display'."
+on the value of `erc-interactive-display'."
   ;; FIXME: The doc string used to say at the end:
   ;; "If USER is omitted, close the current query buffer if one exists
   ;; - except this is broken now ;-)"
diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
index d511c8ff738..3ed7a83653e 100644
--- a/test/lisp/erc/erc-scenarios-base-buffer-display.el
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -118,4 +118,41 @@ erc-scenarios-base-reconnect-options--default
 
      (should (eq (window-buffer) (messages-buffer))))))
 
+
+;; This shows that the option `erc-interactive-display' overrides
+;; `erc-join-buffer' during cold opens and interactive /JOINs.
+
+(ert-deftest erc-scenarios-base-buffer-display--interactive-default ()
+  :tags '(:expensive-test)
+  (should (eq erc-join-buffer 'bury))
+  (should (eq erc-interactive-display 'window))
+
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "join/legacy")
+       (dumb-server (erc-d-run "localhost" t 'foonet))
+       (port (process-contact dumb-server :service))
+       (url (format "tester:changeme@127.0.0.1:%d\r\r" port))
+       (expect (erc-d-t-make-expecter))
+       (erc-server-flood-penalty 0.1)
+       (erc-server-auto-reconnect t)
+       (erc-user-full-name "tester"))
+
+    (ert-info ("Connect to foonet")
+      (with-current-buffer (let (inhibit-interaction)
+                             (ert-simulate-keys url
+                               (call-interactively #'erc)))
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
+
+        (erc-d-t-wait-for 10 "Server buffer shown"
+          (eq (window-buffer) (current-buffer)))
+        (funcall expect 10 "debug mode")
+        (erc-scenarios-common-say "/JOIN #chan")))
+
+    (ert-info ("Wait for output in #chan")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "welcome")
+        (erc-d-t-ensure-for 3 "Channel #chan shown"
+          (eq (window-buffer) (current-buffer)))
+        (funcall expect 10 "be prosperous")))))
+
 ;;; erc-scenarios-base-buffer-display.el ends here
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 29bda7e742d..88b9babf206 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1292,6 +1292,7 @@ erc-process-input-line
       (cl-letf (((symbol-function 'erc-cmd-MSG)
                  (lambda (line)
                    (push line calls)
+                   (should erc--called-as-input-p)
                    (funcall orig-erc-cmd-MSG line)))
                 ((symbol-function 'erc-server-buffer)
                  (lambda () (current-buffer)))
@@ -1469,7 +1470,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when port matches default TLS port")
     (should (equal (ert-simulate-keys "irc.gnu.org\r6697\r\r\r"
@@ -1479,7 +1480,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when URL is ircs://")
     (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
@@ -1489,7 +1490,7 @@ erc-select-read-args
                          :nick (user-login-name)
                          '&interactive-env
                          '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . buffer))))))
+                           (erc-join-buffer . window))))))
 
   (setq-local erc-interactive-display nil) ; cheat to save space
 
@@ -1625,7 +1626,7 @@ erc-tls
                        '("localhost" 6667 "nick" "unknown" t "sesame"
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer)
+                       '((erc-join-buffer window)
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Custom connect function")
@@ -1686,7 +1687,7 @@ erc--interactive
                        '("irc.libera.chat" 6697 "tester" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer) (erc-server-connect-function
+                       '((erc-join-buffer window) (erc-server-connect-function
                                                    erc-open-tls-stream)))))
 
       (ert-info ("Nick supplied, decline TLS upgrade")
@@ -1696,7 +1697,7 @@ erc--interactive
                        '("irc.libera.chat" 6667 "dummy" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer buffer)
+                       '((erc-join-buffer window)
                          (erc-server-connect-function
                           erc-open-network-stream))))))))
 
-- 
2.39.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: 0004-5.6-Ignore-erc-reconnect-display-after-a-timeout.patch --]
[-- Type: text/x-patch, Size: 15858 bytes --]

From dd9f78ea159958ccef4eabf3f782d4f29d4d24e7 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Thu, 20 Apr 2023 19:20:59 -0700
Subject: [PATCH 4/4] [5.6] Ignore erc-reconnect-display after a timeout

* lisp/erc/erc-backend.el (erc--server-reconnect-display-timer): New
variable to store active timer that, upon firing, zeroes out
`erc--server-last-reconnect-count'.
(erc--server-last-reconnect-on-disconnect): New function to run on
`erc-disconnected-hook'.
(erc--server-last-reconnect-display-reset): New function that ensures
the reconnect-display window is closed.
* lisp/erc/erc.el (erc-reconnect-display-timeout): New option to
control how long `erc-reconnect-display' affects the displaying of new
or reassociated buffers following an automatic reconnection.
(erc-process-input-line): Ensure user input marks the end of the
reconnect-display window.
(erc-cmd-JOIN): Don't bother resetting
`erc--server-last-reconnect-count' because it's now handled by the
caller, `erc-process-input-line'.
(erc-connection-established): Schedule timer and register hook to
reset last-reconnect count and close the reconnect-display window.
* test/lisp/erc/erc-scenarios-base-buffer-display.el:
(erc-scenarios-base-buffer-display--reconnect-common): Add new args to
test fixture.
(erc-scenarios-base-reconnect-options--buffer,
erc-scenarios-base-buffer-display--defwin-recbury-intbuf): Rename
former to latter and rework to better reflect realistic settings
for the relevant display options.
(erc-scenarios-base-reconnect-options--default,
erc-scenarios-base-buffer-display--defwino-recbury-intbuf): Rename
former to latter and rework to be more realistic.
(erc-scenarios-base-buffer-display--count-reset-timeout): New
test for new option `erc-reconnect-display-timeout'.  (Bug#62833)
---
 lisp/erc/erc-backend.el                       |  22 +++
 lisp/erc/erc.el                               |  15 +-
 .../erc/erc-scenarios-base-buffer-display.el  | 168 +++++++++++++-----
 3 files changed, 159 insertions(+), 46 deletions(-)

diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 98a1c117cfa..d14640e798d 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -299,6 +299,12 @@ erc-server-connected
 (defvar-local erc-server-reconnect-count 0
   "Number of times we have failed to reconnect to the current server.")
 
+(defvar-local erc--server-reconnect-display-timer nil
+  "Timer that resets `erc--server-last-reconnect-count' to zero.
+Becomes non-nil in all server buffers when an IRC connection is
+first \"established\" and carries out its duties
+`erc-reconnect-display-timeout' seconds later.")
+
 (defvar-local erc--server-last-reconnect-count 0
   "Snapshot of reconnect count when the connection was established.")
 
@@ -903,6 +909,22 @@ erc-server-reconnect-p
         erc-server-reconnecting)
       (erc--server-reconnect-p event)))
 
+(defun erc--server-last-reconnect-on-disconnect (&rest _)
+  (remove-hook 'erc-disconnected-hook
+               #'erc--server-last-reconnect-on-disconnect t)
+  (erc--server-last-reconnect-display-reset (current-buffer)))
+
+(defun erc--server-last-reconnect-display-reset (buffer)
+  "Deactivate `erc-reconnect-display'."
+  (when (buffer-live-p buffer)
+    (with-current-buffer buffer
+      (when erc--server-reconnect-display-timer
+        (cancel-timer erc--server-reconnect-display-timer)
+        (remove-hook 'erc-disconnected-hook
+                     #'erc--server-last-reconnect-display-reset t)
+        (setq erc--server-reconnect-display-timer nil
+              erc--server-last-reconnect-count 0)))))
+
 (defconst erc--mode-line-process-reconnecting
   '(:eval (erc-with-server-buffer
             (and erc--server-reconnect-timer
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index aee4f983beb..847e27904b1 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1564,6 +1564,13 @@ erc-reconnect-display
                  (const :tag "Bury in new buffer" bury)
                  (const :tag "Use current buffer" buffer)))
 
+(defcustom erc-reconnect-display-timeout 10
+  "Duration `erc-reconnect-display' remains active.
+The countdown starts on MOTD and is canceled early by any
+\"slash\" command."
+  :type 'integer
+  :group 'erc-buffers)
+
 (defcustom erc-frame-alist nil
   "Alist of frame parameters for creating erc frames.
 A value of nil means to use `default-frame-alist'."
@@ -3123,6 +3130,7 @@ erc-process-input-line
         (let* ((cmd  (nth 0 command-list))
                (args (nth 1 command-list))
                (erc--called-as-input-p t))
+          (erc--server-last-reconnect-display-reset (erc-server-buffer))
           (condition-case nil
               (if (listp args)
                   (apply cmd args)
@@ -3595,7 +3603,6 @@ erc-cmd-JOIN
                 ((with-current-buffer existing
                    (erc-get-channel-user (erc-current-nick)))))
           (switch-to-buffer existing)
-        (setq erc--server-last-reconnect-count 0)
         (when-let* ; bind `erc-join-buffer' when /JOIN issued
             ((erc--called-as-input-p)
              (fn (lambda (proc parsed)
@@ -5204,6 +5211,12 @@ erc-connection-established
         (setq erc-server-connected t)
         (setq erc--server-last-reconnect-count erc-server-reconnect-count
               erc-server-reconnect-count 0)
+        (setq erc--server-reconnect-display-timer
+              (run-at-time erc-reconnect-display-timeout nil
+                           #'erc--server-last-reconnect-display-reset
+                           (current-buffer)))
+        (add-hook 'erc-disconnected-hook
+                  #'erc--server-last-reconnect-on-disconnect nil t)
         (erc-update-mode-line)
         (erc-set-initial-user-mode nick buffer)
         (erc-server-setup-periodical-ping buffer)
diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
index 3ed7a83653e..53a3d7e8ef7 100644
--- a/test/lisp/erc/erc-scenarios-base-buffer-display.el
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -29,7 +29,8 @@
 ;; These first couple `erc-reconnect-display' tests used to live in
 ;; erc-scenarios-base-reconnect but have since been renamed.
 
-(defun erc-scenarios-base-buffer-display--reconnect-common (test)
+(defun erc-scenarios-base-buffer-display--reconnect-common
+    (assert-server assert-chan assert-rest)
   (erc-scenarios-common-with-cleanup
       ((erc-scenarios-common-dialog "base/reconnect")
        (dumb-server (erc-d-run "localhost" t 'options 'options-again))
@@ -37,87 +38,164 @@ erc-scenarios-base-buffer-display--reconnect-common
        (expect (erc-d-t-make-expecter))
        (erc-server-flood-penalty 0.1)
        (erc-server-auto-reconnect t)
-       erc-autojoin-channels-alist
-       erc-server-buffer)
+       erc-autojoin-channels-alist)
 
     (should (memq 'autojoin erc-modules))
 
     (ert-info ("Connect to foonet")
-      (setq erc-server-buffer (erc :server "127.0.0.1"
-                                   :port port
-                                   :nick "tester"
-                                   :password "changeme"
-                                   :full-name "tester"))
-      (with-current-buffer erc-server-buffer
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :nick "tester"
+                                :password "changeme"
+                                :full-name "tester")
+        (funcall assert-server expect)
         (should (string= (buffer-name) (format "127.0.0.1:%d" port)))
         (funcall expect 10 "debug mode")))
 
     (ert-info ("Wait for some output in channels")
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall assert-chan expect)
         (funcall expect 10 "welcome")))
 
     (ert-info ("Server buffer shows connection failed")
-      (with-current-buffer erc-server-buffer
+      (with-current-buffer "FooNet"
         (funcall expect 10 "Connection failed!  Re-establishing")))
 
     (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))
-
-    (funcall test)
-
-    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
-    (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
-
-    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
-      (not (eq (window-buffer) (get-buffer "#spam"))))
+    (delete-other-windows)
+    (pop-to-buffer-same-window "*Messages*")
 
     (ert-info ("Wait for auto reconnect")
-      (with-current-buffer erc-server-buffer
-        (funcall expect 10 "still in debug mode")))
+      (with-current-buffer "FooNet" (funcall expect 10 "still in debug mode")))
 
-    (ert-info ("Wait for activity to recommence in channels")
+    (funcall assert-rest expect)
+
+    (ert-info ("Wait for activity to recommence in both channels")
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
         (funcall expect 10 "forest of Arden"))
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
         (funcall expect 10 "her elves come here anon")))))
 
-(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
+(ert-deftest erc-scenarios-base-buffer-display--defwin-recbury-intbuf ()
   :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
   (should-not erc-reconnect-display)
 
-  ;; FooNet (the server buffer) is not switched to because it's
-  ;; already current (but not shown) when `erc-open' is called.  See
-  ;; related conditional guard towards the end of that function.
+  (let ((erc-buffer-display 'window)
+        (erc-interactive-display 'buffer)
+        (erc-reconnect-display 'bury))
 
-  (let ((erc-reconnect-display 'buffer))
     (erc-scenarios-base-buffer-display--reconnect-common
-     (lambda ()
-       (pop-to-buffer-same-window "*Messages*")
 
-       (erc-d-t-ensure-for 1 "Server buffer not shown"
-         (not (eq (window-buffer) (get-buffer "FooNet"))))
+     (lambda (_)
+       (should (eq (window-buffer) (current-buffer)))
+       (should-not (frame-root-window-p (selected-window))))
 
-       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
-         (eq (window-buffer) (get-buffer "#chan")))))))
+     (lambda (_)
+       (should (eq (window-buffer) (current-buffer)))
+       (should (equal (get-buffer "FooNet") (window-buffer (next-window)))))
 
-(ert-deftest erc-scenarios-base-reconnect-options--default ()
-  :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
-  (should-not erc-reconnect-display)
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
 
-  (erc-scenarios-base-buffer-display--reconnect-common
+       ;; A manual /JOIN command tells ERC we're done auto-reconnecting
+       (with-current-buffer "FooNet" (erc-scenarios-common-say "/JOIN #spam"))
 
-   (lambda ()
-     (pop-to-buffer-same-window "*Messages*")
+       (ert-info ("#spam ignores `erc-reconnect-display'")
+         ;; Uses `erc-interactive-display' instead.
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (get-buffer "#spam")))
+           ;; Option `buffer' replaces entire window (no split)
+           (should (frame-root-window-p (selected-window)))))))))
 
-     (erc-d-t-ensure-for 1 "Server buffer not shown"
-       (not (eq (window-buffer) (get-buffer "FooNet"))))
+(ert-deftest erc-scenarios-base-buffer-display--defwino-recbury-intbuf ()
+  :tags '(:expensive-test)
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
+  (should-not erc-reconnect-display)
 
-     (erc-d-t-ensure-for 3 "Channel #chan not shown"
-       (not (eq (window-buffer) (get-buffer "#chan"))))
+  (let ((erc-buffer-display 'window-noselect)
+        (erc-reconnect-display 'bury)
+        (erc-interactive-display 'buffer))
+    (erc-scenarios-base-buffer-display--reconnect-common
 
-     (should (eq (window-buffer) (messages-buffer))))))
+     (lambda (_)
+       ;; Selected window shows some non-ERC buffer.  New server
+       ;; buffer appears in another window (other side of split).
+       (should-not (frame-root-window-p (selected-window)))
+       (should-not (eq (window-buffer) (current-buffer)))
+       (with-current-buffer (window-buffer)
+         (should-not (derived-mode-p 'erc-mode)))
+       (should (eq (current-buffer) (window-buffer (next-window)))))
+
+     (lambda (_)
+       (should-not (frame-root-window-p (selected-window)))
+       ;; Current split likely shows scratch.
+       (with-current-buffer (window-buffer)
+         (should-not (derived-mode-p 'erc-mode)))
+       (should (eq (current-buffer) (window-buffer (next-window)))))
+
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
+
+       ;; A non-interactive JOIN command doesn't signal that we're
+       ;; done auto-reconnecting, and `erc-interactive-display' is
+       ;; ignored, so `erc-buffer-display' is again in charge (here,
+       ;; that means `window-noselect').
+       (ert-info ("Join chan noninteractively and open a /QUERY")
+         (with-current-buffer "FooNet"
+           (erc-cmd-JOIN "#spam")
+           ;; However this will reset the option.
+           (erc-scenarios-common-say "/QUERY bob")
+           (should (eq (window-buffer) (get-buffer "bob")))
+           (should (frame-root-window-p (selected-window)))))
+
+       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (get-buffer "bob")))
+           (should-not (frame-root-window-p (selected-window)))
+           (should (eq (current-buffer) (window-buffer (next-window))))))))))
+
+(ert-deftest erc-scenarios-base-buffer-display--count-reset-timeout ()
+  :tags '(:expensive-test)
+  (should (eq erc-buffer-display 'bury))
+  (should (eq erc-interactive-display 'window))
+  (should (eq erc-reconnect-display-timeout 10))
+  (should-not erc-reconnect-display)
 
+  (let ((erc-buffer-display 'window-noselect)
+        (erc-reconnect-display 'bury)
+        (erc-interactive-display 'buffer)
+        ;; Try changing this value to 1.  The last `ert-info' block
+        ;; should fail.
+        (erc-reconnect-display-timeout 0.1))
+    (erc-scenarios-base-buffer-display--reconnect-common
+     #'ignore #'ignore ; These two are identical to the previous test.
+
+     (lambda (_)
+       (with-current-buffer "FooNet"
+         (should (eq (window-buffer) (messages-buffer)))
+         (should (frame-root-window-p (selected-window))))
+
+       ;; A non-interactive JOIN command doesn't signal that we're
+       ;; done auto-reconnecting
+       (ert-info ("Join chan noninteractively")
+         (with-current-buffer "FooNet"
+           (sit-for 0.1)
+           (erc-cmd-JOIN "#spam")))
+
+       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+         (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
+           (should (eq (window-buffer) (messages-buffer)))
+           ;; If `erc-reconnect-display-timeout' were left alone, this
+           ;; would be (frame-root-window-p #<window 1 on *scratch*>).
+           (should-not (frame-root-window-p (selected-window)))
+           (should (eq (current-buffer) (window-buffer (next-window))))))))))
 
 ;; This shows that the option `erc-interactive-display' overrides
 ;; `erc-join-buffer' during cold opens and interactive /JOINs.
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] <87leiuy3cv.fsf@neverwas.me>
  2023-04-21 14:03 ` bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior J.P.
       [not found] ` <87354tcoyk.fsf@neverwas.me>
@ 2023-05-08 22:26 ` J.P.
       [not found] ` <87jzxie9yf.fsf@neverwas.me>
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-05-08 22:26 UTC (permalink / raw)
  To: 62833; +Cc: emacs-erc

"J.P." <jp@neverwas.me> writes:

> The main focus will be those aspects impacting ERC 5.6 and how they
> integrate with the upstream display-buffer facility provided by
> window.el. In a sense, this is a spiritual successor to
>
>   bug#51753: ERC switches to channel buffer on reconnect

Complaints continue to trickle in regarding the option `erc-join-buffer'
and its new default of `bury'. To recap, bug#51753 led to changes [1]
that altered how buffers are displayed in various contexts, most
commonly:

  1. (erc-tls :server ...)
  2. M-x erc-tls RET
  3. /JOIN #chan
  4. /RECONNECT
  5. automatically reconnect

Basically, the attempted fix for 5 also affected the others, most of
which are triggered by user interaction and therefore ought to have been
exempt from any such nerfing (arguably). See, back before the change, a
new or reassociated buffer would simply replace the one in the selected
window. Now, in ERC 5.5 (Emacs 29), new buffers aren't displayed by
default, and the only confirmation that anything's happened (after, say,
invoking M-x erc-tls) is typically a blip in the mode-line. This lack of
feedback has confused new users and irritated existing ones.

Thus, I'm thinking we ought to consider changing the default in Emacs 29
to `window-noselect'. This is exactly what etc/ERC-NEWS currently
recommends for personal setups anyway [2], and the behavior it triggers was
newly redone in 5.5 to make good on its long advertised purpose, which
is to show the new buffer in some other window via the `display-buffer'
action

  (nil (inhibit-same-window . t))

which, AFAIK, never results in that window being selected. If true, then
I believe `window-noselect' (at least, among the available choices) most
closely approximates what will hopefully become an improved user
experience in ERC 5.6.

The main impediment I see to making such a change is that it would mean
yet a third default value for this option in as many years (or four, if
you count `bury' being forever baked into ERC 5.5 on ELPA). That's quite
a bit of whiplash, and it speaks to our being overly equivocal (not
untrue) if not wholly unprofessional (hopefully only possibly true).
There's also the lesser matter of the current behavior having been
somewhat suggested by an Emacs maintainer [3], which makes me less
inclined to pursue a fix unless folks upset enough by the issue voice
their concerns here on the tracker.

Thanks.


[1] commit 132d5cb0a3ec94afbb49772631861e00160ffffb
    Author: F. Jason Park <jp@neverwas.me>
    Date:   Tue Sep 6 19:09:54 2022 -0700

    Bury new ERC buffers by default

    * lisp/erc/erc.el (erc-join-buffer): Change default value to `bury'.
    [...]
    (Bug#51753)

    etc/ERC-NEWS                                  | 14 +++++++--
    lisp/erc/erc.el                               |  5 +--
    test/lisp/erc/erc-scenarios-base-reconnect.el | 45 ++++++++++++++-------------
    3 files changed, 37 insertions(+), 27 deletions(-)


[2] ** Changes to display options for new ERC buffers.

    The default value for the option 'erc-join-buffer', which determines
    how new buffers are displayed, has been changed to 'bury' for
    security reasons. Although the old value of 'buffer' is still
    accessible, along with its original behavior, users wanting a safer
    alternative can now opt for an improved 'window-noselect' instead.
    It still offers the same pronounced visual cue when connecting and
    joining but now avoids any hijacking of the active window as well.

[3] https://lists.gnu.org/archive/html/emacs-erc/2022-09/msg00006.html





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] ` <87jzxie9yf.fsf@neverwas.me>
@ 2023-05-10 21:43   ` Corwin Brust
       [not found]   ` <CAJf-WoTk1vT3gVSHdO7MRs6Rfn4PRcs8UWM=mw_NbzeCGHDfvQ@mail.gmail.com>
  1 sibling, 0 replies; 15+ messages in thread
From: Corwin Brust @ 2023-05-10 21:43 UTC (permalink / raw)
  To: J.P.; +Cc: emacs-erc, 62833

Thank you, JP.

On Mon, May 8, 2023 at 5:26 PM J.P. <jp@neverwas.me> wrote:
>
> "J.P." <jp@neverwas.me> writes:
>
> > The main focus will be those aspects impacting ERC 5.6 and how they
> > integrate with the upstream display-buffer facility provided by
> > window.el. In a sense, this is a spiritual successor to
> >
> >   bug#51753: ERC switches to channel buffer on reconnect
>
> Complaints continue to trickle in regarding the option `erc-join-buffer'
> and its new default of `bury'. To recap, bug#51753 led to changes [1]

Please add me to the list of people who didn't much care for the new default.

> that altered how buffers are displayed in various contexts, most
> commonly:
>
>   1. (erc-tls :server ...)
>   2. M-x erc-tls RET
>   3. /JOIN #chan
>   4. /RECONNECT
>   5. automatically reconnect

FWIW, I connect automatically on startup using a desktop shortcut
running a command something like:
  emacs -f my-erc-init.el -eval "(my-connect-fun)"

>
> Basically, the attempted fix for 5 also affected the others, most of
> which are triggered by user interaction and therefore ought to have been
> exempt from any such nerfing (arguably). See, back before the change, a
> new or reassociated buffer would simply replace the one in the selected
> window. Now, in ERC 5.5 (Emacs 29), new buffers aren't displayed by
> default, and the only confirmation that anything's happened (after, say,
> invoking M-x erc-tls) is typically a blip in the mode-line. This lack of
> feedback has confused new users and irritated existing ones.
>
> Thus, I'm thinking we ought to consider changing the default in Emacs 29
> to `window-noselect'. This is exactly what etc/ERC-NEWS currently
> recommends for personal setups anyway [2], and the behavior it triggers was
> newly redone in 5.5 to make good on its long advertised purpose, which
> is to show the new buffer in some other window via the `display-buffer'

FWIW, I'd prefer that to the present situation.  My sense from chatter
on IRC is that this probably matches others expectations also but
perhaps someone who prefers the new and now current default setting of
bury will weigh in here and dispell my confirmation bias ;)

> action
>
>   (nil (inhibit-same-window . t))
>
> which, AFAIK, never results in that window being selected. If true, then
> I believe `window-noselect' (at least, among the available choices) most
> closely approximates what will hopefully become an improved user
> experience in ERC 5.6.
>
> The main impediment I see to making such a change is that it would mean
> yet a third default value for this option in as many years (or four, if
> you count `bury' being forever baked into ERC 5.5 on ELPA). That's quite
> a bit of whiplash, and it speaks to our being overly equivocal (not
> untrue) if not wholly unprofessional (hopefully only possibly true).
> There's also the lesser matter of the current behavior having been
> somewhat suggested by an Emacs maintainer [3], which makes me less
> inclined to pursue a fix unless folks upset enough by the issue voice
> their concerns here on the tracker.

I don't see an issue with multiple changes to a default within a short
time, either within an ERC version or in several consecutive ones.
Changing it each *Emacs* version seems more problematic, but I think
that's not an issue (so far) in this case.

So I think now is a very good time to make the change, and I'd like to
see it happen.

I hope other ERC users who feel strongly about this will weigh in.

>
> Thanks.
>
>
> [1] commit 132d5cb0a3ec94afbb49772631861e00160ffffb
>     Author: F. Jason Park <jp@neverwas.me>
>     Date:   Tue Sep 6 19:09:54 2022 -0700
>
>     Bury new ERC buffers by default
>
>     * lisp/erc/erc.el (erc-join-buffer): Change default value to `bury'.
>     [...]
>     (Bug#51753)
>
>     etc/ERC-NEWS                                  | 14 +++++++--
>     lisp/erc/erc.el                               |  5 +--
>     test/lisp/erc/erc-scenarios-base-reconnect.el | 45 ++++++++++++++-------------
>     3 files changed, 37 insertions(+), 27 deletions(-)
>
>
> [2] ** Changes to display options for new ERC buffers.
>
>     The default value for the option 'erc-join-buffer', which determines
>     how new buffers are displayed, has been changed to 'bury' for
>     security reasons. Although the old value of 'buffer' is still
>     accessible, along with its original behavior, users wanting a safer
>     alternative can now opt for an improved 'window-noselect' instead.
>     It still offers the same pronounced visual cue when connecting and
>     joining but now avoids any hijacking of the active window as well.
>
> [3] https://lists.gnu.org/archive/html/emacs-erc/2022-09/msg00006.html
>

Really appreciate your work on this JP





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found]   ` <CAJf-WoTk1vT3gVSHdO7MRs6Rfn4PRcs8UWM=mw_NbzeCGHDfvQ@mail.gmail.com>
@ 2023-05-13 14:03     ` J.P.
       [not found]     ` <87sfc08h19.fsf@neverwas.me>
  1 sibling, 0 replies; 15+ messages in thread
From: J.P. @ 2023-05-13 14:03 UTC (permalink / raw)
  To: Corwin Brust; +Cc: emacs-erc, 62833

Corwin Brust <corwin@bru.st> writes:

>> buffers are displayed in various contexts, most commonly:
>>
>>   1. (erc-tls :server ...)
>>   2. M-x erc-tls RET
>>   3. /JOIN #chan
>>   4. /RECONNECT
>>   5. automatically reconnect
>
> FWIW, I connect automatically on startup using a desktop shortcut
> running a command something like:
>   emacs -f my-erc-init.el -eval "(my-connect-fun)"

I guess I could have tried grouping these into user-initiated and non-,
but it seems to me the first item, `erc-tls', can go either way. While
your example, which presumably calls `erc-tls' at some point, clearly
falls into the user-initiated camp, the same might not be said, for
example, of running `erc-tls' whenever Emacs receives a specific message
over DBus. So because detecting a user's intent isn't foolproof (not
only with 1, but in general), we may want to extend the existing display
options by offering some sort of universal escape hatch that affords
more granular control.

However, doing this alone won't cover the problem of communicating to
each user-implemented instance of such an escape hatch the necessary
specifics about the context in which Emacs' display machinery is being
summoned. And I don't think switching away from the one-to-many
arrangement we have now to a single option per context is doable because
of the first problem of accurately detecting intent.

So, as a compromise, I'm thinking we could extend all existing options
to accommodate arbitrary "action" forms, which we'd then pass along to a
new `display-buffer' call (in `erc-setup-buffer') before trusting and
selecting whatever window it spits out. The point would be to supplement
user-supplied "action alists" with extra contextual data to indicate
things like the last slash command invoked. (Alternatively, we could
relay the same info via global erc-* variables; doesn't matter to me.)
However, even this wouldn't be a panacea. A user would still need to
apply some extra elbow grease for things like your `my-connect-fun' or
my DBus example, possibly by doing something like

  (let ((erc-join-buffer
         '(my-use-dedicated-frame (inhibit-same-window . t))))
    (erc-tls :server ...))

which doesn't seem all that painful. Although, at that point, why not
just do

  (display-buffer (let ((erc-join-buffer 'bury)) (erc-tls :server ...))
                  '(my-use-dedicated-frame (inhibit-same-window . t)))

which has always been possible and is no more complicated?

>> Thus, I'm thinking we ought to consider changing the default in Emacs 29
>> to `window-noselect'. This is exactly what etc/ERC-NEWS currently
>> recommends for personal setups anyway [2], and the behavior it triggers was
>> newly redone in 5.5 to make good on its long advertised purpose, which
>> is to show the new buffer in some other window via the `display-buffer'
>
> FWIW, I'd prefer that to the present situation.  My sense from chatter
> on IRC is that this probably matches others expectations also but
> perhaps someone who prefers the new and now current default setting of
> bury will weigh in here and dispell my confirmation bias ;)

That's my sense as well.

>> The main impediment I see to making such a change is that it would mean
>> yet a third default value for this option in as many years (or four, if
>> you count `bury' being forever baked into ERC 5.5 on ELPA). That's quite
>> a bit of whiplash, and it speaks to our being overly equivocal (not
>> untrue) if not wholly unprofessional (hopefully only possibly true).
>> There's also the lesser matter of the current behavior having been
>> somewhat suggested by an Emacs maintainer [3], which makes me less
>> inclined to pursue a fix unless folks upset enough by the issue voice
>> their concerns here on the tracker.
>
> I don't see an issue with multiple changes to a default within a short
> time, either within an ERC version or in several consecutive ones.
> Changing it each *Emacs* version seems more problematic, but I think
> that's not an issue (so far) in this case.

Just to recap, here are the default values of `erc-join-buffer' (now
also aliased to `erc-buffer-display' on HEAD):

  5.4        - buffer
  5.5        - bury
  5.5.1.29.1 - window-noselect (proposed)
  5.6        - bury (repurposed as a catch-all)

In the last one, "catch-all" not only means it's overridden by other
options, which has always been the case, but that contexts formerly
within its domain, like M-x erc and "/JOIN", are now determined
elsewhere, such as by `erc-interactive-display' (currently `window').
(BTW, "/RECONNECT" currently isn't covered by the latter, but that could
change if folks want.)

> So I think now is a very good time to make the change, and I'd like to
> see it happen.
>
> I hope other ERC users who feel strongly about this will weigh in.

I suppose it couldn't hurt to get a patch going for "5.5.1.29.1", just
to have something on standby.

> Really appreciate your work on this JP

Very kind, thanks.





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] <87leiuy3cv.fsf@neverwas.me>
                   ` (3 preceding siblings ...)
       [not found] ` <87jzxie9yf.fsf@neverwas.me>
@ 2023-05-16 14:37 ` Phillip Susi
  2023-06-04 14:52 ` J.P.
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Phillip Susi @ 2023-05-16 14:37 UTC (permalink / raw)
  To: 62833

+1 from me.

This behavior change was highly surprising.  I thought ERC was just
broken until I checked the buffer list and saw it had just decided to
open in the background.






^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found]     ` <87sfc08h19.fsf@neverwas.me>
@ 2023-06-02 14:06       ` J.P.
  0 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-06-02 14:06 UTC (permalink / raw)
  To: 62833; +Cc: Corwin Brust, emacs-erc

[-- Attachment #1: Type: text/plain, Size: 2810 bytes --]

"J.P." <jp@neverwas.me> writes:

> So because detecting a user's intent isn't foolproof (not only with 1,
> but in general), we may want to extend the existing display options by
> offering some sort of universal escape hatch that affords more
> granular control.

I've attempted something along these lines with the attached patch. It
adds a new Custom type variant to all of ERC's buffer-display options.

> However, doing this alone won't cover the problem of communicating to
> each user-implemented instance of such an escape hatch the necessary
> specifics about the context in which Emacs' display machinery is being
> summoned. And I don't think switching away from the one-to-many
> arrangement we have now to a single option per context is doable because
> of the first problem of accurately detecting intent.
>
> So, as a compromise, I'm thinking we could extend all existing options
> to accommodate arbitrary "action" forms, which we'd then pass along to a
> new `display-buffer' call (in `erc-setup-buffer') before trusting and
> selecting whatever window it spits out.

Actually, instead of a `display-buffer' action alone, I went for a cons
of a `display-buffer'-compatible function, like `pop-to-buffer', and an
action argument, together.

> The point would be to supplement user-supplied "action alists" with
> extra contextual data to indicate things like the last slash command
> invoked. (Alternatively, we could relay the same info via global erc-*
> variables; doesn't matter to me.)

For this new variant I'm proposing, ERC calls the user's function with
the newly created buffer and a possibly augmented version of the action
that includes some well defined contextual clues in its alist. The
latter are enumerated in the doc strings of the various user options.

> However, even this wouldn't be a panacea. A user would still need to
> apply some extra elbow grease for things like your `my-connect-fun' or
> my DBus example, possibly by doing something like
>
>   (let ((erc-join-buffer
>          '(my-use-dedicated-frame (inhibit-same-window . t))))
>     (erc-tls :server ...))
>
> which doesn't seem all that painful. Although, at that point, why not
> just do
>
>   (display-buffer (let ((erc-join-buffer 'bury)) (erc-tls :server ...))
>                   '(my-use-dedicated-frame (inhibit-same-window . t)))
>
> which has always been possible and is no more complicated?

This would be preferable if we had more granular options that only
affected a single context, such as something exclusively for
non-interactive `erc-tls' invocations. However, as described above, our
existing options cover multiple contexts, so this approach falls short
in the end, which is a shame because the blanket changes I'm proposing
are somewhat invasive and add a nonzero amount of complexity.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-5.6-Allow-custom-display-buffer-actions-in-ERC.patch --]
[-- Type: text/x-patch, Size: 26567 bytes --]

From ba83fa278a6d8eec5cf0add2958c9021552e6fb0 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Tue, 30 May 2023 23:27:12 -0700
Subject: [PATCH] [5.6] Allow custom display-buffer actions in ERC

* etc/ERC-NEWS: Mention new `display-buffer' "action" variant for all
buffer-display options.
* lisp/erc/erc-backend.el (erc-server-JOIN, erc-server-PRIVMSG,
erc-server-NOTICE): Set `erc--display-context' to a symbol for the IRC
command, like `JOIN' in order to influence `erc-setup-buffer' by way
of `erc--open-target'.
* lisp/erc/erc.el (erc--buffer-display-choices): New helper for
defining common `:type' for all buffer-display options.
(erc-buffer-display): Add new choice of either `display-buffer' or
`pop-to-buffer' paired with an "action alist".
(erc-buffer-display, erc-interactive-display, erc-reconnect-display,
erc-receive-query-display): Use helper `erc--buffer-display-choices'
for defining `:type'.
(erc-setup-buffer): Do nothing when the selected window already shows
current buffer unless user has provided a custom action.  Accommodate
new choice values `display-buffer' and `pop-to-buffer'.
(erc-select-read-args): Add `erc--display-context' to environment.
(erc, erc-tls): Bind `erc--display-context' around calls to
`erc-select-read-args' and main body.
(erc-cmd-JOIN, erc-cmd-QUERY, erc-handle-irc-url): Add item for
`erc-interactive-display' to `erc--display-context'.
* test/lisp/erc/erc-tests.el (erc-setup-buffer--custom-action): New
test.
(erc-select-read-args, erc-tls, erc--interactive): Expect new
environment binding for `erc--display-context'.  (Bug#62833)
---
 etc/ERC-NEWS               |  12 +++-
 lisp/erc/erc-backend.el    |   7 +-
 lisp/erc/erc.el            | 142 ++++++++++++++++++++++++++++---------
 test/lisp/erc/erc-tests.el | 104 ++++++++++++++++++++++++---
 4 files changed, 218 insertions(+), 47 deletions(-)

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index e9ec9e2caab..edf9990d0de 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -37,7 +37,7 @@ decade overdue, this is no longer the case.  Other UX improvements in
 this area aim to make the process of connecting interactively slightly
 more streamlined and less repetitive, even for veteran users.
 
-** Revised buffer-display handling for interactive commands.
+** Revised buffer-display handling.
 A point of friction for new users and one only just introduced with
 ERC 5.5 has been the lack of visual feedback when first connecting via
 M-x erc or when issuing a "/JOIN" command at the prompt.  As explained
@@ -58,6 +58,16 @@ option (now known as 'erc-receive-query-display') is nil, ERC uses
 'erc-interactive-display'.  The old nil behavior can still be gotten
 via the new compatibility flag 'erc-receive-query-display-defer'.
 
+A few subtleties affecting the display of new or reassociated buffers
+have also emerged.  One involves buffers that already occupy the
+selected window.  ERC now treats these as deserving of an implicit
+'bury'.  An escape hatch for this and most other baked-in behaviors is
+now available in the form of a new type variant recognized by all such
+options.  Indeed, users can now specify their own 'display-buffer'
+"actions" to exercise full control over nearly all buffer-display
+related decisions.  See the doc strings of 'erc-display-buffer' and
+friends for details.
+
 ** Setting a module's mode variable via Customize earns a warning.
 Trying and failing to activate a module via its minor mode's Custom
 widget has been an age-old annoyance for new users.  Previously
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 2de24e7cb25..6a1c0745263 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -101,6 +101,7 @@
 (eval-when-compile (require 'cl-lib))
 (require 'erc-common)
 
+(defvar erc--display-context)
 (defvar erc--target)
 (defvar erc--user-from-nick-function)
 (defvar erc-channel-list)
@@ -1688,7 +1689,9 @@ define-erc-response-handler
   "Handle join messages."
   nil
   (let ((chnl (erc-response.contents parsed))
-        (buffer nil))
+        (buffer nil)
+        (erc--display-context `((erc-display-buffer . JOIN)
+                                ,@erc--display-context)))
     (pcase-let ((`(,nick ,login ,host)
                  (erc-parse-user (erc-response.sender parsed))))
       ;; strip the stupid combined JOIN facility (IRC 2.9)
@@ -1887,6 +1890,8 @@ define-erc-response-handler
              (noticep (string= cmd "NOTICE"))
              ;; S.B. downcase *both* tgt and current nick
              (privp (erc-current-nick-p tgt))
+             (erc--display-context `((erc-display-buffer . ,(intern cmd))
+                                     ,@erc--display-context))
              s buffer
              fnick)
         (setf (erc-response.contents parsed) msg)
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 2c2df81fa6d..2c5afc876d1 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1566,9 +1566,33 @@ erc-default-port-tls
   "IRC port to use for encrypted connections if it cannot be \
 detected otherwise.")
 
+(defconst erc--buffer-display-choices
+  `(choice (const :tag "Use value of `erc-buffer-display'" nil)
+           (const :tag "Split window and select" window)
+           (const :tag "Split window but don't select" window-noselect)
+           (const :tag "New frame" frame)
+           (const :tag "Don't display" bury)
+           (const :tag "Use current window" buffer)
+           (cons :tag "Custom action"
+                 (choice :tag "Display function"
+                         (function-item pop-to-buffer)
+                         (function-item display-buffer)
+                         function)
+                 (cons :tag "Action"
+                       (choice :tag "Action function"
+                               function (repeat function))
+                       (alist :tag "Action arguments"
+                              :key-type symbol
+                              :value-type (sexp :tag "Value")))))
+  "Common choices for buffer-display options.")
+
 (defvaralias 'erc-join-buffer 'erc-buffer-display)
 (defcustom erc-buffer-display 'bury
   "How to display a newly created ERC buffer.
+This determines the baseline, \"catch-all\" display behavior.  It
+takes a backseat to more context-specific display options, like
+`erc-interactive-display', `erc-reconnect-display', and
+`erc-receive-query-display'.
 
 The available choices are:
 
@@ -1577,17 +1601,28 @@ erc-buffer-display
   `frame'           - in another frame,
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
+  (FUNC . ACTION)   - call FUNC with buffer and ACTION
+
+Here, FUNC signifies a function, like `display-buffer' or
+`pop-to-buffer', that takes a buffer and an ACTION as arguments.
+If non-nil, ACTION should be as described by the Info
+node `(elisp) Displaying Buffers'.  At times, ERC may add hints
+about the calling context to a given action alist.  Keys are
+symbols of options that have influenced the outcome, like
+`erc-buffer-display', and values are from a set of predefined
+constants.  In the case of this option, ERC uses the symbols
+
+  `JOIN',`PRIVMSG' `NOTICE', `erc', and `erc-tls'.
+
+The first three signify IRC commands received from the server and
+the rest entry-point commands responsible for the connection.
 
-See related options `erc-interactive-display',
-`erc-reconnect-display', and `erc-receive-query-display'."
+Note that when the selected window already shows the current
+buffer, ERC pretends this option's value is `bury' unless it's
+one of the two \"action\" conses described just above."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
-  :type '(choice (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+  :type (cons 'choice (nthcdr 2 erc--buffer-display-choices)))
 
 (defvaralias 'erc-query-display 'erc-interactive-display)
 (defcustom erc-interactive-display 'window
@@ -1596,30 +1631,38 @@ erc-interactive-display
 interactively at the prompt.  It does not apply when calling a
 handler for such a command, like `erc-cmd-JOIN', from lisp code.
 See `erc-buffer-display' for a full description of available
-values."
+values.
+
+When the value is cons indicating a special display action, ERC
+injects a hint about the invocation context as an extra item in
+the action alist passed to `display-buffer' or `pop-to-buffer'.
+The item's key is the symbol `erc-interactive-display' and its
+value one of
+
+  `/QUERY', `/JOIN', `url', `erc', or `erc-tls'.
+
+All are symbols indicating an inciting user action, such as
+issuing a slash command, clicking a URL hyperlink, or invoking an
+entry point."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury new and don't display existing" bury)
-                 (const :tag "Use current buffer" buffer)))
+  :type erc--buffer-display-choices)
 
 (defcustom erc-reconnect-display nil
   "How and whether to display a channel buffer when auto-reconnecting.
 This only affects automatic reconnections and is ignored, like
 all other buffer-display options, when issuing a /RECONNECT or
 successfully reinvoking `erc-tls' with similar arguments.  See
-`erc-buffer-display' for a description of possible values."
+`erc-buffer-display' for a description of possible values.
+
+When the value is cons indicating a special display action, ERC
+injects a hint about the calling context as an extra item in the
+action alist passed to `display-buffer' or `pop-to-buffer'.  The
+item's key is the symbol `erc-reconnect-display' and its value
+non-nil."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)))
+  :type erc--buffer-display-choices)
 
 (defcustom erc-reconnect-display-timeout 10
   "Duration `erc-reconnect-display' remains active.
@@ -2119,7 +2162,7 @@ erc--setup-buffer-first-window
 (defun erc--display-buffer-use-some-frame (buffer alist)
   "Maybe display BUFFER in an existing frame for the same connection.
 If performed, return window used; otherwise, return nil.  Forward ALIST
-to display-buffer machinery."
+to `display-buffer' machinery."
   (when-let*
       ((idp (lambda (value)
               (and erc-networks--id
@@ -2138,12 +2181,27 @@ erc--display-buffer-use-some-frame
 (defvar erc--setup-buffer-hook nil
   "Internal hook for module setup involving windows and frames.")
 
+(defvar erc--display-context nil
+  "Extra action alist items passed to `display-buffer'.
+Non-nil when a user specifies a custom display action for certain
+display-options, like `erc-reconnect-display'.  ERC pairs the
+option's symbol with a context-dependent value and adds the entry
+to the user-provided alist when calling `pop-to-buffer' or
+`display-buffer'.")
+
 (defun erc-setup-buffer (buffer)
   "Consults `erc-join-buffer' to find out how to display `BUFFER'."
   (pcase (if (zerop (erc-with-server-buffer
                       erc--server-last-reconnect-count))
              erc-join-buffer
            (or erc-reconnect-display erc-join-buffer))
+    ((and (or `(,disp-fn) `(,disp-fn . (,actfns . ,alist)))
+          (guard (functionp disp-fn)))
+     (let ((context erc--display-context))
+       (unless (zerop erc--server-last-reconnect-count)
+         (push '(erc-reconnect-display . t) context))
+       (funcall disp-fn buffer `(,actfns ,@context ,@alist))))
+    ((guard (eq (window-buffer) buffer)))
     ('window
      (if (active-minibuffer-window)
          (display-buffer buffer)
@@ -2455,6 +2513,8 @@ erc-select-read-args
          env)
     (when erc-interactive-display
       (push `(erc-join-buffer . ,erc-interactive-display) env))
+    (when erc--display-context
+      (push `(erc--display-context . ,erc--display-context) env))
     (when opener
       (push `(erc-server-connect-function . ,opener) env))
     (when (and passwd (string= "" passwd))
@@ -2508,7 +2568,12 @@ erc
 See `erc-tls' for the meaning of ID.
 
 \(fn &key SERVER PORT NICK USER PASSWORD FULL-NAME ID)"
-  (interactive (erc-select-read-args))
+  (interactive (let ((erc--display-context `((erc-interactive-display . erc)
+                                             ,@erc--display-context)))
+                 (erc-select-read-args)))
+  (unless (assq 'erc--display-context --interactive-env--)
+    (push '(erc--display-context . ((erc-buffer-display . erc)))
+          --interactive-env--))
   (erc--with-entrypoint-environment --interactive-env--
     (erc-open server port nick full-name t password nil nil nil nil user id)))
 
@@ -2573,8 +2638,11 @@ erc-tls
 interactively.
 
 \(fn &key SERVER PORT NICK USER PASSWORD FULL-NAME CLIENT-CERTIFICATE ID)"
-  (interactive (let ((erc-default-port erc-default-port-tls))
-		 (erc-select-read-args)))
+  (interactive
+   (let ((erc-default-port erc-default-port-tls)
+         (erc--display-context `((erc-interactive-display . erc-tls)
+                                 ,@erc--display-context)))
+     (erc-select-read-args)))
   ;; Bind `erc-server-connect-function' to `erc-open-tls-stream'
   ;; around `erc-open' when a non-default value hasn't been specified
   ;; by the user or the interactive form.  And don't bother checking
@@ -2583,6 +2651,9 @@ erc-tls
               (not (eq erc-server-connect-function #'erc-open-network-stream)))
     (push '(erc-server-connect-function . erc-open-tls-stream)
           --interactive-env--))
+  (unless (assq 'erc--display-context --interactive-env--)
+    (push '(erc--display-context . ((erc-buffer-display . erc-tls)))
+          --interactive-env--))
   (erc--with-entrypoint-environment --interactive-env--
     (erc-open server port nick full-name t password
               nil nil nil client-certificate user id)))
@@ -3729,7 +3800,10 @@ erc-cmd-JOIN
                         (sn (erc-extract-nick (erc-response.sender parsed)))
                         ((erc-nick-equal-p sn (erc-current-nick)))
                         (erc-join-buffer (or erc-interactive-display
-                                             erc-join-buffer)))
+                                             erc-join-buffer))
+                        (erc--display-context `((erc-interactive-display
+                                                 . /JOIN)
+                                                ,@erc--display-context)))
                      (run-hook-with-args-until-success
                       'erc-server-JOIN-functions proc parsed)
                      t))))
@@ -4113,7 +4187,9 @@ erc-cmd-QUERY
       ;; currently broken, evil hack to display help anyway
                                         ;(erc-delete-query))))
     (signal 'wrong-number-of-arguments '(erc-cmd-QUERY 0)))
-  (let ((erc-join-buffer erc-interactive-display))
+  (let ((erc-join-buffer erc-interactive-display)
+        (erc--display-context `((erc-interactive-display . /QUERY)
+                                ,@erc--display-context)))
     (erc-with-server-buffer
      (erc--open-target user))))
 
@@ -4897,13 +4973,7 @@ erc-receive-query-display
   :package-version '(ERC . "5.6")
   :group 'erc-buffers
   :group 'erc-query
-  :type '(choice (const :tag "Defer to value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+  :type erc--buffer-display-choices)
 
 (defvar erc-receive-query-display-defer t
   "How to interpret a null `erc-receive-query-display'.
@@ -7925,6 +7995,8 @@ erc-handle-irc-url
 Customize `erc-url-connect-function' to override this."
   (when (eql port 0) (setq port nil))
   (let* ((net (erc-networks--determine host))
+         (erc--display-context `((erc-interactive-display . url)
+                                 ,@erc--display-context))
          (server-buffer
           ;; Viable matches may slip through the cracks for unknown
           ;; networks.  Additional passes could likely improve things.
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 1c75f35e1b5..38186467de1 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -801,6 +801,72 @@ erc-reuse-frames--displayed-full
      (dolist (b '("server" "ircd" "bob" "alice" "#spam" "#chan"))
        (kill-buffer b)))))
 
+(ert-deftest erc-setup-buffer--custom-action ()
+  (erc-mode)
+  (erc-tests--set-fake-server-process "sleep" "1")
+  (setq erc--server-last-reconnect-count 0)
+  (let ((owin (selected-window))
+        (obuf (window-buffer))
+        (mbuf (messages-buffer))
+        calls)
+    (cl-letf (((symbol-function 'switch-to-buffer) ; regression
+               (lambda (&rest r) (push (cons 'switch-to-buffer r) calls)))
+              ((symbol-function 'pop-to-buffer)
+               (lambda (&rest r) (push (cons 'pop-to-buffer r) calls)))
+              ((symbol-function 'erc--test-fun)
+               (lambda (&rest r) (push (cons 'erc--test-fun r) calls)))
+              ((symbol-function 'display-buffer)
+               (lambda (&rest r) (push (cons 'display-buffer r) calls))))
+
+      ;; Baseline
+      (let ((erc-join-buffer 'bury))
+        (erc-setup-buffer mbuf)
+        (should-not calls))
+
+      (should-not erc--display-context)
+
+      ;; `display-buffer'
+      (let ((erc--display-context '((erc-buffer-display . 1)))
+            (erc-join-buffer
+             '(display-buffer display-buffer-no-window (allow-no-window . t))))
+        (erc-setup-buffer mbuf)
+        (should (equal `(display-buffer
+                         ,mbuf
+                         (display-buffer-no-window (erc-buffer-display . 1)
+                                                   (allow-no-window . t)))
+                       (pop calls)))
+        (should-not calls))
+
+      ;; `pop-to-buffer' with `erc-reconnect-display'
+      (let* ((erc--server-last-reconnect-count 1)
+             (erc--display-context '((erc-buffer-display . 1)))
+             (erc-reconnect-display
+              '(pop-to-buffer . ((display-buffer-same-window)))))
+        (erc-setup-buffer mbuf)
+        (should (equal `(pop-to-buffer ,mbuf
+                                       ((display-buffer-same-window)
+                                        (erc-reconnect-display . t)
+                                        (erc-buffer-display . 1)))
+                       (pop calls)))
+        (should-not calls))
+
+      ;; Undocumented variants
+      (pcase-dolist (`(,want ,got)
+                     `(((pop-to-buffer) (nil))
+                       ((pop-to-buffer afun) (afun))
+                       ((pop-to-buffer (afun)) ((afun)))
+                       ((erc--test-fun (afun)) ((afun)))
+                       ((pop-to-buffer afun (a . 1)) (afun (a . 1)))
+                       ((pop-to-buffer (afun) (a . 1)) ((afun) (a . 1)))))
+        (let* ((erc-buffer-display want))
+          (ert-info ((format "want: %S, got: %S, calls: %S" want got calls))
+            (erc-setup-buffer mbuf)
+            (should (equal (list (car want) mbuf got) (pop calls)))
+            (should-not calls)))))
+
+    (should (eq owin (selected-window)))
+    (should (eq obuf (window-buffer)))))
+
 (ert-deftest erc-lurker-maybe-trim ()
   (let (erc-lurker-trim-nicks
         (erc-lurker-ignore-chars "_`"))
@@ -1737,14 +1803,18 @@ erc-select-read-args
                            (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when URL is ircs://")
-    (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
-                     (erc-select-read-args))
-                   (list :server "irc.gnu.org"
-                         :port 6697
-                         :nick (user-login-name)
-                         '&interactive-env
-                         '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . window))))))
+    (let ((erc--display-context '((erc-interactive-display . erc))))
+      (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
+                       (erc-select-read-args))
+                     (list :server "irc.gnu.org"
+                           :port 6697
+                           :nick (user-login-name)
+                           '&interactive-env
+                           '((erc-server-connect-function
+                              . erc-open-tls-stream)
+                             (erc--display-context
+                              . ((erc-interactive-display . erc)))
+                             (erc-join-buffer . window)))))))
 
   (setq-local erc-interactive-display nil) ; cheat to save space
 
@@ -1824,6 +1894,7 @@ erc-tls
               ((symbol-function 'erc-open)
                (lambda (&rest r)
                  (push `((erc-join-buffer ,erc-join-buffer)
+                         (erc--display-context ,@erc--display-context)
                          (erc-server-connect-function
                           ,erc-server-connect-function))
                        env)
@@ -1836,6 +1907,7 @@ erc-tls
                          nil nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Full")
@@ -1852,6 +1924,7 @@ erc-tls
                          "bob:changeme" nil nil nil t "bobo" GNU.org)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       ;; Values are often nil when called by lisp code, which leads to
@@ -1871,6 +1944,7 @@ erc-tls
                              "bob:changeme" nil nil nil nil "bobo" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Interactive")
@@ -1881,6 +1955,8 @@ erc-tls
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer window)
+                         (erc--display-context
+                          (erc-interactive-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Custom connect function")
@@ -1891,6 +1967,8 @@ erc-tls
                            nil nil nil nil nil "user" nil)))
           (should (equal (pop env)
                          '((erc-join-buffer bury)
+                           (erc--display-context
+                            (erc-buffer-display . erc-tls))
                            (erc-server-connect-function my-connect-func))))))
 
       (ert-info ("Advised default function overlooked") ; intentional
@@ -1902,6 +1980,7 @@ erc-tls
                          nil nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream))))
         (advice-remove 'erc-server-connect-function 'erc-tests--erc-tls))
 
@@ -1915,6 +1994,8 @@ erc-tls
                            '("irc.libera.chat" 6697 "tester" "unknown" t
                              nil nil nil nil nil "user" nil)))
             (should (equal (pop env) `((erc-join-buffer bury)
+                                       (erc--display-context
+                                        (erc-buffer-display . erc-tls))
                                        (erc-server-connect-function ,f))))
             (advice-remove 'erc-server-connect-function
                            'erc-tests--erc-tls)))))))
@@ -1929,6 +2010,7 @@ erc--interactive
               ((symbol-function 'erc-open)
                (lambda (&rest r)
                  (push `((erc-join-buffer ,erc-join-buffer)
+                         (erc--display-context ,@erc--display-context)
                          (erc-server-connect-function
                           ,erc-server-connect-function))
                        env)
@@ -1941,8 +2023,9 @@ erc--interactive
                        '("irc.libera.chat" 6697 "tester" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer window) (erc-server-connect-function
-                                                   erc-open-tls-stream)))))
+                       '((erc-join-buffer window)
+                         (erc--display-context (erc-interactive-display . erc))
+                         (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Nick supplied, decline TLS upgrade")
         (ert-simulate-keys "\r\rdummy\r\rn\r"
@@ -1952,6 +2035,7 @@ erc--interactive
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer window)
+                         (erc--display-context (erc-interactive-display . erc))
                          (erc-server-connect-function
                           erc-open-network-stream))))))))
 
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] <87leiuy3cv.fsf@neverwas.me>
                   ` (4 preceding siblings ...)
  2023-05-16 14:37 ` Phillip Susi
@ 2023-06-04 14:52 ` J.P.
       [not found] ` <877csje0uz.fsf@neverwas.me>
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-06-04 14:52 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-erc, 62833

[-- Attachment #1: Type: text/plain, Size: 2074 bytes --]

Hi Eli,

This is regarding a small prospective change to ERC on Emacs 29 that's
been discussed here and there over the past few months [1]. Basically,
ERC has long defaulted to displaying new buffers in the currently
selected window, with the symbol `buffer' representing this display
style in various user options. Because new buffers are often created as
a result of server-initiated messages or feature-driven automated
mechanisms, like "autojoin", they can suddenly appear unceremoniously
and steal keyboard input. For this reason, `buffer' was deemed a
nuisance (if not a hazard), and the default was changed to a no-op in
ERC 5.5 [2].

However, since then, a faint but steady murmur of discontent has been
thrumming among mostly new and casual users, who've described ERC as
being "broken" in this regard because it provides little to no obvious
feedback following certain fundamental user actions, like connecting to
a server or joining a channel. IOW, users can't tell whether ERC is
responding to a command they've just issued, despite subtle cues, like
activity in the echo area and the mode line.

The solution to all this isn't straightforward, and we're making headway
on it for ERC 5.6. In the meantime, I'm wondering if we might consider
appeasing these disgruntled users somehow. Normally, I'd prefer just
reverting back to `buffer', but because much has been made about its
potential for causing mayhem via unintended sharing, I'm thinking we
might change the default in Emacs 29 to `window-noselect'. This value
tells ERC to show new buffers in a sibling window of the same vertical
combination. Such a change would be accompanied by a bump in the patch
component of our already 29-specific ERC version, bringing us from
5.5.0.29.1 to 5.5.1.29.1. I believe the attached patch does what I've
described.

Thanks,
J.P.

[1] https://debbugs.gnu.org/cgi/bugreport.cgi?bug=60428#31
    https://debbugs.gnu.org/cgi/bugreport.cgi?bug=62833#17
    https://debbugs.gnu.org/cgi/bugreport.cgi?bug=62833#23

[2] https://debbugs.gnu.org/cgi/bugreport.cgi?bug=51753#71



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-erc-join-buffer-hotfix-in-new-version-5.5.1.29.1.patch --]
[-- Type: text/x-patch, Size: 5763 bytes --]

From 420a95e855b4e01e57dc1a97dbeac06f6a4af779 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Fri, 19 May 2023 08:10:49 -0700
Subject: [PATCH] Add erc-join-buffer hotfix in new version 5.5.1.29.1

* doc/misc/erc.texi: Change ERCVER to 5.5.1.29.1.
* etc/ERC-NEWS: Add new section for 5.5.1.29.1, and mention the
default for `erc-join-buffer' has changed to `window-noselect'.
* lisp/erc/erc.el (erc-version): Update version to 5.5.1.29.1, and do
the same for the file's Version header.  The rationale for
incrementing the third, "patch" component is that the trailing "29.1"
portion merely indicates (1) that this exact version of ERC is
Emacs-only and does not exist on GNU ELPA and (2) that it contains at
most trivial changes, such as comments and documentation, atop its
corresponding GNU ELPA release, if any (here, there is no such
5.5.1 counterpart on ELPA).
(erc-join-buffer): Change default to `window-noselect'.
* test/lisp/erc/erc-scenarios-base-association.el
(erc-scenarios-networks-merge-server-track): Bind `erc-join-buffer'
to `bury'.
* test/lisp/erc/erc-scenarios-base-reconnect.el
(erc-scenarios-base-reconnect-options--buffer,
erc-scenarios-base-reconnect-options--default): Skip tests because
option `erc-reconnect-display' is bugged in ERC 5.5.  (Bug#62833)
---
 doc/misc/erc.texi                               |  2 +-
 etc/ERC-NEWS                                    | 12 ++++++++++--
 lisp/erc/erc.el                                 |  6 +++---
 test/lisp/erc/erc-scenarios-base-association.el |  1 +
 test/lisp/erc/erc-scenarios-base-reconnect.el   |  4 ++--
 5 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index 7f26b9ed181..ca294788c57 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -2,7 +2,7 @@
 @c %**start of header
 @setfilename ../../info/erc.info
 @settitle ERC Manual
-@set ERCVER 5.5.0.29.1
+@set ERCVER 5.5.1.29.1
 @set ERCDIST as distributed with Emacs @value{EMACSVER}
 @include docstyle.texi
 @syncodeindex fn cp
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 9672a86345b..684ff932d34 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -11,6 +11,16 @@ This file is about changes in ERC, the powerful, modular, and
 extensible IRC (Internet Relay Chat) client distributed with
 GNU Emacs since Emacs version 22.1.
 
+\f
+* Changes in ERC 5.5.1.29.1
+
+** Option 'erc-join-buffer' defaults to 'window-noselect'.
+In response to pushback about the switch from 'buffer' to 'bury', this
+option's default has once again been changed, this time exclusively
+for Emacs 29.  ERC now attempts to display a new or reassociated
+buffer in another window, creating one if necessary, even when the
+selected window already displays the buffer.
+
 \f
 * Changes in ERC 5.5
 
@@ -80,8 +90,6 @@ can now opt for an improved 'window-noselect' instead.  It still
 offers the same pronounced visual cue when connecting and joining but
 now avoids any hijacking of the active window as well.
 
-(Edited for ERC 5.5.0.29.1 in Emacs 29.1.)
-
 ** Improved handling of multiline prompt input.
 This means better detection and handling of intervening and trailing
 blanks when 'erc-send-whitespace-lines' is active.  New options have
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index c7e1645ec26..688bd1c4fd4 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -12,7 +12,7 @@
 ;;               David Edmondson (dme@dme.org)
 ;;               Michael Olson (mwolson@gnu.org)
 ;;               Kelvin White (kwhite@gnu.org)
-;; Version: 5.5.0.29.1
+;; Version: 5.5.1.29.1
 ;; Package-Requires: ((emacs "27.1") (compat "29.1.3.4"))
 ;; Keywords: IRC, chat, client, Internet
 ;; URL: https://www.gnu.org/software/emacs/erc.html
@@ -71,7 +71,7 @@
 (require 'iso8601)
 (eval-when-compile (require 'subr-x) (require 'url-parse))
 
-(defconst erc-version "5.5.0.29.1"
+(defconst erc-version "5.5.1.29.1"
   "This version of ERC.")
 
 (defvar erc-official-location
@@ -1469,7 +1469,7 @@ erc-default-port-tls
   "IRC port to use for encrypted connections if it cannot be \
 detected otherwise.")
 
-(defcustom erc-join-buffer 'bury
+(defcustom erc-join-buffer 'window-noselect
   "Determines how to display a newly created IRC buffer.
 
 The available choices are:
diff --git a/test/lisp/erc/erc-scenarios-base-association.el b/test/lisp/erc/erc-scenarios-base-association.el
index a40a4cb7550..3ac1089c541 100644
--- a/test/lisp/erc/erc-scenarios-base-association.el
+++ b/test/lisp/erc/erc-scenarios-base-association.el
@@ -207,6 +207,7 @@ erc-scenarios-networks-merge-server-track
        (dumb-server (erc-d-run "localhost" t 'track 'track))
        (port (process-contact dumb-server :service))
        (erc-server-flood-penalty 0.1)
+       (erc-join-buffer 'bury)
        (expect (erc-d-t-make-expecter)))
 
     (ert-info ("Connect")
diff --git a/test/lisp/erc/erc-scenarios-base-reconnect.el b/test/lisp/erc/erc-scenarios-base-reconnect.el
index 5b4dc549042..63ed21ba644 100644
--- a/test/lisp/erc/erc-scenarios-base-reconnect.el
+++ b/test/lisp/erc/erc-scenarios-base-reconnect.el
@@ -118,7 +118,7 @@ erc-scenarios-common--base-reconnect-options
 
 (ert-deftest erc-scenarios-base-reconnect-options--buffer ()
   :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
+  (ert-skip "Feature broken in ERC 5.5")
   (should-not erc-reconnect-display)
 
   ;; FooNet (the server buffer) is not switched to because it's
@@ -138,7 +138,7 @@ erc-scenarios-base-reconnect-options--buffer
 
 (ert-deftest erc-scenarios-base-reconnect-options--default ()
   :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'bury))
+  (ert-skip "Feature broken in ERC 5.5")
   (should-not erc-reconnect-display)
 
   (erc-scenarios-common--base-reconnect-options
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] ` <877csje0uz.fsf@neverwas.me>
@ 2023-06-04 15:28   ` Eli Zaretskii
       [not found]   ` <837csj5jsh.fsf@gnu.org>
  1 sibling, 0 replies; 15+ messages in thread
From: Eli Zaretskii @ 2023-06-04 15:28 UTC (permalink / raw)
  To: J.P.; +Cc: emacs-erc, 62833

> From: "J.P." <jp@neverwas.me>
> Cc: emacs-erc@gnu.org, 62833@debbugs.gnu.org
> Date: Sun, 04 Jun 2023 07:52:20 -0700
> 
> The solution to all this isn't straightforward, and we're making headway
> on it for ERC 5.6. In the meantime, I'm wondering if we might consider
> appeasing these disgruntled users somehow. Normally, I'd prefer just
> reverting back to `buffer', but because much has been made about its
> potential for causing mayhem via unintended sharing, I'm thinking we
> might change the default in Emacs 29 to `window-noselect'. This value
> tells ERC to show new buffers in a sibling window of the same vertical
> combination.

I don't use ERC, but how does it make sense to change the default so
close to a release?  I could understand reverting back to 'buffer',
which was used for a long time, but switching to a new default that
had no real testing period?  Are you absolutely sure this is a good
idea?  And on top of that, change it only for Emacs 29?





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found]   ` <837csj5jsh.fsf@gnu.org>
@ 2023-06-04 21:36     ` J.P.
  0 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-06-04 21:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-erc, 62833

Eli Zaretskii <eliz@gnu.org> writes:

>> From: "J.P." <jp@neverwas.me>
>> Cc: emacs-erc@gnu.org, 62833@debbugs.gnu.org
>> Date: Sun, 04 Jun 2023 07:52:20 -0700
>> 
>> The solution to all this isn't straightforward, and we're making headway
>> on it for ERC 5.6. In the meantime, I'm wondering if we might consider
>> appeasing these disgruntled users somehow. Normally, I'd prefer just
>> reverting back to `buffer', but because much has been made about its
>> potential for causing mayhem via unintended sharing, I'm thinking we
>> might change the default in Emacs 29 to `window-noselect'. This value
>> tells ERC to show new buffers in a sibling window of the same vertical
>> combination.
>
> I don't use ERC, but how does it make sense to change the default so
> close to a release?

Doing this is not without risk, but if it's any consolation, it's
perhaps somewhat less fraught given that `window-noselect' has always
been the default for `erc-auto-query', whose value is bound to
`erc-join-buffer' when displaying direct private messages. Since these
are not uncommon, we at least know that in one specific context, this
display style has stood the test of time.

> I could understand reverting back to 'buffer', which was used for a
> long time, but switching to a new default that had no real testing
> period? Are you absolutely sure this is a good idea?

There are no good ideas here, and I'd say it's a wash between `buffer'
and `window-noselect' depending on whose priorities we're intent on
favoring. I'd feel better about going back to `buffer' if those who
advocated for dropping it in bug#51753 would be willing to concede that
user feedback has proven that solution insufficient.

> And on top of that, change it only for Emacs 29?

Yes only for Emacs 29. This problem doesn't exist in Emacs 30.

In the end, this issue probably isn't worth much more of anyone's time.
Come late July-ish, we'll hopefully have ERC 5.6 out the door and can
just redirect folks there. And until then, this brief email exchange
should prove useful enough in fending off any related FUD on IRC.

To others listening in, I can only reiterate that this, along with other
20yr old UX bugs addressed in ERC 5.6 have been a major distraction that
I should have, in retrospect, had the wherewithal to ignore in lieu of
focusing on ERC's more existential problems, which I've been belaboring
for the better part of three years. The most pressing of these is, of
course, the adoption of essential protocol extensions, without which ERC
will not long survive.





^ permalink raw reply	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] <87leiuy3cv.fsf@neverwas.me>
                   ` (6 preceding siblings ...)
       [not found] ` <877csje0uz.fsf@neverwas.me>
@ 2023-06-09 13:50 ` J.P.
  2023-06-22 13:48   ` J.P.
  2023-07-08 14:19 ` J.P.
       [not found] ` <87r0pi32po.fsf@neverwas.me>
  9 siblings, 1 reply; 15+ messages in thread
From: J.P. @ 2023-06-09 13:50 UTC (permalink / raw)
  To: 62833; +Cc: emacs-erc

[-- Attachment #1: Type: text/plain, Size: 1766 bytes --]

v2 (Custom function choice). Fix multiple misspellings of the variable
`erc-buffer-display'. Simplify defcustom type from cons to plain
function. Convert `displayed' frames variant to function item. Add
escape hatch for no-op when buffer matches `window-buffer'.

"J.P." <jp@neverwas.me> writes:

> Actually, instead of a `display-buffer' action alone, I went for a cons
> of a `display-buffer'-compatible function, like `pop-to-buffer', and an
> action argument, together.
>
> [...]
>
> For this new variant I'm proposing, ERC calls the user's function with
> the newly created buffer and a possibly augmented version of the action
> that includes some well defined contextual clues in its alist. The
> latter are enumerated in the doc strings of the various user options.

I've simplified this further. Instead of a cons of a `display-buffer'-
compatible function and action, I think it's simpler to expect a
function that takes as arguments the new buffer and a "context alist"
(that can double as an "action alist"). This way, the user's code can
inspect the context, assemble the action parameter appropriately, and
dispatch `display-buffer' or similar as needed. I've also added some
ready-made function items, though possibly only as placeholders.

>> Although, at that point, why not just do
>>
>>   (display-buffer (let ((erc-join-buffer 'bury)) (erc-tls :server ...))
>>                   '(my-use-dedicated-frame (inhibit-same-window . t)))
>>
>> which has always been possible and is no more complicated?
>
> This would be preferable if we had more granular options that only
> affected a single context, such as something exclusively for
> non-interactive `erc-tls' invocations.

I've described this briefly in the doc string for `erc-buffer-display'.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v1-v2.diff --]
[-- Type: text/x-patch, Size: 22791 bytes --]

From 24dafe2771b6756c10a259ca5f5782f7d11f8fe7 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Fri, 9 Jun 2023 00:14:52 -0700
Subject: [PATCH 0/2] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (2):
  [5.6] Allow custom display-buffer actions in ERC
  [5.6] Move erc-reuse-frames choice to action function

 etc/ERC-NEWS               |  20 +--
 lisp/erc/erc-backend.el    |   9 +-
 lisp/erc/erc.el            | 249 ++++++++++++++++++++++++++-----------
 test/lisp/erc/erc-tests.el |  91 +++++++++++---
 4 files changed, 268 insertions(+), 101 deletions(-)

Interdiff:
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index edf9990d0de..0aa8b4fc634 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -59,14 +59,14 @@ option (now known as 'erc-receive-query-display') is nil, ERC uses
 via the new compatibility flag 'erc-receive-query-display-defer'.
 
 A few subtleties affecting the display of new or reassociated buffers
-have also emerged.  One involves buffers that already occupy the
-selected window.  ERC now treats these as deserving of an implicit
+have also been introduced.  One involves buffers that already occupy
+the selected window.  ERC now treats these as deserving of an implicit
 'bury'.  An escape hatch for this and most other baked-in behaviors is
 now available in the form of a new type variant recognized by all such
-options.  Indeed, users can now specify their own 'display-buffer'
-"actions" to exercise full control over nearly all buffer-display
-related decisions.  See the doc strings of 'erc-display-buffer' and
-friends for details.
+options.  That is, users can now specify their own 'display-buffer'
+function to exercise full control over nearly all buffer-display
+related decisions.  See the newly expanded doc strings of
+'erc-buffer-display' and friends for details.
 
 ** Setting a module's mode variable via Customize earns a warning.
 Trying and failing to activate a module via its minor mode's Custom
@@ -92,14 +92,6 @@ connectivity before attempting to reconnect in earnest.  See options
 'erc-server-reconnect-function' and 'erc-nickname-in-use-functions' to
 get started.
 
-** Easily constrain all ERC-related business to a dedicated frame.
-The option 'erc-reuse-frames' can now be set to 'displayed', which
-tells ERC to show new buffers in frames already occupied by buffers
-from the same connection.  This customization depends on the option
-'erc-buffer-display' (formerly 'erc-join-buffer') being set to
-'frame'.  If you find the name 'displayed' unhelpful, please suggest
-an alternative by writing to the mailing list.
-
 ** Module 'fill' can add a bit of space between messages.
 On graphical displays, it's now possible to add some breathing room
 around certain messages via the new option 'erc-fill-line-spacing'.
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 6a1c0745263..ee0fd7a549d 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -1690,7 +1690,7 @@ define-erc-response-handler
   nil
   (let ((chnl (erc-response.contents parsed))
         (buffer nil)
-        (erc--display-context `((erc-display-buffer . JOIN)
+        (erc--display-context `((erc-buffer-display . JOIN)
                                 ,@erc--display-context)))
     (pcase-let ((`(,nick ,login ,host)
                  (erc-parse-user (erc-response.sender parsed))))
@@ -1890,7 +1890,7 @@ define-erc-response-handler
              (noticep (string= cmd "NOTICE"))
              ;; S.B. downcase *both* tgt and current nick
              (privp (erc-current-nick-p tgt))
-             (erc--display-context `((erc-display-buffer . ,(intern cmd))
+             (erc--display-context `((erc-buffer-display . ,(intern cmd))
                                      ,@erc--display-context))
              s buffer
              fnick)
@@ -1906,6 +1906,8 @@ define-erc-response-handler
                               (and erc-ensure-target-buffer-on-privmsg
                                    (or erc-receive-query-display
                                        erc-join-buffer)))))
+                (push `(erc-receive-query-display . ,(intern cmd))
+                      erc--display-context)
                 (setq buffer (erc--open-target nick)))
             ;; A channel buffer has been killed but is still joined.
             (when erc-ensure-target-buffer-on-privmsg
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 2c5afc876d1..a7646f5e4c1 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1573,17 +1573,10 @@ erc--buffer-display-choices
            (const :tag "New frame" frame)
            (const :tag "Don't display" bury)
            (const :tag "Use current window" buffer)
-           (cons :tag "Custom action"
-                 (choice :tag "Display function"
-                         (function-item pop-to-buffer)
-                         (function-item display-buffer)
-                         function)
-                 (cons :tag "Action"
-                       (choice :tag "Action function"
-                               function (repeat function))
-                       (alist :tag "Action arguments"
-                              :key-type symbol
-                              :value-type (sexp :tag "Value")))))
+           (choice :tag "Show in granular, context-aware manner"
+                   (function-item erc-display-buffer-in-existing-frame)
+                   (function-item erc-display-buffer-ensuring-frame)
+                   (function :tag "User-provided function")))
   "Common choices for buffer-display options.")
 
 (defvaralias 'erc-join-buffer 'erc-buffer-display)
@@ -1601,25 +1594,30 @@ erc-buffer-display
   `frame'           - in another frame,
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
-  (FUNC . ACTION)   - call FUNC with buffer and ACTION
+  DISPLAY-FUNCTION  - called with a buffer and an ACTION-ALIST
 
-Here, FUNC signifies a function, like `display-buffer' or
-`pop-to-buffer', that takes a buffer and an ACTION as arguments.
-If non-nil, ACTION should be as described by the Info
-node `(elisp) Displaying Buffers'.  At times, ERC may add hints
-about the calling context to a given action alist.  Keys are
-symbols of options that have influenced the outcome, like
-`erc-buffer-display', and values are from a set of predefined
-constants.  In the case of this option, ERC uses the symbols
+Here, DISPLAY-FUNCTION should accept a buffer and an ACTION-ALIST
+of the kind described by the Info node `(elisp) Buffer Display
+Action Alists'.  Note that unlike a full display \"action\", this
+lacks a function (or list of functions) at its head.  At times,
+ERC may add hints about the calling context to this alist.  Keys
+are symbols of options themselves, like `erc-buffer-display'.
+Values are chosen from a set of predefined constants.  In the
+case of this option specifically, ERC uses the symbols
 
-  `JOIN',`PRIVMSG' `NOTICE', `erc', and `erc-tls'.
+  `JOIN', `PRIVMSG', `NOTICE', `erc', and `erc-tls'.
 
 The first three signify IRC commands received from the server and
 the rest entry-point commands responsible for the connection.
+When dealing with the latter, users may prefer to call
+DISPLAY-FUNCTION directly on a returned buffer (in this case,
+server buffer) because the context leading to its creation is
+plainly obvious.
 
 Note that when the selected window already shows the current
-buffer, ERC pretends this option's value is `bury' unless it's
-one of the two \"action\" conses described just above."
+buffer, ERC pretends this option's value is `bury' unless the
+variable `erc-skip-displaying-selected-window-buffer' is nil or
+the value of this option is DISPLAY-FUNCTION."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type (cons 'choice (nthcdr 2 erc--buffer-display-choices)))
@@ -1633,17 +1631,16 @@ erc-interactive-display
 See `erc-buffer-display' for a full description of available
 values.
 
-When the value is cons indicating a special display action, ERC
-injects a hint about the invocation context as an extra item in
-the action alist passed to `display-buffer' or `pop-to-buffer'.
-The item's key is the symbol `erc-interactive-display' and its
-value one of
+When the value is a user-provided function, ERC may inject a hint
+about the invocation context as an extra item in the alist passed
+as the second argument.  The hint item's key is the symbol
+`erc-interactive-display' and its value one of
 
   `/QUERY', `/JOIN', `url', `erc', or `erc-tls'.
 
-All are symbols indicating an inciting user action, such as
-issuing a slash command, clicking a URL hyperlink, or invoking an
-entry point."
+All are symbols indicating an inciting user action, such as the
+issuance of a slash command, the clicking of a URL hyperlink, or
+the invocation of an entry-point command."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
   :type erc--buffer-display-choices)
@@ -1655,11 +1652,10 @@ erc-reconnect-display
 successfully reinvoking `erc-tls' with similar arguments.  See
 `erc-buffer-display' for a description of possible values.
 
-When the value is cons indicating a special display action, ERC
-injects a hint about the calling context as an extra item in the
-action alist passed to `display-buffer' or `pop-to-buffer'.  The
-item's key is the symbol `erc-reconnect-display' and its value
-non-nil."
+When the value is function, ERC may inject a hint about the
+calling context as an extra item in the second, \"action alist\"
+argument.  The item's key is the symbol `erc-reconnect-display'
+and its value something non-nil."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type erc--buffer-display-choices)
@@ -1687,23 +1683,14 @@ erc-frame-dedicated-flag
 
 (defcustom erc-reuse-frames t
   "Determines whether new frames are always created.
-
-A value of t means only create a frame for undisplayed buffers.
-`displayed' means use any existing, potentially hidden frame
-already displaying a buffer from the same network context or,
-failing that, a frame showing any ERC buffer.  As a last resort,
-`displayed' defaults to the selected frame, except for brand new
-connections, for which the invoking frame is always used.  When
-this option is nil, a new frame is always created.
-
-Regardless of its value, this option is ignored unless
-`erc-join-buffer' is set to `frame'.  And like most options in
-the `erc-buffer' customize group, this has no effect on server
-buffers while reconnecting because those are always buried."
-  :package-version '(ERC . "5.6") ; FIXME sync on release
+Non-nil means only create a frame for undisplayed buffers.  Nil
+means always create a new frame.  Regardless of its value, this
+option is ignored unless `erc-join-buffer' is set to `frame'.
+And like most options in the `erc-buffer' customize group, this
+has no effect on server buffers while reconnecting because those
+are always buried."
   :group 'erc-buffers
-  :type '(choice boolean
-                 (const displayed)))
+  :type '(choice boolean))
 
 (defun erc-channel-p (channel)
   "Return non-nil if CHANNEL seems to be an IRC channel name."
@@ -2161,22 +2148,61 @@ erc--setup-buffer-first-window
 
 (defun erc--display-buffer-use-some-frame (buffer alist)
   "Maybe display BUFFER in an existing frame for the same connection.
-If performed, return window used; otherwise, return nil.  Forward ALIST
-to `display-buffer' machinery."
-  (when-let*
-      ((idp (lambda (value)
-              (and erc-networks--id
-                   (erc-networks--id-equal-p erc-networks--id value))))
-       (procp (lambda (frame)
-                (erc--setup-buffer-first-window frame idp erc-networks--id)))
-       (ercp (lambda (frame)
-               (erc--setup-buffer-first-window frame 'major-mode 'erc-mode)))
-       ((or (cdr (frame-list)) (funcall ercp (selected-frame)))))
-    ;; Workaround to avoid calling `window--display-buffer' directly
-    (or (display-buffer-use-some-frame buffer
-                                       `((frame-predicate . ,procp) ,@alist))
-        (display-buffer-use-some-frame buffer
-                                       `((frame-predicate . ,ercp) ,@alist)))))
+If performed, return winning window; otherwise, return nil.
+Forward ALIST to `display-buffer' machinery."
+  (let* ((idp (lambda (value)
+                (and erc-networks--id
+                     (erc-networks--id-equal-p erc-networks--id value))))
+         (procp (lambda (frame)
+                  (erc--setup-buffer-first-window frame idp erc-networks--id)))
+         (anyp (assq 'erc--frame-any alist))
+         (ercp (lambda (frame)
+                 (let ((val (erc--setup-buffer-first-window frame 'major-mode
+                                                            'erc-mode)))
+                   (if anyp val (not val)))))
+         new)
+    (when (or (cdr (frame-list))
+              (funcall ercp (selected-frame))
+              (and (not anyp)
+                   (push `(pop-up-frame-parameters ,erc-frame-alist) alist)
+                   (setq new t)))
+      (or (and new (display-buffer-pop-up-frame buffer alist))
+          ;; Workaround to avoid calling `window--display-buffer' directly.
+          (display-buffer-use-some-frame buffer `((frame-predicate . ,procp)
+                                                  ,@alist))
+          (display-buffer-use-some-frame buffer `((frame-predicate . ,ercp)
+                                                  ,@alist))))))
+
+(defun erc-display-buffer-in-existing-frame (buffer alist)
+  "Display BUFFER in an existing frame with others from ERC.
+Use any existing, potentially hidden frame already displaying a
+buffer from the same network context or, failing that, a frame
+showing any ERC buffer.  As a last resort, use the selected
+frame, except for brand new connections, which always get the
+invoking frame.  Pass ALIST along to `display-buffer'."
+  (unless (get-buffer-window buffer t)
+    (display-buffer buffer `((erc--display-buffer-use-some-frame)
+                             (erc--frame-any . t)
+                             (inhibit-switch-frame . t)
+                             (inhibit-same-window . t)
+                             ,@alist))))
+
+(defun erc-display-buffer-ensuring-frame (buffer alist)
+  "Display BUFFER in a frame with others from its connection.
+Use any frame already displaying a buffer from the same network
+context.  Failing that, create one unless some existing frame is
+free of any ERC buffers, in which case, use that.  Pass ALIST
+along to `display-buffer'.
+
+WARNING: if a frame for the existing connection does not exist,
+this function may raise a new one and steal focus."
+  (unless (get-buffer-window buffer t)
+    (let ((interactivep (alist-get 'erc-interactive-display alist)))
+      (display-buffer buffer `((erc--display-buffer-use-some-frame)
+                               ,@(and (not interactivep)
+                                      '((inhibit-same-window . t)))
+                               (inhibit-switch-frame . t)
+                               ,@alist)))))
 
 (defvar erc--setup-buffer-hook nil
   "Internal hook for module setup involving windows and frames.")
@@ -2189,19 +2215,24 @@ erc--display-context
 to the user-provided alist when calling `pop-to-buffer' or
 `display-buffer'.")
 
+(defvar erc-skip-displaying-selected-window-buffer t
+  "Whether to forgo showing a buffer that's already being displayed.
+But only in the selected window.")
+(make-obsolete 'erc-show-already-displayed-buffer
+               "non-nil behavior to be made permanent" "30.1")
+
 (defun erc-setup-buffer (buffer)
   "Consults `erc-join-buffer' to find out how to display `BUFFER'."
   (pcase (if (zerop (erc-with-server-buffer
                       erc--server-last-reconnect-count))
              erc-join-buffer
            (or erc-reconnect-display erc-join-buffer))
-    ((and (or `(,disp-fn) `(,disp-fn . (,actfns . ,alist)))
-          (guard (functionp disp-fn)))
-     (let ((context erc--display-context))
-       (unless (zerop erc--server-last-reconnect-count)
-         (push '(erc-reconnect-display . t) context))
-       (funcall disp-fn buffer `(,actfns ,@context ,@alist))))
-    ((guard (eq (window-buffer) buffer)))
+    ((and (pred functionp) disp-fn (let context erc--display-context))
+     (unless (zerop erc--server-last-reconnect-count)
+       (push '(erc-reconnect-display . t) context))
+     (funcall disp-fn buffer context))
+    ((guard (and erc-skip-displaying-selected-window-buffer
+                 (eq (window-buffer) buffer))))
     ('window
      (if (active-minibuffer-window)
          (display-buffer buffer)
@@ -2211,21 +2242,15 @@ erc-setup-buffer
     ('bury
      nil)
     ('frame
-     (cond
-      ((and (eq erc-reuse-frames 'displayed)
-            (not (get-buffer-window buffer t)))
-       (display-buffer buffer '((erc--display-buffer-use-some-frame)
-                                (inhibit-switch-frame . t)
-                                (inhibit-same-window . t))))
-      ((or (not erc-reuse-frames)
-           (not (get-buffer-window buffer t)))
+     (when (or (not erc-reuse-frames)
+               (not (get-buffer-window buffer t)))
        (let ((frame (make-frame (or erc-frame-alist
                                     default-frame-alist))))
          (raise-frame frame)
          (select-frame frame))
        (switch-to-buffer buffer)
        (when erc-frame-dedicated-flag
-         (set-window-dedicated-p (selected-window) t)))))
+         (set-window-dedicated-p (selected-window) t))))
     (_
      (if (active-minibuffer-window)
          (display-buffer buffer)
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 38186467de1..eb1c8f4ead2 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -627,7 +627,7 @@ erc-reuse-frames--t
 
 (defun erc-tests--erc-reuse-frames--displayed-single (_ server-name chan-name)
 
-  (should (eq erc-reuse-frames 'displayed))
+  (should (eq erc-buffer-display #'erc-display-buffer-in-existing-frame))
 
   (ert-info ("New server buffer shown in existing frame")
     (with-current-buffer (generate-new-buffer server-name)
@@ -665,7 +665,7 @@ erc-reuse-frames--displayed-single
   :tags '(:unstable :expensive-test)
   (erc-tests--erc-reuse-frames
    (lambda (orig-frame)
-     (let ((erc-reuse-frames 'displayed))
+     (let ((erc-buffer-display #'erc-display-buffer-in-existing-frame))
        (erc-tests--erc-reuse-frames--displayed-single orig-frame
                                                       "server" "#chan")
        (should-not (cdr (frame-list))))
@@ -683,7 +683,7 @@ erc-tests--assert-server-split
     buffer-frame))
 
 (defun erc-tests--erc-reuse-frames--displayed-double (_)
-  (should (eq erc-reuse-frames 'displayed))
+  (should (eq erc-buffer-display #'erc-display-buffer-in-existing-frame))
 
   (make-frame '((name . "other")))
   (select-frame (make-frame '((name . "server"))) 'no-record)
@@ -727,7 +727,7 @@ erc-reuse-frames--displayed-double
   :tags '(:unstable :expensive-test)
   (erc-tests--erc-reuse-frames
    (lambda (orig-frame)
-     (let ((erc-reuse-frames 'displayed))
+     (let ((erc-buffer-display #'erc-display-buffer-in-existing-frame))
        (erc-tests--erc-reuse-frames--displayed-double orig-frame))
      (dolist (b '("server" "#chan"))
        (kill-buffer b)))))
@@ -796,7 +796,7 @@ erc-reuse-frames--displayed-full
   :tags '(:unstable :expensive-test)
   (erc-tests--erc-reuse-frames
    (lambda (orig-frame)
-     (let ((erc-reuse-frames 'displayed))
+     (let ((erc-buffer-display #'erc-display-buffer-in-existing-frame))
        (erc-tests--erc-reuse-frames--displayed-full orig-frame))
      (dolist (b '("server" "ircd" "bob" "alice" "#spam" "#chan"))
        (kill-buffer b)))))
@@ -811,8 +811,6 @@ erc-setup-buffer--custom-action
         calls)
     (cl-letf (((symbol-function 'switch-to-buffer) ; regression
                (lambda (&rest r) (push (cons 'switch-to-buffer r) calls)))
-              ((symbol-function 'pop-to-buffer)
-               (lambda (&rest r) (push (cons 'pop-to-buffer r) calls)))
               ((symbol-function 'erc--test-fun)
                (lambda (&rest r) (push (cons 'erc--test-fun r) calls)))
               ((symbol-function 'display-buffer)
@@ -827,42 +825,21 @@ erc-setup-buffer--custom-action
 
       ;; `display-buffer'
       (let ((erc--display-context '((erc-buffer-display . 1)))
-            (erc-join-buffer
-             '(display-buffer display-buffer-no-window (allow-no-window . t))))
+            (erc-join-buffer 'erc--test-fun))
         (erc-setup-buffer mbuf)
-        (should (equal `(display-buffer
-                         ,mbuf
-                         (display-buffer-no-window (erc-buffer-display . 1)
-                                                   (allow-no-window . t)))
+        (should (equal `(erc--test-fun ,mbuf ((erc-buffer-display . 1)))
                        (pop calls)))
         (should-not calls))
 
       ;; `pop-to-buffer' with `erc-reconnect-display'
       (let* ((erc--server-last-reconnect-count 1)
              (erc--display-context '((erc-buffer-display . 1)))
-             (erc-reconnect-display
-              '(pop-to-buffer . ((display-buffer-same-window)))))
+             (erc-reconnect-display 'erc--test-fun))
         (erc-setup-buffer mbuf)
-        (should (equal `(pop-to-buffer ,mbuf
-                                       ((display-buffer-same-window)
-                                        (erc-reconnect-display . t)
-                                        (erc-buffer-display . 1)))
+        (should (equal `(erc--test-fun ,mbuf ((erc-reconnect-display . t)
+                                              (erc-buffer-display . 1)))
                        (pop calls)))
-        (should-not calls))
-
-      ;; Undocumented variants
-      (pcase-dolist (`(,want ,got)
-                     `(((pop-to-buffer) (nil))
-                       ((pop-to-buffer afun) (afun))
-                       ((pop-to-buffer (afun)) ((afun)))
-                       ((erc--test-fun (afun)) ((afun)))
-                       ((pop-to-buffer afun (a . 1)) (afun (a . 1)))
-                       ((pop-to-buffer (afun) (a . 1)) ((afun) (a . 1)))))
-        (let* ((erc-buffer-display want))
-          (ert-info ((format "want: %S, got: %S, calls: %S" want got calls))
-            (erc-setup-buffer mbuf)
-            (should (equal (list (car want) mbuf got) (pop calls)))
-            (should-not calls)))))
+        (should-not calls)))
 
     (should (eq owin (selected-window)))
     (should (eq obuf (window-buffer)))))
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
  2023-06-09 13:50 ` J.P.
@ 2023-06-22 13:48   ` J.P.
  0 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-06-22 13:48 UTC (permalink / raw)
  To: 62833; +Cc: emacs-erc

[-- Attachment #1: Type: text/plain, Size: 3834 bytes --]

v3 (Custom function choice). Change interface of function choice in
Custom `:type' spec to support full `display-buffer' action parameter.
Cancel second patch focusing on frames-related display styles (work to
continue in a separate bug).

"J.P." <jp@neverwas.me> writes:

> I've simplified this further. Instead of a cons of a `display-buffer'-
> compatible function and action, I think it's simpler to expect a
> function that takes as arguments the new buffer and a "context alist"
> (that can double as an "action alist"). This way, the user's code can
> inspect the context, assemble the action parameter appropriately, and
> dispatch `display-buffer' or similar as needed.

I've flip-flopped on this completely and have reverted back to favoring
the idea of having user implementations (and `function-item' offerings)
expect a full `display-buffer' action rather than just the alist portion
alone. As explained up thread, I originally figured it'd be easier on
users if we gave them just the alist to ponder rather than something
requiring destructuring and recomposing. But now I think it's cleaner to
sacrifice that minor convenience in favor of having folks mentally
associate these functions with `display-buffer', `pop-to-buffer', etc.,
since they roughly serve the same purpose (as opposed to the "action
functions" they consume).

Along with this U-turn, I think we ought to consider exporting the new
variable `erc--display-context'. I've left it internal, for now, but
making it public would allow folks with an existing collection of
`display-buffer' actions to write simple match predicates (for
`display-buffer-alist') that decide things based on calling context.

> I've also added some ready-made function items, though possibly only
> as placeholders.

Actually, looking back on

  bug#55540: 29.0.50; ERC launches autojoin-channels in current frame

which led to a change that's been on HEAD for two months now

  https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=0e4c07dc

I've come to the belated realization that this feature is not all that
useful in and of itself. For whatever reason, I fixated on fulfilling
the requirements described in the bug without questioning whether that
alone would result in a satisfactory user experience. Indeed, after
actually trying out the feature from the perspective of someone wanting
to conduct all ERC-related business in a separate frame, merely offering
a display style oriented toward that end seems wholly insufficient.

For example, buffers spawned in other contexts, via related options,
such as `erc-receive-query-display', won't find their way to the correct
frame unless they've also been customized accordingly. Similarly, once a
buffer is correctly routed to a "dedicated" frame, it's unclear how the
user wants it displayed. I suppose we could affix existing options as
combined choices, like

  `erc-use-existing-frame-buffer'
  `erc-use-existing-frame-window'
  `erc-use-existing-frame-window-no-select'
  `erc-create-new-frame-buffer'
  ...

but that adds a lot of clutter and isn't great for maintenance. There's
also the issue of integrating with other modules, like `erc-track',
whose users likely want the mode line to only show changes for
associated buffers and C-SPC to limit its switching between those.

In sum, I think the basic idea of being able to marry new ERC buffers to
an ERC-managed frame is a good one, but it may be orthogonal to our
traditional idea of display styles. I'm now leaning toward exploring
something like a separate module to make the experience feel more
integrated and less bolted on. I therefore move that we revert the
change cited above in the meantime so people don't get used to it, and
then pursue a more comprehensive solution to frame-oriented
buffer-display styles in this (or some related) bug. Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v2-v3.diff --]
[-- Type: text/x-patch, Size: 27473 bytes --]

From 78bba39e65b168a117c077518f2aee2a8465e470 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Wed, 21 Jun 2023 06:36:45 -0700
Subject: [PATCH 0/2] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (2):
  Revert "Allow erc-reuse-frames to favor connections"
  [5.6] Allow custom display-buffer actions in ERC

 etc/ERC-NEWS               |  20 +-
 lisp/erc/erc-backend.el    |   9 +-
 lisp/erc/erc.el            | 217 ++++++++++++---------
 test/lisp/erc/erc-tests.el | 376 +++++++------------------------------
 4 files changed, 212 insertions(+), 410 deletions(-)

Interdiff:
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 6cec2919460..9177f61f8cc 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -58,8 +58,8 @@ option (now known as 'erc-receive-query-display') is nil, ERC uses
 'erc-interactive-display'.  The old nil behavior can still be gotten
 via the new compatibility flag 'erc-receive-query-display-defer'.
 
-A few subtleties affecting the display of new or reassociated buffers
-have also been introduced.  One involves buffers that already occupy
+This release also introduces a few subtleties affecting the display of
+new or reassociated buffers.  One involves buffers that already occupy
 the selected window.  ERC now treats these as deserving of an implicit
 'bury'.  An escape hatch for this and most other baked-in behaviors is
 now available in the form of a new type variant recognized by all such
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index a0e89795a57..f1dafd0dbf8 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1555,10 +1555,10 @@ erc--buffer-display-choices
            (const :tag "New frame" frame)
            (const :tag "Don't display" bury)
            (const :tag "Use current window" buffer)
-           (choice :tag "Show in granular, context-aware manner"
-                   (function-item erc-display-buffer-in-existing-frame)
-                   (function-item erc-display-buffer-ensuring-frame)
-                   (function :tag "User-provided function")))
+           (choice :tag "Defer to a display function"
+                   (function-item display-buffer)
+                   (function-item pop-to-buffer)
+                   (function :tag "User-defined")))
   "Common choices for buffer-display options.")
 
 (defvaralias 'erc-join-buffer 'erc-buffer-display)
@@ -1576,24 +1576,23 @@ erc-buffer-display
   `frame'           - in another frame,
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
-  DISPLAY-FUNCTION  - called with a buffer and an ACTION-ALIST
+  DISPLAY-FUNCTION  - a `display-buffer'-like function
 
-Here, DISPLAY-FUNCTION should accept a buffer and an ACTION-ALIST
-of the kind described by the Info node `(elisp) Buffer Display
-Action Alists'.  Note that unlike a full display \"action\", this
-lacks a function (or list of functions) at its head.  At times,
-ERC may add hints about the calling context to this alist.  Keys
-are symbols of options themselves, like `erc-buffer-display'.
-Values are chosen from a set of predefined constants.  In the
-case of this option specifically, ERC uses the symbols
+Here, DISPLAY-FUNCTION should accept a buffer and an ACTION of
+the kind described by the Info node `(elisp) Choosing Window'.
+At times, ERC may add hints about the calling context to the
+ACTION's alist.  Keys are symbols of user options, like
+`erc-buffer-display', and values are predefined constants
+specific to each.  For this particular option, possible values
+include the symbols
 
   `JOIN', `PRIVMSG', `NOTICE', `erc', and `erc-tls'.
 
 The first three signify IRC commands received from the server and
 the rest entry-point commands responsible for the connection.
-When dealing with the latter, users may prefer to call
-DISPLAY-FUNCTION directly on a returned buffer (in this case,
-server buffer) because the context leading to its creation is
+When dealing with the latter two, users may prefer to call
+DISPLAY-FUNCTION directly on (server) buffers returned by these
+entry points because the context leading to their creation is
 plainly obvious.
 
 Note that when the selected window already shows the current
@@ -1614,9 +1613,9 @@ erc-interactive-display
 values.
 
 When the value is a user-provided function, ERC may inject a hint
-about the invocation context as an extra item in the alist passed
-as the second argument.  The hint item's key is the symbol
-`erc-interactive-display' and its value one of
+about the invocation context as an extra item in the \"action
+alist\" included as part of the second argument.  The item's key
+is the symbol `erc-interactive-display' and its value one of
 
   `/QUERY', `/JOIN', `url', `erc', or `erc-tls'.
 
@@ -1635,9 +1634,9 @@ erc-reconnect-display
 `erc-buffer-display' for a description of possible values.
 
 When the value is function, ERC may inject a hint about the
-calling context as an extra item in the second, \"action alist\"
-argument.  The item's key is the symbol `erc-reconnect-display'
-and its value something non-nil."
+calling context as an extra item in the alist making up the tail
+of the second, \"action\" argument.  The item's key is the symbol
+`erc-reconnect-display' and its value something non-nil."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type erc--buffer-display-choices)
@@ -1666,13 +1665,13 @@ erc-frame-dedicated-flag
 (defcustom erc-reuse-frames t
   "Determines whether new frames are always created.
 Non-nil means only create a frame for undisplayed buffers.  Nil
-means always create a new frame.  Regardless of its value, this
-option is ignored unless `erc-join-buffer' is set to `frame'.
-And like most options in the `erc-buffer' customize group, this
-has no effect on server buffers while reconnecting because those
-are always buried."
+means always create a new frame.  Regardless of its value, ERC
+ignores this option unless `erc-join-buffer' is `frame'.  And
+like most options in the `erc-buffer' customize group, this has
+no effect on server buffers while reconnecting because ERC always
+buries those."
   :group 'erc-buffers
-  :type '(choice boolean))
+  :type 'boolean)
 
 (defun erc-channel-p (channel)
   "Return non-nil if CHANNEL seems to be an IRC channel name."
@@ -2125,74 +2124,6 @@ erc--updating-modules-p
 confidently call (erc-foo-mode 1) without having to learn
 anything about the dependency's implementation.")
 
-(defun erc--setup-buffer-first-window (frame a b)
-  (catch 'found
-    (walk-window-tree
-     (lambda (w)
-       (when (cond ((functionp a) (with-current-buffer (window-buffer w)
-                                    (funcall a b)))
-                   (t (eq (buffer-local-value a (window-buffer w)) b)))
-         (throw 'found t)))
-     frame nil 0)))
-
-(defun erc--display-buffer-use-some-frame (buffer alist)
-  "Maybe display BUFFER in an existing frame for the same connection.
-If performed, return winning window; otherwise, return nil.
-Forward ALIST to `display-buffer' machinery."
-  (let* ((idp (lambda (value)
-                (and erc-networks--id
-                     (erc-networks--id-equal-p erc-networks--id value))))
-         (procp (lambda (frame)
-                  (erc--setup-buffer-first-window frame idp erc-networks--id)))
-         (anyp (assq 'erc--frame-any alist))
-         (ercp (lambda (frame)
-                 (let ((val (erc--setup-buffer-first-window frame 'major-mode
-                                                            'erc-mode)))
-                   (if anyp val (not val)))))
-         new)
-    (when (or (cdr (frame-list))
-              (funcall ercp (selected-frame))
-              (and (not anyp)
-                   (push `(pop-up-frame-parameters ,erc-frame-alist) alist)
-                   (setq new t)))
-      (or (and new (display-buffer-pop-up-frame buffer alist))
-          ;; Workaround to avoid calling `window--display-buffer' directly.
-          (display-buffer-use-some-frame buffer `((frame-predicate . ,procp)
-                                                  ,@alist))
-          (display-buffer-use-some-frame buffer `((frame-predicate . ,ercp)
-                                                  ,@alist))))))
-
-(defun erc-display-buffer-in-existing-frame (buffer alist)
-  "Display BUFFER in an existing frame with others from ERC.
-Use any existing, potentially hidden frame already displaying a
-buffer from the same network context or, failing that, a frame
-showing any ERC buffer.  As a last resort, use the selected
-frame, except for brand new connections, which always get the
-invoking frame.  Pass ALIST along to `display-buffer'."
-  (unless (get-buffer-window buffer t)
-    (display-buffer buffer `((erc--display-buffer-use-some-frame)
-                             (erc--frame-any . t)
-                             (inhibit-switch-frame . t)
-                             (inhibit-same-window . t)
-                             ,@alist))))
-
-(defun erc-display-buffer-ensuring-frame (buffer alist)
-  "Display BUFFER in a frame with others from its connection.
-Use any frame already displaying a buffer from the same network
-context.  Failing that, create one unless some existing frame is
-free of any ERC buffers, in which case, use that.  Pass ALIST
-along to `display-buffer'.
-
-WARNING: if a frame for the existing connection does not exist,
-this function may raise a new one and steal focus."
-  (unless (get-buffer-window buffer t)
-    (let ((interactivep (alist-get 'erc-interactive-display alist)))
-      (display-buffer buffer `((erc--display-buffer-use-some-frame)
-                               ,@(and (not interactivep)
-                                      '((inhibit-same-window . t)))
-                               (inhibit-switch-frame . t)
-                               ,@alist)))))
-
 (defvar erc--setup-buffer-hook nil
   "Internal hook for module setup involving windows and frames.")
 
@@ -2210,6 +2141,11 @@ erc-skip-displaying-selected-window-buffer
 (make-obsolete 'erc-show-already-displayed-buffer
                "non-nil behavior to be made permanent" "30.1")
 
+(defvar-local erc--display-buffer-action nil
+  "The value of `display-buffer-overriding-action' when non-nil.
+Influences the displaying of new or reassociated ERC buffers.
+Reserved for use by built-in modules.")
+
 (defun erc-setup-buffer (buffer)
   "Consults `erc-join-buffer' to find out how to display `BUFFER'."
   (pcase (if (zerop (erc-with-server-buffer
@@ -2219,7 +2155,7 @@ erc-setup-buffer
     ((and (pred functionp) disp-fn (let context erc--display-context))
      (unless (zerop erc--server-last-reconnect-count)
        (push '(erc-reconnect-display . t) context))
-     (funcall disp-fn buffer context))
+     (funcall disp-fn buffer (cons nil context)))
     ((guard (and erc-skip-displaying-selected-window-buffer
                  (eq (window-buffer) buffer))))
     ('window
@@ -2419,8 +2355,10 @@ erc-open
         ;; we can't log to debug buffer, it may not exist yet
         (message "erc: old buffer %s, switching to %s"
                  old-buffer buffer))
-      (erc-setup-buffer buffer)
-      (run-hooks 'erc--setup-buffer-hook))
+      (let ((display-buffer-overriding-action
+             (or erc--display-buffer-action display-buffer-overriding-action)))
+        (erc-setup-buffer buffer)
+        (run-hooks 'erc--setup-buffer-hook)))
 
     buffer))
 
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 6e45a9731ed..6dbd0d4704f 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -503,309 +503,6 @@ erc--switch-to-buffer
     (dolist (b '("server" "other" "#chan" "#foo" "#fake"))
       (kill-buffer b))))
 
-(defun erc-tests--run-in-term (&optional debug)
-  (let* ((default-directory (getenv "EMACS_TEST_DIRECTORY"))
-         (emacs (expand-file-name invocation-name invocation-directory))
-         (process-environment (cons "ERC_TESTS_SUBPROCESS=1"
-                                    process-environment))
-         (name (ert-test-name (ert-running-test)))
-         (temp-file (make-temp-file "erc-term-test-"))
-         (cmd `(let ((stats 1))
-                 (setq enable-dir-local-variables nil)
-                 (unwind-protect
-                     (setq stats (ert-run-tests-batch ',name))
-                   (unless ',debug
-                     (let ((buf (with-current-buffer (messages-buffer)
-                                  (buffer-string))))
-                       (with-temp-file ,temp-file
-                         (insert buf)))
-                     (kill-emacs (ert-stats-completed-unexpected stats))))))
-         ;; `ert-test' object in Emacs 29 has a `file-name' field
-         (file-name (symbol-file name 'ert--test))
-         (default-directory (expand-file-name (file-name-directory file-name)))
-         (package (if-let* ((found (getenv "ERC_PACKAGE_NAME"))
-                            ((string-prefix-p "erc-" found)))
-                      (intern found)
-                    'erc))
-         (setup (and (featurep 'compat)
-                     `(progn
-                        (require 'package)
-                        (let ((package-load-list '((compat t) (,package t))))
-                          (package-initialize)))))
-         ;; Make subprocess terminal bigger than controlling.
-         (buf (cl-letf (((symbol-function 'window-screen-lines)
-                         (lambda () 20))
-                        ((symbol-function 'window-max-chars-per-line)
-                         (lambda () 40)))
-                (make-term (symbol-name name) emacs nil "-Q" "-nw"
-                           "-eval" (prin1-to-string setup)
-                           "-l" file-name "-eval" (format "%S" cmd))))
-         (proc (get-buffer-process buf))
-         (err (lambda ()
-                (with-temp-buffer
-                  (insert-file-contents temp-file)
-                  (message "Subprocess: %s" (buffer-string))
-                  (delete-file temp-file)))))
-    (with-current-buffer buf
-      (set-process-query-on-exit-flag proc nil)
-      (with-timeout (10 (funcall err) (error "Timed out awaiting result"))
-        (while (process-live-p proc)
-          (accept-process-output proc 0.1)))
-      (while (accept-process-output proc))
-      (goto-char (point-min))
-      ;; Otherwise gives process exited abnormally with exit-code >0
-      (unless (search-forward (format "Process %s finished" name) nil t)
-        (funcall err)
-        (ert-fail (when (search-forward "exited" nil t)
-                    (buffer-substring-no-properties (line-beginning-position)
-                                                    (line-end-position)))))
-      (delete-file temp-file)
-      (when noninteractive
-        (kill-buffer)))))
-
-(defun erc-tests--servars (source &rest vars)
-  (unless (bufferp source)
-    (setq source (get-buffer source)))
-  (dolist (var vars)
-    (should (local-variable-if-set-p var))
-    (set var (buffer-local-value var source))))
-
-(defun erc-tests--erc-reuse-frames (test &optional debug)
-  (if (and (or debug noninteractive) (not (getenv "ERC_TESTS_SUBPROCESS")))
-      (progn
-        (when (memq system-type '(windows-nt ms-dos))
-          (ert-skip "System must be UNIX"))
-        (erc-tests--run-in-term debug))
-    (should-not erc-frame-dedicated-flag)
-    (should (eq erc-reuse-frames t))
-    (let ((erc-join-buffer 'frame)
-          (erc-reuse-frames t)
-          (erc-frame-alist nil)
-          (orig-frame (selected-frame))
-          erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook)
-      (delete-other-frames)
-      (delete-other-windows)
-      (set-window-buffer (selected-window) "*scratch*")
-      (funcall test orig-frame)
-      (delete-other-frames orig-frame)
-      (delete-other-windows))))
-
-;; TODO add cases for frame-display behavior while reconnecting
-
-(defun erc-tests--erc-reuse-frames--t (_)
-  (ert-info ("New server buffer creates and raises second frame")
-    (with-current-buffer (generate-new-buffer "server")
-      (erc-mode)
-      (setq erc-server-process (start-process "server"
-                                              (current-buffer) "sleep" "10")
-            erc-frame-alist (cons '(name . "server") default-frame-alist)
-            erc-network 'foonet
-            erc-networks--id (erc-networks--id-create nil)
-            erc--server-last-reconnect-count 0)
-      (set-process-buffer erc-server-process (current-buffer))
-      (set-process-query-on-exit-flag erc-server-process nil)
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should (equal "server" (frame-parameter (window-frame) 'name)))
-      (should (get-buffer-window (current-buffer) t))))
-
-  (ert-info ("New channel creates and raises third frame")
-    (with-current-buffer (generate-new-buffer "#chan")
-      (erc-mode)
-      (erc-tests--servars "server" 'erc-server-process 'erc-networks--id
-                          'erc-network)
-      (setq erc-frame-alist (cons '(name . "#chan") default-frame-alist)
-            erc-default-recipients '("#chan"))
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should (equal "#chan" (frame-parameter (window-frame) 'name)))
-      (should (get-buffer-window (current-buffer) t))
-      (should (cddr (frame-list))))))
-
-(ert-deftest erc-reuse-frames--t ()
-  :tags '(:unstable :expensive-test)
-  (erc-tests--erc-reuse-frames
-   (lambda (orig-frame)
-     (erc-tests--erc-reuse-frames--t orig-frame)
-     (dolist (b '("server" "#chan"))
-       (kill-buffer b)))))
-
-(defun erc-tests--erc-reuse-frames--displayed-single (_ server-name chan-name)
-
-  (should (eq erc-buffer-display #'erc-display-buffer-in-existing-frame))
-
-  (ert-info ("New server buffer shown in existing frame")
-    (with-current-buffer (generate-new-buffer server-name)
-      (erc-mode)
-      (setq erc-server-process (start-process server-name (current-buffer)
-                                              "sleep" "10")
-            erc-frame-alist (cons `(name . ,server-name) default-frame-alist)
-            erc-network (make-symbol server-name)
-            erc-server-current-nick "tester"
-            erc-networks--id (erc-networks--id-create nil)
-            erc--server-last-reconnect-count 0)
-      (set-process-buffer erc-server-process (current-buffer))
-      (set-process-query-on-exit-flag erc-server-process nil)
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should-not (equal server-name (frame-parameter (window-frame) 'name)))
-      ;; New server buffer window appears in split below ERT/scratch
-      (should (get-buffer-window (current-buffer) t))))
-
-  (ert-info ("New channel shown in existing frame")
-    (with-current-buffer (generate-new-buffer chan-name)
-      (erc-mode)
-      (erc-tests--servars server-name 'erc-server-process 'erc-networks--id
-                          'erc-network)
-      (setq erc-frame-alist (cons `(name . ,chan-name) default-frame-alist)
-            erc-default-recipients (list chan-name))
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should-not (equal chan-name (frame-parameter (window-frame) 'name)))
-      ;; New channel buffer replaces server in lower window
-      (should (get-buffer-window (current-buffer) t))
-      (should-not (get-buffer-window server-name t)))))
-
-(ert-deftest erc-reuse-frames--displayed-single ()
-  :tags '(:unstable :expensive-test)
-  (erc-tests--erc-reuse-frames
-   (lambda (orig-frame)
-     (let ((erc-buffer-display #'erc-display-buffer-in-existing-frame))
-       (erc-tests--erc-reuse-frames--displayed-single orig-frame
-                                                      "server" "#chan")
-       (should-not (cdr (frame-list))))
-     (dolist (b '("server" "#chan"))
-       (kill-buffer b)))))
-
-(defun erc-tests--assert-server-split (buffer-or-name frame-name)
-  ;; Assert current buffer resides on one side of a horizontal split
-  ;; in the "server" frame but is not selected.
-  (let* ((buffer-window (get-buffer-window buffer-or-name t))
-         (buffer-frame (window-frame buffer-window)))
-    (should (equal frame-name (frame-parameter buffer-frame 'name)))
-    (should (memq buffer-window (car-safe (window-tree buffer-frame))))
-    (should-not (eq buffer-window (frame-selected-window)))
-    buffer-frame))
-
-(defun erc-tests--erc-reuse-frames--displayed-double (_)
-  (should (eq erc-buffer-display #'erc-display-buffer-in-existing-frame))
-
-  (make-frame '((name . "other")))
-  (select-frame (make-frame '((name . "server"))) 'no-record)
-  (set-window-buffer (selected-window) "*scratch*") ; invokes `erc'
-
-  ;; A user invokes an entry point and switches immediately to a new
-  ;; frame before autojoin kicks in (bug#55540).
-
-  (ert-info ("New server buffer shown in selected frame")
-    (with-current-buffer (generate-new-buffer "server")
-      (erc-mode)
-      (setq erc-server-process (start-process "server" (current-buffer)
-                                              "sleep" "10")
-            erc-network 'foonet
-            erc-server-current-nick "tester"
-            erc-networks--id (erc-networks--id-create nil)
-            erc--server-last-reconnect-count 0)
-      (set-process-buffer erc-server-process (current-buffer))
-      (set-process-query-on-exit-flag erc-server-process nil)
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should (equal "server" (frame-parameter (window-frame) 'name)))
-      (should (get-buffer-window (current-buffer) t))))
-
-  (select-frame-by-name "other")
-
-  (ert-info ("New channel shown in dedicated frame")
-    (with-current-buffer (generate-new-buffer "#chan")
-      (erc-mode)
-      (erc-tests--servars "server" 'erc-server-process 'erc-networks--id
-                          'erc-network)
-      (setq erc-frame-alist (cons '(name . "#chan") default-frame-alist)
-            erc-default-recipients '("#chan"))
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (erc-tests--assert-server-split (current-buffer) "server")
-      ;; New channel buffer replaces server in lower window of other frame
-      (should-not (get-buffer-window "server" t)))))
-
-(ert-deftest erc-reuse-frames--displayed-double ()
-  :tags '(:unstable :expensive-test)
-  (erc-tests--erc-reuse-frames
-   (lambda (orig-frame)
-     (let ((erc-buffer-display #'erc-display-buffer-in-existing-frame))
-       (erc-tests--erc-reuse-frames--displayed-double orig-frame))
-     (dolist (b '("server" "#chan"))
-       (kill-buffer b)))))
-
-;; If a frame showing ERC buffers exists among other frames, new,
-;; additional connections will use the existing IRC frame.  However,
-;; if two or more frames exist with ERC buffers unique to a particular
-;; connection, the correct frame will be found.
-
-(defun erc-tests--erc-reuse-frames--displayed-full (orig-frame)
-  (erc-tests--erc-reuse-frames--displayed-double orig-frame)
-  ;; Server buffer is not displayed because #chan has replaced it in
-  ;; the "server" frame, which is not selected.
-  (should (equal "other" (frame-parameter (window-frame) 'name)))
-  (erc-tests--erc-reuse-frames--displayed-single orig-frame "ircd" "#spam")
-  (should (equal "other" (frame-parameter (window-frame) 'name)))
-
-  ;; Buffer "#spam" has replaced "ircd", which earlier replaced
-  ;; "#chan" in frame "server".  But this is confusing, so...
-  (ert-info ("Arrange windows for second connection in other frame")
-    (set-window-buffer (selected-window) "ircd")
-    (split-window-below)
-    (set-window-buffer (next-window) "#spam")
-    (should (equal (cddar (window-tree))
-                   (list (get-buffer-window "ircd" t)
-                         (get-buffer-window "#spam" t)))))
-
-  (ert-info ("Arrange windows for first connection in server frame")
-    (select-frame-by-name "server")
-    (set-window-buffer (selected-window) "server")
-    (set-window-buffer (next-window) "#chan")
-    (should (equal (cddar (window-tree))
-                   (list (get-buffer-window "server" t)
-                         (get-buffer-window "#chan" t)))))
-
-  ;; Select original ERT frame
-  (ert-info ("New target for connection server finds appropriate frame")
-    (select-frame orig-frame 'no-record)
-    (with-current-buffer (window-buffer (selected-window))
-      (should (member (buffer-name) '("*ert*" "*scratch*")))
-      (with-current-buffer (generate-new-buffer "alice")
-        (erc-mode)
-        (erc-tests--servars "server" 'erc-server-process 'erc-networks--id)
-        (setq erc-default-recipients '("alice"))
-        (should-not (get-buffer-window (current-buffer) t))
-        (erc-setup-buffer (current-buffer))
-        ;; Window created in frame "server"
-        (should (eq (selected-frame) orig-frame))
-        (erc-tests--assert-server-split (current-buffer) "server"))))
-
-  (ert-info ("New target for connection ircd finds appropriate frame")
-    (select-frame orig-frame 'no-record)
-    (with-current-buffer (window-buffer (selected-window))
-      (should (member (buffer-name) '("*ert*" "*scratch*")))
-      (with-current-buffer (generate-new-buffer "bob")
-        (erc-mode)
-        (erc-tests--servars "ircd" 'erc-server-process 'erc-networks--id)
-        (setq erc-default-recipients '("bob"))
-        (should-not (get-buffer-window (current-buffer) t))
-        (erc-setup-buffer (current-buffer))
-        ;; Window created in frame "other"
-        (should (eq (selected-frame) orig-frame))
-        (erc-tests--assert-server-split (current-buffer) "other")))))
-
-(ert-deftest erc-reuse-frames--displayed-full ()
-  :tags '(:unstable :expensive-test)
-  (erc-tests--erc-reuse-frames
-   (lambda (orig-frame)
-     (let ((erc-buffer-display #'erc-display-buffer-in-existing-frame))
-       (erc-tests--erc-reuse-frames--displayed-full orig-frame))
-     (dolist (b '("server" "ircd" "bob" "alice" "#spam" "#chan"))
-       (kill-buffer b)))))
-
 (ert-deftest erc-setup-buffer--custom-action ()
   (erc-mode)
   (erc-tests--set-fake-server-process "sleep" "1")
@@ -832,7 +529,7 @@ erc-setup-buffer--custom-action
       (let ((erc--display-context '((erc-buffer-display . 1)))
             (erc-join-buffer 'erc--test-fun))
         (erc-setup-buffer mbuf)
-        (should (equal `(erc--test-fun ,mbuf ((erc-buffer-display . 1)))
+        (should (equal `(erc--test-fun ,mbuf (nil (erc-buffer-display . 1)))
                        (pop calls)))
         (should-not calls))
 
@@ -841,8 +538,8 @@ erc-setup-buffer--custom-action
              (erc--display-context '((erc-buffer-display . 1)))
              (erc-reconnect-display 'erc--test-fun))
         (erc-setup-buffer mbuf)
-        (should (equal `(erc--test-fun ,mbuf ((erc-reconnect-display . t)
-                                              (erc-buffer-display . 1)))
+        (should (equal `(erc--test-fun ,mbuf (nil (erc-reconnect-display . t)
+                                                  (erc-buffer-display . 1)))
                        (pop calls)))
         (should-not calls)))
 
-- 
2.40.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-Revert-Allow-erc-reuse-frames-to-favor-connections.patch --]
[-- Type: text/x-patch, Size: 20749 bytes --]

From 17c1c7b9c3e2c1708e300638ec351bbdfc1776f5 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 19 Jun 2023 23:14:40 -0700
Subject: [PATCH 1/2] Revert "Allow erc-reuse-frames to favor connections"

This (mostly) reverts commit 0e4c07dc7448aafd2aa5f6e101d7b7aac23d8a6b.

* etc/ERC-NEWS: Also revert hunk from 52c8d537 "* etc/ERC-NEWS: Add
section for ERC 5.6." because it announced this feature, which no
longer exists.
* lisp/erc/erc.el (erc-reuse-frames): Revise doc string instead of
reverting completely.  (Bug#62833)
---
 etc/ERC-NEWS               |   8 -
 lisp/erc/erc.el            |  64 ++------
 test/lisp/erc/erc-tests.el | 303 -------------------------------------
 3 files changed, 10 insertions(+), 365 deletions(-)

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 68f1083621c..68cf0e2d6ca 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -82,14 +82,6 @@ connectivity before attempting to reconnect in earnest.  See options
 'erc-server-reconnect-function' and 'erc-nickname-in-use-functions' to
 get started.
 
-** Easily constrain all ERC-related business to a dedicated frame.
-The option 'erc-reuse-frames' can now be set to 'displayed', which
-tells ERC to show new buffers in frames already occupied by buffers
-from the same connection.  This customization depends on the option
-'erc-buffer-display' (formerly 'erc-join-buffer') being set to
-'frame'.  If you find the name 'displayed' unhelpful, please suggest
-an alternative by writing to the mailing list.
-
 ** Module 'fill' can add a bit of space between messages.
 On graphical displays, it's now possible to add some breathing room
 around certain messages via the new option 'erc-fill-line-spacing'.
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index a1538962602..70adbb15b5f 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1626,23 +1626,14 @@ erc-frame-dedicated-flag
 
 (defcustom erc-reuse-frames t
   "Determines whether new frames are always created.
-
-A value of t means only create a frame for undisplayed buffers.
-`displayed' means use any existing, potentially hidden frame
-already displaying a buffer from the same network context or,
-failing that, a frame showing any ERC buffer.  As a last resort,
-`displayed' defaults to the selected frame, except for brand new
-connections, for which the invoking frame is always used.  When
-this option is nil, a new frame is always created.
-
-Regardless of its value, this option is ignored unless
-`erc-join-buffer' is set to `frame'.  And like most options in
-the `erc-buffer' customize group, this has no effect on server
-buffers while reconnecting because those are always buried."
-  :package-version '(ERC . "5.6") ; FIXME sync on release
+Non-nil means only create a frame for undisplayed buffers.  Nil
+means always create a new frame.  Regardless of its value, ERC
+ignores this option unless `erc-join-buffer' is `frame'.  And
+like most options in the `erc-buffer' customize group, this has
+no effect on server buffers while reconnecting because ERC always
+buries those."
   :group 'erc-buffers
-  :type '(choice boolean
-                 (const displayed)))
+  :type 'boolean)
 
 (defun erc-channel-p (channel)
   "Return non-nil if CHANNEL seems to be an IRC channel name."
@@ -2095,35 +2086,6 @@ erc--updating-modules-p
 confidently call (erc-foo-mode 1) without having to learn
 anything about the dependency's implementation.")
 
-(defun erc--setup-buffer-first-window (frame a b)
-  (catch 'found
-    (walk-window-tree
-     (lambda (w)
-       (when (cond ((functionp a) (with-current-buffer (window-buffer w)
-                                    (funcall a b)))
-                   (t (eq (buffer-local-value a (window-buffer w)) b)))
-         (throw 'found t)))
-     frame nil 0)))
-
-(defun erc--display-buffer-use-some-frame (buffer alist)
-  "Maybe display BUFFER in an existing frame for the same connection.
-If performed, return window used; otherwise, return nil.  Forward ALIST
-to display-buffer machinery."
-  (when-let*
-      ((idp (lambda (value)
-              (and erc-networks--id
-                   (erc-networks--id-equal-p erc-networks--id value))))
-       (procp (lambda (frame)
-                (erc--setup-buffer-first-window frame idp erc-networks--id)))
-       (ercp (lambda (frame)
-               (erc--setup-buffer-first-window frame 'major-mode 'erc-mode)))
-       ((or (cdr (frame-list)) (funcall ercp (selected-frame)))))
-    ;; Workaround to avoid calling `window--display-buffer' directly
-    (or (display-buffer-use-some-frame buffer
-                                       `((frame-predicate . ,procp) ,@alist))
-        (display-buffer-use-some-frame buffer
-                                       `((frame-predicate . ,ercp) ,@alist)))))
-
 (defvar erc--setup-buffer-hook nil
   "Internal hook for module setup involving windows and frames.")
 
@@ -2142,21 +2104,15 @@ erc-setup-buffer
     ('bury
      nil)
     ('frame
-     (cond
-      ((and (eq erc-reuse-frames 'displayed)
-            (not (get-buffer-window buffer t)))
-       (display-buffer buffer '((erc--display-buffer-use-some-frame)
-                                (inhibit-switch-frame . t)
-                                (inhibit-same-window . t))))
-      ((or (not erc-reuse-frames)
-           (not (get-buffer-window buffer t)))
+     (when (or (not erc-reuse-frames)
+               (not (get-buffer-window buffer t)))
        (let ((frame (make-frame (or erc-frame-alist
                                     default-frame-alist))))
          (raise-frame frame)
          (select-frame frame))
        (switch-to-buffer buffer)
        (when erc-frame-dedicated-flag
-         (set-window-dedicated-p (selected-window) t)))))
+         (set-window-dedicated-p (selected-window) t))))
     (_
      (if (active-minibuffer-window)
          (display-buffer buffer)
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index f3489a16386..b751ef50520 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -503,309 +503,6 @@ erc--switch-to-buffer
     (dolist (b '("server" "other" "#chan" "#foo" "#fake"))
       (kill-buffer b))))
 
-(defun erc-tests--run-in-term (&optional debug)
-  (let* ((default-directory (getenv "EMACS_TEST_DIRECTORY"))
-         (emacs (expand-file-name invocation-name invocation-directory))
-         (process-environment (cons "ERC_TESTS_SUBPROCESS=1"
-                                    process-environment))
-         (name (ert-test-name (ert-running-test)))
-         (temp-file (make-temp-file "erc-term-test-"))
-         (cmd `(let ((stats 1))
-                 (setq enable-dir-local-variables nil)
-                 (unwind-protect
-                     (setq stats (ert-run-tests-batch ',name))
-                   (unless ',debug
-                     (let ((buf (with-current-buffer (messages-buffer)
-                                  (buffer-string))))
-                       (with-temp-file ,temp-file
-                         (insert buf)))
-                     (kill-emacs (ert-stats-completed-unexpected stats))))))
-         ;; `ert-test' object in Emacs 29 has a `file-name' field
-         (file-name (symbol-file name 'ert--test))
-         (default-directory (expand-file-name (file-name-directory file-name)))
-         (package (if-let* ((found (getenv "ERC_PACKAGE_NAME"))
-                            ((string-prefix-p "erc-" found)))
-                      (intern found)
-                    'erc))
-         (setup (and (featurep 'compat)
-                     `(progn
-                        (require 'package)
-                        (let ((package-load-list '((compat t) (,package t))))
-                          (package-initialize)))))
-         ;; Make subprocess terminal bigger than controlling.
-         (buf (cl-letf (((symbol-function 'window-screen-lines)
-                         (lambda () 20))
-                        ((symbol-function 'window-max-chars-per-line)
-                         (lambda () 40)))
-                (make-term (symbol-name name) emacs nil "-Q" "-nw"
-                           "-eval" (prin1-to-string setup)
-                           "-l" file-name "-eval" (format "%S" cmd))))
-         (proc (get-buffer-process buf))
-         (err (lambda ()
-                (with-temp-buffer
-                  (insert-file-contents temp-file)
-                  (message "Subprocess: %s" (buffer-string))
-                  (delete-file temp-file)))))
-    (with-current-buffer buf
-      (set-process-query-on-exit-flag proc nil)
-      (with-timeout (10 (funcall err) (error "Timed out awaiting result"))
-        (while (process-live-p proc)
-          (accept-process-output proc 0.1)))
-      (while (accept-process-output proc))
-      (goto-char (point-min))
-      ;; Otherwise gives process exited abnormally with exit-code >0
-      (unless (search-forward (format "Process %s finished" name) nil t)
-        (funcall err)
-        (ert-fail (when (search-forward "exited" nil t)
-                    (buffer-substring-no-properties (line-beginning-position)
-                                                    (line-end-position)))))
-      (delete-file temp-file)
-      (when noninteractive
-        (kill-buffer)))))
-
-(defun erc-tests--servars (source &rest vars)
-  (unless (bufferp source)
-    (setq source (get-buffer source)))
-  (dolist (var vars)
-    (should (local-variable-if-set-p var))
-    (set var (buffer-local-value var source))))
-
-(defun erc-tests--erc-reuse-frames (test &optional debug)
-  (if (and (or debug noninteractive) (not (getenv "ERC_TESTS_SUBPROCESS")))
-      (progn
-        (when (memq system-type '(windows-nt ms-dos))
-          (ert-skip "System must be UNIX"))
-        (erc-tests--run-in-term debug))
-    (should-not erc-frame-dedicated-flag)
-    (should (eq erc-reuse-frames t))
-    (let ((erc-join-buffer 'frame)
-          (erc-reuse-frames t)
-          (erc-frame-alist nil)
-          (orig-frame (selected-frame))
-          erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook)
-      (delete-other-frames)
-      (delete-other-windows)
-      (set-window-buffer (selected-window) "*scratch*")
-      (funcall test orig-frame)
-      (delete-other-frames orig-frame)
-      (delete-other-windows))))
-
-;; TODO add cases for frame-display behavior while reconnecting
-
-(defun erc-tests--erc-reuse-frames--t (_)
-  (ert-info ("New server buffer creates and raises second frame")
-    (with-current-buffer (generate-new-buffer "server")
-      (erc-mode)
-      (setq erc-server-process (start-process "server"
-                                              (current-buffer) "sleep" "10")
-            erc-frame-alist (cons '(name . "server") default-frame-alist)
-            erc-network 'foonet
-            erc-networks--id (erc-networks--id-create nil)
-            erc--server-last-reconnect-count 0)
-      (set-process-buffer erc-server-process (current-buffer))
-      (set-process-query-on-exit-flag erc-server-process nil)
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should (equal "server" (frame-parameter (window-frame) 'name)))
-      (should (get-buffer-window (current-buffer) t))))
-
-  (ert-info ("New channel creates and raises third frame")
-    (with-current-buffer (generate-new-buffer "#chan")
-      (erc-mode)
-      (erc-tests--servars "server" 'erc-server-process 'erc-networks--id
-                          'erc-network)
-      (setq erc-frame-alist (cons '(name . "#chan") default-frame-alist)
-            erc-default-recipients '("#chan"))
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should (equal "#chan" (frame-parameter (window-frame) 'name)))
-      (should (get-buffer-window (current-buffer) t))
-      (should (cddr (frame-list))))))
-
-(ert-deftest erc-reuse-frames--t ()
-  :tags '(:unstable :expensive-test)
-  (erc-tests--erc-reuse-frames
-   (lambda (orig-frame)
-     (erc-tests--erc-reuse-frames--t orig-frame)
-     (dolist (b '("server" "#chan"))
-       (kill-buffer b)))))
-
-(defun erc-tests--erc-reuse-frames--displayed-single (_ server-name chan-name)
-
-  (should (eq erc-reuse-frames 'displayed))
-
-  (ert-info ("New server buffer shown in existing frame")
-    (with-current-buffer (generate-new-buffer server-name)
-      (erc-mode)
-      (setq erc-server-process (start-process server-name (current-buffer)
-                                              "sleep" "10")
-            erc-frame-alist (cons `(name . ,server-name) default-frame-alist)
-            erc-network (make-symbol server-name)
-            erc-server-current-nick "tester"
-            erc-networks--id (erc-networks--id-create nil)
-            erc--server-last-reconnect-count 0)
-      (set-process-buffer erc-server-process (current-buffer))
-      (set-process-query-on-exit-flag erc-server-process nil)
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should-not (equal server-name (frame-parameter (window-frame) 'name)))
-      ;; New server buffer window appears in split below ERT/scratch
-      (should (get-buffer-window (current-buffer) t))))
-
-  (ert-info ("New channel shown in existing frame")
-    (with-current-buffer (generate-new-buffer chan-name)
-      (erc-mode)
-      (erc-tests--servars server-name 'erc-server-process 'erc-networks--id
-                          'erc-network)
-      (setq erc-frame-alist (cons `(name . ,chan-name) default-frame-alist)
-            erc-default-recipients (list chan-name))
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should-not (equal chan-name (frame-parameter (window-frame) 'name)))
-      ;; New channel buffer replaces server in lower window
-      (should (get-buffer-window (current-buffer) t))
-      (should-not (get-buffer-window server-name t)))))
-
-(ert-deftest erc-reuse-frames--displayed-single ()
-  :tags '(:unstable :expensive-test)
-  (erc-tests--erc-reuse-frames
-   (lambda (orig-frame)
-     (let ((erc-reuse-frames 'displayed))
-       (erc-tests--erc-reuse-frames--displayed-single orig-frame
-                                                      "server" "#chan")
-       (should-not (cdr (frame-list))))
-     (dolist (b '("server" "#chan"))
-       (kill-buffer b)))))
-
-(defun erc-tests--assert-server-split (buffer-or-name frame-name)
-  ;; Assert current buffer resides on one side of a horizontal split
-  ;; in the "server" frame but is not selected.
-  (let* ((buffer-window (get-buffer-window buffer-or-name t))
-         (buffer-frame (window-frame buffer-window)))
-    (should (equal frame-name (frame-parameter buffer-frame 'name)))
-    (should (memq buffer-window (car-safe (window-tree buffer-frame))))
-    (should-not (eq buffer-window (frame-selected-window)))
-    buffer-frame))
-
-(defun erc-tests--erc-reuse-frames--displayed-double (_)
-  (should (eq erc-reuse-frames 'displayed))
-
-  (make-frame '((name . "other")))
-  (select-frame (make-frame '((name . "server"))) 'no-record)
-  (set-window-buffer (selected-window) "*scratch*") ; invokes `erc'
-
-  ;; A user invokes an entry point and switches immediately to a new
-  ;; frame before autojoin kicks in (bug#55540).
-
-  (ert-info ("New server buffer shown in selected frame")
-    (with-current-buffer (generate-new-buffer "server")
-      (erc-mode)
-      (setq erc-server-process (start-process "server" (current-buffer)
-                                              "sleep" "10")
-            erc-network 'foonet
-            erc-server-current-nick "tester"
-            erc-networks--id (erc-networks--id-create nil)
-            erc--server-last-reconnect-count 0)
-      (set-process-buffer erc-server-process (current-buffer))
-      (set-process-query-on-exit-flag erc-server-process nil)
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (should (equal "server" (frame-parameter (window-frame) 'name)))
-      (should (get-buffer-window (current-buffer) t))))
-
-  (select-frame-by-name "other")
-
-  (ert-info ("New channel shown in dedicated frame")
-    (with-current-buffer (generate-new-buffer "#chan")
-      (erc-mode)
-      (erc-tests--servars "server" 'erc-server-process 'erc-networks--id
-                          'erc-network)
-      (setq erc-frame-alist (cons '(name . "#chan") default-frame-alist)
-            erc-default-recipients '("#chan"))
-      (should-not (get-buffer-window (current-buffer) t))
-      (erc-setup-buffer (current-buffer))
-      (erc-tests--assert-server-split (current-buffer) "server")
-      ;; New channel buffer replaces server in lower window of other frame
-      (should-not (get-buffer-window "server" t)))))
-
-(ert-deftest erc-reuse-frames--displayed-double ()
-  :tags '(:unstable :expensive-test)
-  (erc-tests--erc-reuse-frames
-   (lambda (orig-frame)
-     (let ((erc-reuse-frames 'displayed))
-       (erc-tests--erc-reuse-frames--displayed-double orig-frame))
-     (dolist (b '("server" "#chan"))
-       (kill-buffer b)))))
-
-;; If a frame showing ERC buffers exists among other frames, new,
-;; additional connections will use the existing IRC frame.  However,
-;; if two or more frames exist with ERC buffers unique to a particular
-;; connection, the correct frame will be found.
-
-(defun erc-tests--erc-reuse-frames--displayed-full (orig-frame)
-  (erc-tests--erc-reuse-frames--displayed-double orig-frame)
-  ;; Server buffer is not displayed because #chan has replaced it in
-  ;; the "server" frame, which is not selected.
-  (should (equal "other" (frame-parameter (window-frame) 'name)))
-  (erc-tests--erc-reuse-frames--displayed-single orig-frame "ircd" "#spam")
-  (should (equal "other" (frame-parameter (window-frame) 'name)))
-
-  ;; Buffer "#spam" has replaced "ircd", which earlier replaced
-  ;; "#chan" in frame "server".  But this is confusing, so...
-  (ert-info ("Arrange windows for second connection in other frame")
-    (set-window-buffer (selected-window) "ircd")
-    (split-window-below)
-    (set-window-buffer (next-window) "#spam")
-    (should (equal (cddar (window-tree))
-                   (list (get-buffer-window "ircd" t)
-                         (get-buffer-window "#spam" t)))))
-
-  (ert-info ("Arrange windows for first connection in server frame")
-    (select-frame-by-name "server")
-    (set-window-buffer (selected-window) "server")
-    (set-window-buffer (next-window) "#chan")
-    (should (equal (cddar (window-tree))
-                   (list (get-buffer-window "server" t)
-                         (get-buffer-window "#chan" t)))))
-
-  ;; Select original ERT frame
-  (ert-info ("New target for connection server finds appropriate frame")
-    (select-frame orig-frame 'no-record)
-    (with-current-buffer (window-buffer (selected-window))
-      (should (member (buffer-name) '("*ert*" "*scratch*")))
-      (with-current-buffer (generate-new-buffer "alice")
-        (erc-mode)
-        (erc-tests--servars "server" 'erc-server-process 'erc-networks--id)
-        (setq erc-default-recipients '("alice"))
-        (should-not (get-buffer-window (current-buffer) t))
-        (erc-setup-buffer (current-buffer))
-        ;; Window created in frame "server"
-        (should (eq (selected-frame) orig-frame))
-        (erc-tests--assert-server-split (current-buffer) "server"))))
-
-  (ert-info ("New target for connection ircd finds appropriate frame")
-    (select-frame orig-frame 'no-record)
-    (with-current-buffer (window-buffer (selected-window))
-      (should (member (buffer-name) '("*ert*" "*scratch*")))
-      (with-current-buffer (generate-new-buffer "bob")
-        (erc-mode)
-        (erc-tests--servars "ircd" 'erc-server-process 'erc-networks--id)
-        (setq erc-default-recipients '("bob"))
-        (should-not (get-buffer-window (current-buffer) t))
-        (erc-setup-buffer (current-buffer))
-        ;; Window created in frame "other"
-        (should (eq (selected-frame) orig-frame))
-        (erc-tests--assert-server-split (current-buffer) "other")))))
-
-(ert-deftest erc-reuse-frames--displayed-full ()
-  :tags '(:unstable :expensive-test)
-  (erc-tests--erc-reuse-frames
-   (lambda (orig-frame)
-     (let ((erc-reuse-frames 'displayed))
-       (erc-tests--erc-reuse-frames--displayed-full orig-frame))
-     (dolist (b '("server" "ircd" "bob" "alice" "#spam" "#chan"))
-       (kill-buffer b)))))
-
 (ert-deftest erc-lurker-maybe-trim ()
   (let (erc-lurker-trim-nicks
         (erc-lurker-ignore-chars "_`"))
-- 
2.40.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0002-5.6-Allow-custom-display-buffer-actions-in-ERC.patch --]
[-- Type: text/x-patch, Size: 26783 bytes --]

From 78bba39e65b168a117c077518f2aee2a8465e470 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Tue, 30 May 2023 23:27:12 -0700
Subject: [PATCH 2/2] [5.6] Allow custom display-buffer actions in ERC

* etc/ERC-NEWS: Mention new `display-buffer' "action" variant for all
buffer-display options.
* lisp/erc/erc-backend.el (erc-server-JOIN, erc-server-PRIVMSG,
erc-server-NOTICE): Set `erc--display-context' to a symbol for the IRC
command, like `JOIN' in order to influence `erc-setup-buffer' by way
of `erc--open-target'.
* lisp/erc/erc.el (erc--buffer-display-choices): New helper for
defining common `:type' for all buffer-display options.
(erc-buffer-display): Add new choice of either `display-buffer' or
`pop-to-buffer' paired with an "action alist".
(erc-buffer-display, erc-interactive-display, erc-reconnect-display,
erc-receive-query-display): Use helper `erc--buffer-display-choices'
for defining `:type'.
(erc-skip-displaying-selected-window-buffer): New variable, deprecated
at birth, to act as an escape hatch for folks who don't want to skip
the displaying of buffers already showing in the selected window.
(erc--display-buffer-action): Local variable allowing modules to
influence the displaying of new ERC buffers independently of user
options.
(erc-open): Bind `display-buffer-overriding-action' to the value of
`erc--display-buffer-action' around calls to `erc-setup-buffer'.
(erc-setup-buffer): Do nothing when the selected window already shows
current buffer unless user has provided a custom action.  Accommodate
new choice values `display-buffer' and `pop-to-buffer'.
(erc-select-read-args): Add `erc--display-context' to environment.
(erc, erc-tls): Bind `erc--display-context' around calls to
`erc-select-read-args' and main body.
(erc-cmd-JOIN, erc-cmd-QUERY, erc-handle-irc-url): Add item for
`erc-interactive-display' to `erc--display-context'.
* test/lisp/erc/erc-tests.el (erc-setup-buffer--custom-action): New
test.
(erc-select-read-args, erc-tls, erc--interactive): Expect new
environment binding for `erc--display-context'.  (Bug#62833)
---
 etc/ERC-NEWS               |  12 ++-
 lisp/erc/erc-backend.el    |   9 ++-
 lisp/erc/erc.el            | 153 ++++++++++++++++++++++++++++---------
 test/lisp/erc/erc-tests.el |  81 +++++++++++++++++---
 4 files changed, 206 insertions(+), 49 deletions(-)

diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 68cf0e2d6ca..9177f61f8cc 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -37,7 +37,7 @@ decade overdue, this is no longer the case.  Other UX improvements in
 this area aim to make the process of connecting interactively slightly
 more streamlined and less repetitive, even for veteran users.
 
-** Revised buffer-display handling for interactive commands.
+** Revised buffer-display handling.
 A point of friction for new users and one only just introduced with
 ERC 5.5 has been the lack of visual feedback when first connecting via
 M-x erc or when issuing a "/JOIN" command at the prompt.  As explained
@@ -58,6 +58,16 @@ option (now known as 'erc-receive-query-display') is nil, ERC uses
 'erc-interactive-display'.  The old nil behavior can still be gotten
 via the new compatibility flag 'erc-receive-query-display-defer'.
 
+This release also introduces a few subtleties affecting the display of
+new or reassociated buffers.  One involves buffers that already occupy
+the selected window.  ERC now treats these as deserving of an implicit
+'bury'.  An escape hatch for this and most other baked-in behaviors is
+now available in the form of a new type variant recognized by all such
+options.  That is, users can now specify their own 'display-buffer'
+function to exercise full control over nearly all buffer-display
+related decisions.  See the newly expanded doc strings of
+'erc-buffer-display' and friends for details.
+
 ** Setting a module's mode variable via Customize earns a warning.
 Trying and failing to activate a module via its minor mode's Custom
 widget has been an age-old annoyance for new users.  Previously
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index b5bd96c189d..3bcda27444d 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -101,6 +101,7 @@
 (eval-when-compile (require 'cl-lib))
 (require 'erc-common)
 
+(defvar erc--display-context)
 (defvar erc--target)
 (defvar erc--user-from-nick-function)
 (defvar erc-channel-list)
@@ -1686,7 +1687,9 @@ define-erc-response-handler
   "Handle join messages."
   nil
   (let ((chnl (erc-response.contents parsed))
-        (buffer nil))
+        (buffer nil)
+        (erc--display-context `((erc-buffer-display . JOIN)
+                                ,@erc--display-context)))
     (pcase-let ((`(,nick ,login ,host)
                  (erc-parse-user (erc-response.sender parsed))))
       ;; strip the stupid combined JOIN facility (IRC 2.9)
@@ -1885,6 +1888,8 @@ define-erc-response-handler
              (noticep (string= cmd "NOTICE"))
              ;; S.B. downcase *both* tgt and current nick
              (privp (erc-current-nick-p tgt))
+             (erc--display-context `((erc-buffer-display . ,(intern cmd))
+                                     ,@erc--display-context))
              s buffer
              fnick)
         (setf (erc-response.contents parsed) msg)
@@ -1899,6 +1904,8 @@ define-erc-response-handler
                               (and erc-ensure-target-buffer-on-privmsg
                                    (or erc-receive-query-display
                                        erc-join-buffer)))))
+                (push `(erc-receive-query-display . ,(intern cmd))
+                      erc--display-context)
                 (setq buffer (erc--open-target nick)))
             ;; A channel buffer has been killed but is still joined.
             (when erc-ensure-target-buffer-on-privmsg
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 70adbb15b5f..f1dafd0dbf8 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1548,9 +1548,26 @@ erc-default-port-tls
   "IRC port to use for encrypted connections if it cannot be \
 detected otherwise.")
 
+(defconst erc--buffer-display-choices
+  `(choice (const :tag "Use value of `erc-buffer-display'" nil)
+           (const :tag "Split window and select" window)
+           (const :tag "Split window but don't select" window-noselect)
+           (const :tag "New frame" frame)
+           (const :tag "Don't display" bury)
+           (const :tag "Use current window" buffer)
+           (choice :tag "Defer to a display function"
+                   (function-item display-buffer)
+                   (function-item pop-to-buffer)
+                   (function :tag "User-defined")))
+  "Common choices for buffer-display options.")
+
 (defvaralias 'erc-join-buffer 'erc-buffer-display)
 (defcustom erc-buffer-display 'bury
   "How to display a newly created ERC buffer.
+This determines the baseline, \"catch-all\" display behavior.  It
+takes a backseat to more context-specific display options, like
+`erc-interactive-display', `erc-reconnect-display', and
+`erc-receive-query-display'.
 
 The available choices are:
 
@@ -1559,17 +1576,32 @@ erc-buffer-display
   `frame'           - in another frame,
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
-
-See related options `erc-interactive-display',
-`erc-reconnect-display', and `erc-receive-query-display'."
+  DISPLAY-FUNCTION  - a `display-buffer'-like function
+
+Here, DISPLAY-FUNCTION should accept a buffer and an ACTION of
+the kind described by the Info node `(elisp) Choosing Window'.
+At times, ERC may add hints about the calling context to the
+ACTION's alist.  Keys are symbols of user options, like
+`erc-buffer-display', and values are predefined constants
+specific to each.  For this particular option, possible values
+include the symbols
+
+  `JOIN', `PRIVMSG', `NOTICE', `erc', and `erc-tls'.
+
+The first three signify IRC commands received from the server and
+the rest entry-point commands responsible for the connection.
+When dealing with the latter two, users may prefer to call
+DISPLAY-FUNCTION directly on (server) buffers returned by these
+entry points because the context leading to their creation is
+plainly obvious.
+
+Note that when the selected window already shows the current
+buffer, ERC pretends this option's value is `bury' unless the
+variable `erc-skip-displaying-selected-window-buffer' is nil or
+the value of this option is DISPLAY-FUNCTION."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
-  :type '(choice (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+  :type (cons 'choice (nthcdr 2 erc--buffer-display-choices)))
 
 (defvaralias 'erc-query-display 'erc-interactive-display)
 (defcustom erc-interactive-display 'window
@@ -1578,30 +1610,36 @@ erc-interactive-display
 interactively at the prompt.  It does not apply when calling a
 handler for such a command, like `erc-cmd-JOIN', from lisp code.
 See `erc-buffer-display' for a full description of available
-values."
+values.
+
+When the value is a user-provided function, ERC may inject a hint
+about the invocation context as an extra item in the \"action
+alist\" included as part of the second argument.  The item's key
+is the symbol `erc-interactive-display' and its value one of
+
+  `/QUERY', `/JOIN', `url', `erc', or `erc-tls'.
+
+All are symbols indicating an inciting user action, such as the
+issuance of a slash command, the clicking of a URL hyperlink, or
+the invocation of an entry-point command."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury new and don't display existing" bury)
-                 (const :tag "Use current buffer" buffer)))
+  :type erc--buffer-display-choices)
 
 (defcustom erc-reconnect-display nil
   "How and whether to display a channel buffer when auto-reconnecting.
 This only affects automatic reconnections and is ignored, like
 all other buffer-display options, when issuing a /RECONNECT or
 successfully reinvoking `erc-tls' with similar arguments.  See
-`erc-buffer-display' for a description of possible values."
+`erc-buffer-display' for a description of possible values.
+
+When the value is function, ERC may inject a hint about the
+calling context as an extra item in the alist making up the tail
+of the second, \"action\" argument.  The item's key is the symbol
+`erc-reconnect-display' and its value something non-nil."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)))
+  :type erc--buffer-display-choices)
 
 (defcustom erc-reconnect-display-timeout 10
   "Duration `erc-reconnect-display' remains active.
@@ -2089,12 +2127,37 @@ erc--updating-modules-p
 (defvar erc--setup-buffer-hook nil
   "Internal hook for module setup involving windows and frames.")
 
+(defvar erc--display-context nil
+  "Extra action alist items passed to `display-buffer'.
+Non-nil when a user specifies a custom display action for certain
+display-options, like `erc-reconnect-display'.  ERC pairs the
+option's symbol with a context-dependent value and adds the entry
+to the user-provided alist when calling `pop-to-buffer' or
+`display-buffer'.")
+
+(defvar erc-skip-displaying-selected-window-buffer t
+  "Whether to forgo showing a buffer that's already being displayed.
+But only in the selected window.")
+(make-obsolete 'erc-show-already-displayed-buffer
+               "non-nil behavior to be made permanent" "30.1")
+
+(defvar-local erc--display-buffer-action nil
+  "The value of `display-buffer-overriding-action' when non-nil.
+Influences the displaying of new or reassociated ERC buffers.
+Reserved for use by built-in modules.")
+
 (defun erc-setup-buffer (buffer)
   "Consults `erc-join-buffer' to find out how to display `BUFFER'."
   (pcase (if (zerop (erc-with-server-buffer
                       erc--server-last-reconnect-count))
              erc-join-buffer
            (or erc-reconnect-display erc-join-buffer))
+    ((and (pred functionp) disp-fn (let context erc--display-context))
+     (unless (zerop erc--server-last-reconnect-count)
+       (push '(erc-reconnect-display . t) context))
+     (funcall disp-fn buffer (cons nil context)))
+    ((guard (and erc-skip-displaying-selected-window-buffer
+                 (eq (window-buffer) buffer))))
     ('window
      (if (active-minibuffer-window)
          (display-buffer buffer)
@@ -2292,8 +2355,10 @@ erc-open
         ;; we can't log to debug buffer, it may not exist yet
         (message "erc: old buffer %s, switching to %s"
                  old-buffer buffer))
-      (erc-setup-buffer buffer)
-      (run-hooks 'erc--setup-buffer-hook))
+      (let ((display-buffer-overriding-action
+             (or erc--display-buffer-action display-buffer-overriding-action)))
+        (erc-setup-buffer buffer)
+        (run-hooks 'erc--setup-buffer-hook)))
 
     buffer))
 
@@ -2401,6 +2466,8 @@ erc-select-read-args
          env)
     (when erc-interactive-display
       (push `(erc-join-buffer . ,erc-interactive-display) env))
+    (when erc--display-context
+      (push `(erc--display-context . ,erc--display-context) env))
     (when opener
       (push `(erc-server-connect-function . ,opener) env))
     (when (and passwd (string= "" passwd))
@@ -2454,7 +2521,12 @@ erc
 See `erc-tls' for the meaning of ID.
 
 \(fn &key SERVER PORT NICK USER PASSWORD FULL-NAME ID)"
-  (interactive (erc-select-read-args))
+  (interactive (let ((erc--display-context `((erc-interactive-display . erc)
+                                             ,@erc--display-context)))
+                 (erc-select-read-args)))
+  (unless (assq 'erc--display-context --interactive-env--)
+    (push '(erc--display-context . ((erc-buffer-display . erc)))
+          --interactive-env--))
   (erc--with-entrypoint-environment --interactive-env--
     (erc-open server port nick full-name t password nil nil nil nil user id)))
 
@@ -2519,8 +2591,11 @@ erc-tls
 interactively.
 
 \(fn &key SERVER PORT NICK USER PASSWORD FULL-NAME CLIENT-CERTIFICATE ID)"
-  (interactive (let ((erc-default-port erc-default-port-tls))
-		 (erc-select-read-args)))
+  (interactive
+   (let ((erc-default-port erc-default-port-tls)
+         (erc--display-context `((erc-interactive-display . erc-tls)
+                                 ,@erc--display-context)))
+     (erc-select-read-args)))
   ;; Bind `erc-server-connect-function' to `erc-open-tls-stream'
   ;; around `erc-open' when a non-default value hasn't been specified
   ;; by the user or the interactive form.  And don't bother checking
@@ -2529,6 +2604,9 @@ erc-tls
               (not (eq erc-server-connect-function #'erc-open-network-stream)))
     (push '(erc-server-connect-function . erc-open-tls-stream)
           --interactive-env--))
+  (unless (assq 'erc--display-context --interactive-env--)
+    (push '(erc--display-context . ((erc-buffer-display . erc-tls)))
+          --interactive-env--))
   (erc--with-entrypoint-environment --interactive-env--
     (erc-open server port nick full-name t password
               nil nil nil client-certificate user id)))
@@ -3683,7 +3761,10 @@ erc-cmd-JOIN
                         (sn (erc-extract-nick (erc-response.sender parsed)))
                         ((erc-nick-equal-p sn (erc-current-nick)))
                         (erc-join-buffer (or erc-interactive-display
-                                             erc-join-buffer)))
+                                             erc-join-buffer))
+                        (erc--display-context `((erc-interactive-display
+                                                 . /JOIN)
+                                                ,@erc--display-context)))
                      (run-hook-with-args-until-success
                       'erc-server-JOIN-functions proc parsed)
                      t))))
@@ -4067,7 +4148,9 @@ erc-cmd-QUERY
       ;; currently broken, evil hack to display help anyway
                                         ;(erc-delete-query))))
     (signal 'wrong-number-of-arguments '(erc-cmd-QUERY 0)))
-  (let ((erc-join-buffer erc-interactive-display))
+  (let ((erc-join-buffer erc-interactive-display)
+        (erc--display-context `((erc-interactive-display . /QUERY)
+                                ,@erc--display-context)))
     (erc-with-server-buffer
      (erc--open-target user))))
 
@@ -4851,13 +4934,7 @@ erc-receive-query-display
   :package-version '(ERC . "5.6")
   :group 'erc-buffers
   :group 'erc-query
-  :type '(choice (const :tag "Defer to value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+  :type erc--buffer-display-choices)
 
 (defvar erc-receive-query-display-defer t
   "How to interpret a null `erc-receive-query-display'.
@@ -7853,6 +7930,8 @@ erc-handle-irc-url
 Customize `erc-url-connect-function' to override this."
   (when (eql port 0) (setq port nil))
   (let* ((net (erc-networks--determine host))
+         (erc--display-context `((erc-interactive-display . url)
+                                 ,@erc--display-context))
          (server-buffer
           ;; Viable matches may slip through the cracks for unknown
           ;; networks.  Additional passes could likely improve things.
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index b751ef50520..6dbd0d4704f 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -503,6 +503,49 @@ erc--switch-to-buffer
     (dolist (b '("server" "other" "#chan" "#foo" "#fake"))
       (kill-buffer b))))
 
+(ert-deftest erc-setup-buffer--custom-action ()
+  (erc-mode)
+  (erc-tests--set-fake-server-process "sleep" "1")
+  (setq erc--server-last-reconnect-count 0)
+  (let ((owin (selected-window))
+        (obuf (window-buffer))
+        (mbuf (messages-buffer))
+        calls)
+    (cl-letf (((symbol-function 'switch-to-buffer) ; regression
+               (lambda (&rest r) (push (cons 'switch-to-buffer r) calls)))
+              ((symbol-function 'erc--test-fun)
+               (lambda (&rest r) (push (cons 'erc--test-fun r) calls)))
+              ((symbol-function 'display-buffer)
+               (lambda (&rest r) (push (cons 'display-buffer r) calls))))
+
+      ;; Baseline
+      (let ((erc-join-buffer 'bury))
+        (erc-setup-buffer mbuf)
+        (should-not calls))
+
+      (should-not erc--display-context)
+
+      ;; `display-buffer'
+      (let ((erc--display-context '((erc-buffer-display . 1)))
+            (erc-join-buffer 'erc--test-fun))
+        (erc-setup-buffer mbuf)
+        (should (equal `(erc--test-fun ,mbuf (nil (erc-buffer-display . 1)))
+                       (pop calls)))
+        (should-not calls))
+
+      ;; `pop-to-buffer' with `erc-reconnect-display'
+      (let* ((erc--server-last-reconnect-count 1)
+             (erc--display-context '((erc-buffer-display . 1)))
+             (erc-reconnect-display 'erc--test-fun))
+        (erc-setup-buffer mbuf)
+        (should (equal `(erc--test-fun ,mbuf (nil (erc-reconnect-display . t)
+                                                  (erc-buffer-display . 1)))
+                       (pop calls)))
+        (should-not calls)))
+
+    (should (eq owin (selected-window)))
+    (should (eq obuf (window-buffer)))))
+
 (ert-deftest erc-lurker-maybe-trim ()
   (let (erc-lurker-trim-nicks
         (erc-lurker-ignore-chars "_`"))
@@ -1439,14 +1482,18 @@ erc-select-read-args
                            (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when URL is ircs://")
-    (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
-                     (erc-select-read-args))
-                   (list :server "irc.gnu.org"
-                         :port 6697
-                         :nick (user-login-name)
-                         '&interactive-env
-                         '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . window))))))
+    (let ((erc--display-context '((erc-interactive-display . erc))))
+      (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
+                       (erc-select-read-args))
+                     (list :server "irc.gnu.org"
+                           :port 6697
+                           :nick (user-login-name)
+                           '&interactive-env
+                           '((erc-server-connect-function
+                              . erc-open-tls-stream)
+                             (erc--display-context
+                              . ((erc-interactive-display . erc)))
+                             (erc-join-buffer . window)))))))
 
   (setq-local erc-interactive-display nil) ; cheat to save space
 
@@ -1526,6 +1573,7 @@ erc-tls
               ((symbol-function 'erc-open)
                (lambda (&rest r)
                  (push `((erc-join-buffer ,erc-join-buffer)
+                         (erc--display-context ,@erc--display-context)
                          (erc-server-connect-function
                           ,erc-server-connect-function))
                        env)
@@ -1538,6 +1586,7 @@ erc-tls
                          nil nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Full")
@@ -1554,6 +1603,7 @@ erc-tls
                          "bob:changeme" nil nil nil t "bobo" GNU.org)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       ;; Values are often nil when called by lisp code, which leads to
@@ -1573,6 +1623,7 @@ erc-tls
                              "bob:changeme" nil nil nil nil "bobo" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Interactive")
@@ -1583,6 +1634,8 @@ erc-tls
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer window)
+                         (erc--display-context
+                          (erc-interactive-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Custom connect function")
@@ -1593,6 +1646,8 @@ erc-tls
                            nil nil nil nil nil "user" nil)))
           (should (equal (pop env)
                          '((erc-join-buffer bury)
+                           (erc--display-context
+                            (erc-buffer-display . erc-tls))
                            (erc-server-connect-function my-connect-func))))))
 
       (ert-info ("Advised default function overlooked") ; intentional
@@ -1604,6 +1659,7 @@ erc-tls
                          nil nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream))))
         (advice-remove 'erc-server-connect-function 'erc-tests--erc-tls))
 
@@ -1617,6 +1673,8 @@ erc-tls
                            '("irc.libera.chat" 6697 "tester" "unknown" t
                              nil nil nil nil nil "user" nil)))
             (should (equal (pop env) `((erc-join-buffer bury)
+                                       (erc--display-context
+                                        (erc-buffer-display . erc-tls))
                                        (erc-server-connect-function ,f))))
             (advice-remove 'erc-server-connect-function
                            'erc-tests--erc-tls)))))))
@@ -1631,6 +1689,7 @@ erc--interactive
               ((symbol-function 'erc-open)
                (lambda (&rest r)
                  (push `((erc-join-buffer ,erc-join-buffer)
+                         (erc--display-context ,@erc--display-context)
                          (erc-server-connect-function
                           ,erc-server-connect-function))
                        env)
@@ -1643,8 +1702,9 @@ erc--interactive
                        '("irc.libera.chat" 6697 "tester" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer window) (erc-server-connect-function
-                                                   erc-open-tls-stream)))))
+                       '((erc-join-buffer window)
+                         (erc--display-context (erc-interactive-display . erc))
+                         (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Nick supplied, decline TLS upgrade")
         (ert-simulate-keys "\r\rdummy\r\rn\r"
@@ -1654,6 +1714,7 @@ erc--interactive
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer window)
+                         (erc--display-context (erc-interactive-display . erc))
                          (erc-server-connect-function
                           erc-open-network-stream))))))))
 
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] <87leiuy3cv.fsf@neverwas.me>
                   ` (7 preceding siblings ...)
  2023-06-09 13:50 ` J.P.
@ 2023-07-08 14:19 ` J.P.
       [not found] ` <87r0pi32po.fsf@neverwas.me>
  9 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-07-08 14:19 UTC (permalink / raw)
  To: 62833; +Cc: emacs-erc

[-- Attachment #1: Type: text/plain, Size: 237 bytes --]

v4. Add new section to manual. Alias `erc-reconnect-display' to
`erc-auto-reconnect-display'. Add new contexts for `autojoin' module and
manual /RECONNECT invocations. Add option to force displaying of server
buffers when reconnecting.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v3-v4.diff --]
[-- Type: text/x-patch, Size: 28511 bytes --]

From 3e3a63eb426fc92558a7ca8e58022a3b50301ed5 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Sat, 8 Jul 2023 07:11:40 -0700
Subject: [PATCH 0/1] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (1):
  [5.6] Allow custom display-buffer actions in ERC

 doc/misc/erc.texi                             | 177 +++++++++++++++
 etc/ERC-NEWS                                  |  16 +-
 lisp/erc/erc-backend.el                       |  21 +-
 lisp/erc/erc-join.el                          |  17 +-
 lisp/erc/erc.el                               | 202 +++++++++++++-----
 .../erc/erc-scenarios-base-buffer-display.el  |  28 +--
 test/lisp/erc/erc-tests.el                    |  87 ++++++--
 7 files changed, 464 insertions(+), 84 deletions(-)

Interdiff:
diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index ddfdb2e2b64..f8da23a9865 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -613,6 +613,7 @@ Advanced Usage
 * URL::                         Opening IRC URLs in ERC.
 * SOCKS::                       Connecting to IRC with a SOCKS proxy.
 * auth-source::                 Retrieving auth-source entries with ERC.
+* display-buffer::              Controlling how ERC displays buffers.
 
 @end detailmenu
 @end menu
@@ -1226,6 +1227,7 @@ Integrations
 
 @menu
 * auth-source::                 Retrieving auth-source entries with ERC.
+* display-buffer::              Controlling how ERC displays buffers.
 @end menu
 
 @anchor{URL}
@@ -1468,6 +1470,181 @@ auth-source
 @samp{user} field (for example, @samp{login "#fsf"}, in netrc's case).
 The actual key goes in the @samp{password} (or @samp{secret}) field.
 
+@node display-buffer
+@subsection display-buffer
+@cindex display-buffer
+
+ERC supports the ``action'' interface used by @code{display-buffer}
+and friends from @file{window.el}.  @xref{Top,,Displaying Buffers,
+elisp, Emacs buffer display machinery}, for specifics.  When ERC
+displays a new or ``reassociated'' buffer, it consults its various
+buffer-display options, such as @code{erc-buffer-display}, to decide
+whether and how the buffer ought to appear in a window.  Exactly which
+one it consults depends on the context in which the buffer is being
+manifested.
+
+For some buffer-display options, the context is pretty cut and dry.
+For instance, in the case of @code{erc-receive-query-display}, you're
+receiving a query from someone you haven't yet chatted with in the
+current session.  For other options, like
+@code{erc-interactive-display}, the precise context varies.  For
+example, you might be opening a query buffer with the command
+@kbd{/QUERY bob @key{RET}} or joining a new channel with @kbd{/JOIN
+#chan @key{RET}}.  Power users wishing to distinguish between such
+nuanced contexts or just exercise more control over buffer-display
+behavior generally can elect to override these options by setting one
+or more to a ``@code{display-buffer}-like'' function that accepts a
+@var{buffer} and an @var{action} argument.
+
+@subsubheading Examples
+
+In this first example, a user-provided buffer-display function
+displays new server buffers in a split window when the user issues an
+@kbd{M-x erc-tls @key{RET}} or clicks an @samp{irc://}-style
+@acronym{URL} (@pxref{URL}).  Otherwise, ERC simply ``buries'' the
+buffer.  (For historical reasons, ERC's buffer-display options use the
+term ``bury'' to mean ``ignore'' rather than some operation that
+possibly modifies the buffer list.)
+
+@lisp
+(defun my-erc-interactive-display-buffer (buffer action)
+  "Pop to BUFFER when running \\[erc-tls] or clicking a link."
+  (when-let ((alist (cdr action))
+             (found (alist-get 'erc-interactive-display alist))
+             ((memq found '(erc-tls url))))
+    (pop-to-buffer buffer action)))
+
+(setopt erc-interactive-display #'my-erc-interactive-display-buffer)
+@end lisp
+
+@noindent
+Observe that ERC supplies the names of buffer-display options as
+@var{action} alist keys and pairs them with contextual constants, like
+the symbols @samp{erc-tls} or @samp{url}, the full lineup of which are
+enumerated below.
+
+In this second example, the user writes three predicates that somewhat
+resemble the ``@code{display-buffer}-like'' function above.  These too
+look for @var{action} alist keys sharing the names of buffer-display
+options (and, in one case, a module's minor mode).
+
+@lisp
+(defun my-erc-disp-entry-p (_ action)
+  (memq (cdr (or (assq 'erc-buffer-display action)
+                 (assq 'erc-interactive-display action)))
+        '(erc-tls url)))
+
+(defun my-erc-disp-query-p (_ action)
+  (or (eq (cdr (assq 'erc-interactive-display action)) '/QUERY)
+      (and (eq (cdr (assq 'erc-receive-query-display action)) 'PRIVMSG)
+           (member (erc-default-target) '("bob" "alice")))))
+
+(defun my-erc-disp-chan-p (_ action)
+  (or (assq 'erc-autojoin-mode action)
+      (and (memq (cdr (assq 'erc-buffer-display alist)) 'JOIN)
+           (member (erc-default-target) '("#emacs" "#fsf")))))
+@end lisp
+
+@noindent
+You'll notice we ignore the @var{buffer} parameter of these predicates
+because ERC ensures that @var{buffer} is already current (which is why
+we can freely call @code{erc-default-target}).  Note also that we
+cheat a little by treating the @var{action} parameter like an alist
+when it's really a cons of one or more functions and an alist.
+
+@noindent
+To complement our predicates, we set all three buffer-display options
+referenced in their @var{action}-alist lookups to
+@code{display-buffer}.  This tells ERC to defer to that function in
+the display contexts covered by these options.
+
+@lisp
+(setopt erc-buffer-display #'display-buffer
+        erc-interactive-display #'display-buffer
+        erc-receive-query-display #'display-buffer
+        ;;
+        erc-auto-reconnect-display 'bury)
+@end lisp
+
+@noindent
+Finally, we compose our predicates into @code{buffer-match-p}
+conditions and pair them with various well known @code{display-buffer}
+action functions and action-alist members.
+
+@lisp
+(setopt display-buffer-alist
+
+        ;; Create new frame with M-x erc-tls RET or (erc-tls ...)
+        '(((and (major-mode . erc-mode) my-erc-disp-entry-p)
+           display-buffer-pop-up-frame
+           (reusable-frames . visible))
+
+          ;; Show important chans and queries in a split.
+          ((and (major-mode . erc-mode)
+                (or my-erc-disp-chan-p my-erc-disp-query-p))
+           display-buffer-pop-up-window)
+
+          ;; Ignore everything else.
+          ((major-mode . erc-mode)
+           display-buffer-no-window
+           (allow-no-window . t))))
+@end lisp
+
+@noindent
+Of course, we could just as well set our buffer-display options to one
+or more homespun functions instead of bothering with
+@code{display-buffer-alist} at all (in what would make for a more
+complicated version of our first example).  But perhaps we already
+have a growing menagerie of similar predicates and like to keep
+everything in one place in our @file{init.el}.
+
+@subsubheading Action alist items
+
+@table @asis
+@item Option-based keys:
+All keys are symbols, as are values, unless otherwise noted.
+
+@itemize @bullet
+@item @code{erc-buffer-display}
+@itemize @minus
+@item @samp{JOIN}
+@item @samp{NOTICE}
+@item @samp{PRIVMSG}
+@item @samp{erc} (entry point called non-interactively)
+@item @samp{erc-tls}
+@end itemize
+
+@item @code{erc-interactive-display}
+@itemize @minus
+@item @samp{/QUERY}
+@item @samp{/JOIN}
+@item @samp{/RECONNECT}
+@item @samp{url} (hyperlink clicked)
+@item @samp{erc} (entry point called interactively)
+@item @samp{erc-tls}
+@end itemize
+
+@item @code{erc-receive-query-display}
+@itemize @minus
+@item @samp{NOTICE}
+@item @samp{PRIVMSG}
+@end itemize
+
+@item @code{erc-auto-reconnect-display}
+@itemize @minus
+@item something non-@code{nil}
+@end itemize
+@end itemize
+
+@item Module-based (minor-mode) keys:
+
+@itemize @bullet
+@item @code{erc-interactive-display}
+@itemize @minus
+@item channel name as a string
+@end itemize
+@end itemize
+@end table
 
 @node Options
 @section Options
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index a02d070d812..55f9aa4a8d5 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -56,17 +56,19 @@ reported as being difficult to discover and remember.  When the latter
 option (now known as 'erc-receive-query-display') is nil, ERC uses
 'erc-join-buffer' in its place, much like it does for
 'erc-interactive-display'.  The old nil behavior can still be gotten
-via the new compatibility flag 'erc-receive-query-display-defer'.
+via the new compatibility flag 'erc-receive-query-display-defer'.  The
+relatively new option 'erc-reconnect-display' has likewise been
+renamed, this time for clarity, to 'erc-auto-reconnect-display'.
 
 This release also introduces a few subtleties affecting the display of
 new or reassociated buffers.  One involves buffers that already occupy
 the selected window.  ERC now treats these as deserving of an implicit
 'bury'.  An escape hatch for this and most other baked-in behaviors is
 now available in the form of a new type variant recognized by all such
-options.  That is, users can now specify their own 'display-buffer'
-function to exercise full control over nearly all buffer-display
-related decisions.  See the newly expanded doc strings of
-'erc-buffer-display' and friends for details.
+options.  That is, users can now specify their own function to
+exercise full control over nearly all buffer-display related
+decisions.  See the newly expanded doc strings of 'erc-buffer-display'
+and friends for details.
 
 ** Setting a module's mode variable via Customize earns a warning.
 Trying and failing to activate a module via its minor mode's Custom
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index e01e6019e7c..dcec817107f 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -305,7 +305,7 @@ erc--server-reconnect-display-timer
   "Timer that resets `erc--server-last-reconnect-count' to zero.
 Becomes non-nil in all server buffers when an IRC connection is
 first \"established\" and carries out its duties
-`erc-reconnect-display-timeout' seconds later.")
+`erc-auto-reconnect-display-timeout' seconds later.")
 
 (defvar-local erc--server-last-reconnect-count 0
   "Snapshot of reconnect count when the connection was established.")
@@ -958,7 +958,7 @@ erc--server-last-reconnect-on-disconnect
   (erc--server-last-reconnect-display-reset (current-buffer)))
 
 (defun erc--server-last-reconnect-display-reset (buffer)
-  "Deactivate `erc-reconnect-display'."
+  "Deactivate `erc-auto-reconnect-display'."
   (when (buffer-live-p buffer)
     (with-current-buffer buffer
       (when erc--server-reconnect-display-timer
@@ -1685,13 +1685,17 @@ define-erc-response-handler
          parsed 'notice 'active
          'INVITE ?n nick ?u login ?h host ?c chnl)))))
 
+(cl-defmethod erc--server-determine-join-display-context (_channel alist)
+  "Determine `erc--display-context' for JOINs."
+  (if (assq 'erc-buffer-display alist)
+      alist
+    `((erc-buffer-display . JOIN) ,@alist)))
+
 (define-erc-response-handler (JOIN)
   "Handle join messages."
   nil
   (let ((chnl (erc-response.contents parsed))
-        (buffer nil)
-        (erc--display-context `((erc-buffer-display . JOIN)
-                                ,@erc--display-context)))
+        (buffer nil))
     (pcase-let ((`(,nick ,login ,host)
                  (erc-parse-user (erc-response.sender parsed))))
       ;; strip the stupid combined JOIN facility (IRC 2.9)
@@ -1701,7 +1705,11 @@ define-erc-response-handler
         (let* ((str (cond
                      ;; If I have joined a channel
                      ((erc-current-nick-p nick)
-                      (when (setq buffer (erc--open-target chnl))
+                      (let ((erc--display-context
+                             (erc--server-determine-join-display-context
+                              chnl erc--display-context)))
+                        (setq buffer (erc--open-target chnl)))
+                      (when buffer
                         (set-buffer buffer)
                         (with-suppressed-warnings
                             ((obsolete erc-add-default-channel))
diff --git a/lisp/erc/erc-join.el b/lisp/erc/erc-join.el
index 45cfd565f89..4419b620cf5 100644
--- a/lisp/erc/erc-join.el
+++ b/lisp/erc/erc-join.el
@@ -48,7 +48,9 @@ autojoin
   ((remove-hook 'erc-after-connect #'erc-autojoin-channels)
    (remove-hook 'erc-nickserv-identified-hook #'erc-autojoin-after-ident)
    (remove-hook 'erc-server-JOIN-functions #'erc-autojoin-add)
-   (remove-hook 'erc-server-PART-functions #'erc-autojoin-remove)))
+   (remove-hook 'erc-server-PART-functions #'erc-autojoin-remove)
+   (erc-buffer-do (lambda ()
+                    (kill-local-variable 'erc-join--requested-channels)))))
 
 (defcustom erc-autojoin-channels-alist nil
   "Alist of channels to autojoin on IRC networks.
@@ -138,6 +140,18 @@ erc-autojoin-server-match
       (string-match-p candidate (or erc-server-announced-name
                                     erc-session-server)))))
 
+(defvar-local erc-join--requested-channels nil
+  "List of channels for which an outgoing JOIN was sent.")
+
+(cl-defmethod erc--server-determine-join-display-context
+  (channel alist &context (erc-autojoin-mode (eql t)))
+  "Add item to `erc-display-context' ALIST if CHANNEL was autojoined."
+  (when (member channel erc-join--requested-channels)
+    (setq erc-join--requested-channels
+          (delete channel erc-join--requested-channels))
+    (push (cons 'erc-autojoin-mode channel) alist))
+  (cl-call-next-method channel alist))
+
 (defun erc-autojoin--join ()
   ;; This is called in the server buffer
   (pcase-dolist (`(,name . ,channels) erc-autojoin-channels-alist)
@@ -146,6 +160,7 @@ erc-autojoin--join
         (let ((buf (erc-get-buffer chan erc-server-process)))
           (unless (and buf (with-current-buffer buf
                              (erc--current-buffer-joined-p)))
+            (push chan erc-join--requested-channels)
             (erc-server-join-channel nil chan)))))))
 
 (defun erc-autojoin-after-ident (_network _nick)
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 849b433348c..c4c698c8273 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1564,9 +1564,9 @@ erc--buffer-display-choices
 (defvaralias 'erc-join-buffer 'erc-buffer-display)
 (defcustom erc-buffer-display 'bury
   "How to display a newly created ERC buffer.
-This determines the baseline, \"catch-all\" display behavior.  It
-takes a backseat to more context-specific display options, like
-`erc-interactive-display', `erc-reconnect-display', and
+This determines ERC's baseline, \"catch-all\" buffer-display
+behavior.  It takes a backseat to more specific options, like
+`erc-interactive-display', `erc-auto-reconnect-display', and
 `erc-receive-query-display'.
 
 The available choices are:
@@ -1581,10 +1581,11 @@ erc-buffer-display
 Here, DISPLAY-FUNCTION should accept a buffer and an ACTION of
 the kind described by the Info node `(elisp) Choosing Window'.
 At times, ERC may add hints about the calling context to the
-ACTION's alist.  Keys are symbols of user options, like
-`erc-buffer-display', and values are predefined constants
-specific to each.  For this particular option, possible values
-include the symbols
+ACTION's alist.  Keys are symbols such as user options, like
+`erc-buffer-display', or module minor modes, like
+`erc-autojoin-mode'.  Values are non-nil constants specific to
+each.  For this particular option, possible values include the
+symbols
 
   `JOIN', `PRIVMSG', `NOTICE', `erc', and `erc-tls'.
 
@@ -1617,7 +1618,7 @@ erc-interactive-display
 alist\" included as part of the second argument.  The item's key
 is the symbol `erc-interactive-display' and its value one of
 
-  `/QUERY', `/JOIN', `url', `erc', or `erc-tls'.
+  `/QUERY', `/JOIN', `/RECONNECT', `url', `erc', or `erc-tls'.
 
 All are symbols indicating an inciting user action, such as the
 issuance of a slash command, the clicking of a URL hyperlink, or
@@ -1626,28 +1627,41 @@ erc-interactive-display
   :group 'erc-buffers
   :type erc--buffer-display-choices)
 
-(defcustom erc-reconnect-display nil
-  "How and whether to display a channel buffer when auto-reconnecting.
-This only affects automatic reconnections and is ignored, like
-all other buffer-display options, when issuing a /RECONNECT or
-successfully reinvoking `erc-tls' with similar arguments.  See
-`erc-buffer-display' for a description of possible values.
+(defvaralias 'erc-reconnect-display 'erc-auto-reconnect-display)
+(defcustom erc-auto-reconnect-display nil
+  "How to display a channel buffer when automatically reconnecting.
+ERC ignores this option when a user issues a /RECONNECT or
+successfully reinvokes `erc-tls' with similar arguments to those
+from the prior connection.  See `erc-buffer-display' for a
+description of possible values.
 
 When the value is function, ERC may inject a hint about the
 calling context as an extra item in the alist making up the tail
 of the second, \"action\" argument.  The item's key is the symbol
-`erc-reconnect-display' and its value something non-nil."
+`erc-auto-reconnect-display' and its value something non-nil."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
   :type erc--buffer-display-choices)
 
-(defcustom erc-reconnect-display-timeout 10
-  "Duration `erc-reconnect-display' remains active.
+(defcustom erc-auto-reconnect-display-timeout 10
+  "Duration `erc-auto-reconnect-display' remains active.
 The countdown starts on MOTD and is canceled early by any
 \"slash\" command."
+  :package-version '(ERC . "5.6") ; FIXME sync on release
   :type 'integer
   :group 'erc-buffers)
 
+(defcustom erc-reconnect-display-server-buffers nil
+  "Apply buffer-display options to server buffers when reconnecting.
+By default, ERC does not consider `erc-auto-reconnect-display'
+for server buffers when automatically reconnecting, nor does it
+consider `erc-interactive-display' when users issue a /RECONNECT.
+Enabling this tells ERC to always display server buffers
+according to those options."
+  :package-version '(ERC . "5.6") ; FIXME sync on release
+  :type 'boolean
+  :group 'erc-buffers)
+
 (defcustom erc-frame-alist nil
   "Alist of frame parameters for creating erc frames.
 A value of nil means to use `default-frame-alist'."
@@ -1857,9 +1871,8 @@ erc-buffer-filter
 
 (defalias 'erc-buffer-do 'erc-buffer-filter
   "Call FUNCTION in all ERC buffers or only those for PROC.
-Expect users to prefer this alias to `erc-buffer-filter' in cases
-where the latter would only be called for effect and its return
-value thrown away.
+Expect to be preferred over `erc-buffer-filter' in cases where
+the return value goes unused.
 
 \(fn FUNCTION &optional PROC)")
 
@@ -2130,9 +2143,9 @@ erc--setup-buffer-hook
 (defvar erc--display-context nil
   "Extra action alist items passed to `display-buffer'.
 Non-nil when a user specifies a custom display action for certain
-display-options, like `erc-reconnect-display'.  ERC pairs the
-option's symbol with a context-dependent value and adds the entry
-to the user-provided alist when calling `pop-to-buffer' or
+display-options, like `erc-auto-reconnect-display'.  ERC pairs
+the option's symbol with a context-dependent value and adds the
+entry to the user-provided alist when calling `pop-to-buffer' or
 `display-buffer'.")
 
 (defvar erc-skip-displaying-selected-window-buffer t
@@ -2141,7 +2154,7 @@ erc-skip-displaying-selected-window-buffer
 (make-obsolete 'erc-show-already-displayed-buffer
                "non-nil behavior to be made permanent" "30.1")
 
-(defvar-local erc--display-buffer-action nil
+(defvar-local erc--display-buffer-overriding-action nil
   "The value of `display-buffer-overriding-action' when non-nil.
 Influences the displaying of new or reassociated ERC buffers.
 Reserved for use by built-in modules.")
@@ -2151,10 +2164,10 @@ erc-setup-buffer
   (pcase (if (zerop (erc-with-server-buffer
                       erc--server-last-reconnect-count))
              erc-join-buffer
-           (or erc-reconnect-display erc-join-buffer))
+           (or erc-auto-reconnect-display erc-join-buffer))
     ((and (pred functionp) disp-fn (let context erc--display-context))
      (unless (zerop erc--server-last-reconnect-count)
-       (push '(erc-reconnect-display . t) context))
+       (push '(erc-auto-reconnect-display . t) context))
      (funcall disp-fn buffer (cons nil context)))
     ((guard (and erc-skip-displaying-selected-window-buffer
                  (eq (window-buffer) buffer))))
@@ -2350,13 +2363,16 @@ erc-open
       (erc-update-mode-line))
 
     ;; Now display the buffer in a window as per user wishes.
-    (unless (eq buffer old-buffer)
+    (when (eq buffer old-buffer) (cl-assert (and connect (not target))))
+    (unless (and (not erc-reconnect-display-server-buffers)
+                 (eq buffer old-buffer))
       (when erc-log-p
         ;; we can't log to debug buffer, it may not exist yet
         (message "erc: old buffer %s, switching to %s"
                  old-buffer buffer))
       (let ((display-buffer-overriding-action
-             (or erc--display-buffer-action display-buffer-overriding-action)))
+             (or erc--display-buffer-overriding-action
+                 display-buffer-overriding-action)))
         (erc-setup-buffer buffer)
         (run-hooks 'erc--setup-buffer-hook)))
 
@@ -4270,6 +4286,9 @@ 'erc-cmd-GQ
 
 (defun erc--cmd-reconnect ()
   (let ((buffer (erc-server-buffer))
+        (erc-join-buffer erc-interactive-display)
+        (erc--display-context `((erc-interactive-display . /RECONNECT)
+                                ,@erc--display-context))
         (process nil))
     (unless (buffer-live-p buffer)
       (setq buffer (current-buffer)))
@@ -5362,7 +5381,7 @@ erc-connection-established
         (setq erc--server-last-reconnect-count erc-server-reconnect-count
               erc-server-reconnect-count 0)
         (setq erc--server-reconnect-display-timer
-              (run-at-time erc-reconnect-display-timeout nil
+              (run-at-time erc-auto-reconnect-display-timeout nil
                            #'erc--server-last-reconnect-display-reset
                            (current-buffer)))
         (add-hook 'erc-disconnected-hook
diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
index 548ad00e2d9..df292a8c113 100644
--- a/test/lisp/erc/erc-scenarios-base-buffer-display.el
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -26,8 +26,8 @@
 
 (eval-when-compile (require 'erc-join))
 
-;; These first couple `erc-reconnect-display' tests used to live in
-;; erc-scenarios-base-reconnect but have since been renamed.
+;; These first couple `erc-auto-reconnect-display' tests used to live
+;; in erc-scenarios-base-reconnect but have since been renamed.
 
 (defun erc-scenarios-base-buffer-display--reconnect-common
     (assert-server assert-chan assert-rest)
@@ -80,11 +80,11 @@ erc-scenarios-base-buffer-display--defwin-recbury-intbuf
   :tags '(:expensive-test)
   (should (eq erc-buffer-display 'bury))
   (should (eq erc-interactive-display 'window))
-  (should-not erc-reconnect-display)
+  (should-not erc-auto-reconnect-display)
 
   (let ((erc-buffer-display 'window)
         (erc-interactive-display 'buffer)
-        (erc-reconnect-display 'bury))
+        (erc-auto-reconnect-display 'bury))
 
     (erc-scenarios-base-buffer-display--reconnect-common
 
@@ -104,7 +104,7 @@ erc-scenarios-base-buffer-display--defwin-recbury-intbuf
        ;; A manual /JOIN command tells ERC we're done auto-reconnecting
        (with-current-buffer "FooNet" (erc-scenarios-common-say "/JOIN #spam"))
 
-       (ert-info ("#spam ignores `erc-reconnect-display'")
+       (ert-info ("#spam ignores `erc-auto-reconnect-display'")
          ;; Uses `erc-interactive-display' instead.
          (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
            (should (eq (window-buffer) (get-buffer "#spam")))
@@ -115,10 +115,10 @@ erc-scenarios-base-buffer-display--defwino-recbury-intbuf
   :tags '(:expensive-test)
   (should (eq erc-buffer-display 'bury))
   (should (eq erc-interactive-display 'window))
-  (should-not erc-reconnect-display)
+  (should-not erc-auto-reconnect-display)
 
   (let ((erc-buffer-display 'window-noselect)
-        (erc-reconnect-display 'bury)
+        (erc-auto-reconnect-display 'bury)
         (erc-interactive-display 'buffer))
     (erc-scenarios-base-buffer-display--reconnect-common
 
@@ -155,7 +155,7 @@ erc-scenarios-base-buffer-display--defwino-recbury-intbuf
            (should (eq (window-buffer) (get-buffer "bob")))
            (should (frame-root-window-p (selected-window)))))
 
-       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+       (ert-info ("Newly joined chan ignores `erc-auto-reconnect-display'")
          (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
            (should (eq (window-buffer) (get-buffer "bob")))
            (should-not (frame-root-window-p (selected-window)))
@@ -165,13 +165,13 @@ erc-scenarios-base-buffer-display--count-reset-timeout
   :tags '(:expensive-test)
   (should (eq erc-buffer-display 'bury))
   (should (eq erc-interactive-display 'window))
-  (should (eq erc-reconnect-display-timeout 10))
-  (should-not erc-reconnect-display)
+  (should (eq erc-auto-reconnect-display-timeout 10))
+  (should-not erc-auto-reconnect-display)
 
   (let ((erc-buffer-display 'window-noselect)
-        (erc-reconnect-display 'bury)
+        (erc-auto-reconnect-display 'bury)
         (erc-interactive-display 'buffer)
-        (erc-reconnect-display-timeout 0.5))
+        (erc-auto-reconnect-display-timeout 0.5))
     (erc-scenarios-base-buffer-display--reconnect-common
      #'ignore #'ignore ; These two are identical to the previous test.
 
@@ -188,10 +188,10 @@ erc-scenarios-base-buffer-display--count-reset-timeout
            (erc-d-t-wait-for 1 (null erc--server-reconnect-display-timer))
            (erc-cmd-JOIN "#spam")))
 
-       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+       (ert-info ("Newly joined chan ignores `erc-auto-reconnect-display'")
          (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
            (should (eq (window-buffer) (messages-buffer)))
-           ;; If `erc-reconnect-display-timeout' were left alone, this
+           ;; If `erc-auto-reconnect-display-timeout' were left alone, this
            ;; would be (frame-root-window-p #<window 1 on *scratch*>).
            (should-not (frame-root-window-p (selected-window)))
            (should (eq (current-buffer) (window-buffer (next-window))))))))))
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 2bad79c948b..4596dd9845e 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -418,8 +418,9 @@ erc--initialize-markers
       (should (looking-at-p (regexp-quote "*** Welcome"))))
 
     (ert-info ("Reconnect")
-      (erc-open "localhost" 6667 "tester" "Tester" nil
-                "fake" nil "#chan" proc nil "user" nil)
+      (with-current-buffer (erc-server-buffer)
+        (erc-open "localhost" 6667 "tester" "Tester" nil
+                  "fake" nil "#chan" proc nil "user" nil))
       (should-not (get-buffer "#chan<2>")))
 
     (ert-info ("Existing prompt respected")
@@ -533,13 +534,14 @@ erc-setup-buffer--custom-action
                        (pop calls)))
         (should-not calls))
 
-      ;; `pop-to-buffer' with `erc-reconnect-display'
+      ;; `pop-to-buffer' with `erc-auto-reconnect-display'
       (let* ((erc--server-last-reconnect-count 1)
              (erc--display-context '((erc-buffer-display . 1)))
-             (erc-reconnect-display 'erc--test-fun))
+             (erc-auto-reconnect-display 'erc--test-fun))
         (erc-setup-buffer mbuf)
-        (should (equal `(erc--test-fun ,mbuf (nil (erc-reconnect-display . t)
-                                                  (erc-buffer-display . 1)))
+        (should (equal `(erc--test-fun ,mbuf
+                                       (nil (erc-auto-reconnect-display . t)
+                                            (erc-buffer-display . 1)))
                        (pop calls)))
         (should-not calls)))
 
-- 
2.41.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-5.6-Allow-custom-display-buffer-actions-in-ERC.patch --]
[-- Type: text/x-patch, Size: 47994 bytes --]

From 3e3a63eb426fc92558a7ca8e58022a3b50301ed5 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Tue, 30 May 2023 23:27:12 -0700
Subject: [PATCH 1/1] [5.6] Allow custom display-buffer actions in ERC

* doc/misc/erc.texi: Add new section for buffer-display options under
the Integrations chapter.
* etc/ERC-NEWS: Mention new function variant for all buffer-display
options.
* lisp/erc/erc-backend.el (erc--server-reconnect-display-timer,
erc-server-last-reconnect-on-disconnect): Use new name for option
`erc-reconnect-display', now `erc-auto-reconnect-display'.
(erc--server-determine-join-display-context): New generic function to
determine value of `erc--display-context' during JOINs.
(erc-server-JOIN, erc-server-PRIVMSG): Set `erc--display-context' to a
symbol for the handler's IRC command, like `JOIN', in order to
influence `erc-setup-buffer' by way of `erc--open-target'.
* lisp/erc/erc-join.el (erc-autojoin-disable): Unset variable
`erc-join--requested-channels'.
(erc-join--requested-channels): New local variable to remember
channels we've attempted to JOIN this session that haven't yet been
confirmed by the server.
(erc--server-determine-join-display-context): New implementation of
generic function for `erc-autojoin-mode'.
(erc-autojoin--join): Remember JOIN'd channels.
* lisp/erc/erc.el (erc--buffer-display-choices): New helper for
defining common `:type' for all buffer-display options.
(erc-buffer-display): Add new choice of either `display-buffer' or
`pop-to-buffer' paired with an "action alist".
(erc-buffer-display, erc-interactive-display,
erc-auto-reconnect-display, erc-receive-query-display): Use helper
`erc--buffer-display-choices' for defining `:type'.
(erc-reconnect-display, erc-auto-reconnect-display): Alias former to
latter, now the preferred name.
(erc-reconnect-timeout, erc-auto-reconnect-timeout): Change name from
former to latter.  This option is new in ERC 5.6.
(erc-reconnect-display-include-server-buffers): New option.
(erc-buffer-do): Revise doc string.
(erc--display-context): New variable to contain an alist of "context
tokens" to be provided as the "action alist" to `erc-buffer-display'
functions.
(erc-skip-displaying-selected-window-buffer): New variable, deprecated
at birth, to act as an escape hatch for folks who don't want to skip
the displaying of buffers already showing in the selected window.
(erc--display-buffer-overriding-action): Local variable allowing
modules to influence the displaying of new ERC buffers independently
of user options.
(erc-setup-buffer): Do nothing when the selected window already shows
current buffer unless user has provided a custom display function.
Accommodate new Custom choice function values `display-buffer' and
`pop-to-buffer'.
(erc-open): Run `erc-setup-buffer' when option
`erc-reconnect-display-include-server-buffers' is non-nil, even for
existing server buffers.  Bind `display-buffer-overriding-action' to
the value of `erc--display-buffer-overriding-action' around calls to
`erc-setup-buffer'.
(erc-select-read-args): Add `erc--display-context' to environment.
(erc, erc-tls): Bind `erc--display-context' around calls to
`erc-select-read-args' and main body.
(erc-cmd-JOIN, erc-cmd-QUERY, erc--cmd-reconnect, erc-handle-irc-url):
Add item for `erc-interactive-display' to `erc--display-context'.
* test/lisp/erc/erc-scenarios-base-buffer-display.el
(erc-scenarios-base-buffer-display--defwin-recbury-intbuf,
erc-scenarios-base-buffer-display--defwino-recbury-intbuf,
erc-scenarios-base-buffer-display--count-reset-timeout): Use preferred
name `erc-auto-reconnect-display' for `erc-reconnect-display'.
* test/lisp/erc/erc-tests.el (erc--initialize-markers): Fix
unrealistic call to `erc-open'.
(erc-setup-buffer--custom-action): New test.
(erc-select-read-args, erc-tls, erc--interactive):
Expect new environment binding for `erc--display-context'.
(Bug#62833)
---
 doc/misc/erc.texi                             | 177 +++++++++++++++
 etc/ERC-NEWS                                  |  16 +-
 lisp/erc/erc-backend.el                       |  21 +-
 lisp/erc/erc-join.el                          |  17 +-
 lisp/erc/erc.el                               | 202 +++++++++++++-----
 .../erc/erc-scenarios-base-buffer-display.el  |  28 +--
 test/lisp/erc/erc-tests.el                    |  87 ++++++--
 7 files changed, 464 insertions(+), 84 deletions(-)

diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index ddfdb2e2b64..f8da23a9865 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -613,6 +613,7 @@ Advanced Usage
 * URL::                         Opening IRC URLs in ERC.
 * SOCKS::                       Connecting to IRC with a SOCKS proxy.
 * auth-source::                 Retrieving auth-source entries with ERC.
+* display-buffer::              Controlling how ERC displays buffers.
 
 @end detailmenu
 @end menu
@@ -1226,6 +1227,7 @@ Integrations
 
 @menu
 * auth-source::                 Retrieving auth-source entries with ERC.
+* display-buffer::              Controlling how ERC displays buffers.
 @end menu
 
 @anchor{URL}
@@ -1468,6 +1470,181 @@ auth-source
 @samp{user} field (for example, @samp{login "#fsf"}, in netrc's case).
 The actual key goes in the @samp{password} (or @samp{secret}) field.
 
+@node display-buffer
+@subsection display-buffer
+@cindex display-buffer
+
+ERC supports the ``action'' interface used by @code{display-buffer}
+and friends from @file{window.el}.  @xref{Top,,Displaying Buffers,
+elisp, Emacs buffer display machinery}, for specifics.  When ERC
+displays a new or ``reassociated'' buffer, it consults its various
+buffer-display options, such as @code{erc-buffer-display}, to decide
+whether and how the buffer ought to appear in a window.  Exactly which
+one it consults depends on the context in which the buffer is being
+manifested.
+
+For some buffer-display options, the context is pretty cut and dry.
+For instance, in the case of @code{erc-receive-query-display}, you're
+receiving a query from someone you haven't yet chatted with in the
+current session.  For other options, like
+@code{erc-interactive-display}, the precise context varies.  For
+example, you might be opening a query buffer with the command
+@kbd{/QUERY bob @key{RET}} or joining a new channel with @kbd{/JOIN
+#chan @key{RET}}.  Power users wishing to distinguish between such
+nuanced contexts or just exercise more control over buffer-display
+behavior generally can elect to override these options by setting one
+or more to a ``@code{display-buffer}-like'' function that accepts a
+@var{buffer} and an @var{action} argument.
+
+@subsubheading Examples
+
+In this first example, a user-provided buffer-display function
+displays new server buffers in a split window when the user issues an
+@kbd{M-x erc-tls @key{RET}} or clicks an @samp{irc://}-style
+@acronym{URL} (@pxref{URL}).  Otherwise, ERC simply ``buries'' the
+buffer.  (For historical reasons, ERC's buffer-display options use the
+term ``bury'' to mean ``ignore'' rather than some operation that
+possibly modifies the buffer list.)
+
+@lisp
+(defun my-erc-interactive-display-buffer (buffer action)
+  "Pop to BUFFER when running \\[erc-tls] or clicking a link."
+  (when-let ((alist (cdr action))
+             (found (alist-get 'erc-interactive-display alist))
+             ((memq found '(erc-tls url))))
+    (pop-to-buffer buffer action)))
+
+(setopt erc-interactive-display #'my-erc-interactive-display-buffer)
+@end lisp
+
+@noindent
+Observe that ERC supplies the names of buffer-display options as
+@var{action} alist keys and pairs them with contextual constants, like
+the symbols @samp{erc-tls} or @samp{url}, the full lineup of which are
+enumerated below.
+
+In this second example, the user writes three predicates that somewhat
+resemble the ``@code{display-buffer}-like'' function above.  These too
+look for @var{action} alist keys sharing the names of buffer-display
+options (and, in one case, a module's minor mode).
+
+@lisp
+(defun my-erc-disp-entry-p (_ action)
+  (memq (cdr (or (assq 'erc-buffer-display action)
+                 (assq 'erc-interactive-display action)))
+        '(erc-tls url)))
+
+(defun my-erc-disp-query-p (_ action)
+  (or (eq (cdr (assq 'erc-interactive-display action)) '/QUERY)
+      (and (eq (cdr (assq 'erc-receive-query-display action)) 'PRIVMSG)
+           (member (erc-default-target) '("bob" "alice")))))
+
+(defun my-erc-disp-chan-p (_ action)
+  (or (assq 'erc-autojoin-mode action)
+      (and (memq (cdr (assq 'erc-buffer-display alist)) 'JOIN)
+           (member (erc-default-target) '("#emacs" "#fsf")))))
+@end lisp
+
+@noindent
+You'll notice we ignore the @var{buffer} parameter of these predicates
+because ERC ensures that @var{buffer} is already current (which is why
+we can freely call @code{erc-default-target}).  Note also that we
+cheat a little by treating the @var{action} parameter like an alist
+when it's really a cons of one or more functions and an alist.
+
+@noindent
+To complement our predicates, we set all three buffer-display options
+referenced in their @var{action}-alist lookups to
+@code{display-buffer}.  This tells ERC to defer to that function in
+the display contexts covered by these options.
+
+@lisp
+(setopt erc-buffer-display #'display-buffer
+        erc-interactive-display #'display-buffer
+        erc-receive-query-display #'display-buffer
+        ;;
+        erc-auto-reconnect-display 'bury)
+@end lisp
+
+@noindent
+Finally, we compose our predicates into @code{buffer-match-p}
+conditions and pair them with various well known @code{display-buffer}
+action functions and action-alist members.
+
+@lisp
+(setopt display-buffer-alist
+
+        ;; Create new frame with M-x erc-tls RET or (erc-tls ...)
+        '(((and (major-mode . erc-mode) my-erc-disp-entry-p)
+           display-buffer-pop-up-frame
+           (reusable-frames . visible))
+
+          ;; Show important chans and queries in a split.
+          ((and (major-mode . erc-mode)
+                (or my-erc-disp-chan-p my-erc-disp-query-p))
+           display-buffer-pop-up-window)
+
+          ;; Ignore everything else.
+          ((major-mode . erc-mode)
+           display-buffer-no-window
+           (allow-no-window . t))))
+@end lisp
+
+@noindent
+Of course, we could just as well set our buffer-display options to one
+or more homespun functions instead of bothering with
+@code{display-buffer-alist} at all (in what would make for a more
+complicated version of our first example).  But perhaps we already
+have a growing menagerie of similar predicates and like to keep
+everything in one place in our @file{init.el}.
+
+@subsubheading Action alist items
+
+@table @asis
+@item Option-based keys:
+All keys are symbols, as are values, unless otherwise noted.
+
+@itemize @bullet
+@item @code{erc-buffer-display}
+@itemize @minus
+@item @samp{JOIN}
+@item @samp{NOTICE}
+@item @samp{PRIVMSG}
+@item @samp{erc} (entry point called non-interactively)
+@item @samp{erc-tls}
+@end itemize
+
+@item @code{erc-interactive-display}
+@itemize @minus
+@item @samp{/QUERY}
+@item @samp{/JOIN}
+@item @samp{/RECONNECT}
+@item @samp{url} (hyperlink clicked)
+@item @samp{erc} (entry point called interactively)
+@item @samp{erc-tls}
+@end itemize
+
+@item @code{erc-receive-query-display}
+@itemize @minus
+@item @samp{NOTICE}
+@item @samp{PRIVMSG}
+@end itemize
+
+@item @code{erc-auto-reconnect-display}
+@itemize @minus
+@item something non-@code{nil}
+@end itemize
+@end itemize
+
+@item Module-based (minor-mode) keys:
+
+@itemize @bullet
+@item @code{erc-interactive-display}
+@itemize @minus
+@item channel name as a string
+@end itemize
+@end itemize
+@end table
 
 @node Options
 @section Options
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 5665b760ea9..55f9aa4a8d5 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -37,7 +37,7 @@ decade overdue, this is no longer the case.  Other UX improvements in
 this area aim to make the process of connecting interactively slightly
 more streamlined and less repetitive, even for veteran users.
 
-** Revised buffer-display handling for interactive commands.
+** Revised buffer-display handling.
 A point of friction for new users and one only just introduced with
 ERC 5.5 has been the lack of visual feedback when first connecting via
 M-x erc or when issuing a "/JOIN" command at the prompt.  As explained
@@ -56,7 +56,19 @@ reported as being difficult to discover and remember.  When the latter
 option (now known as 'erc-receive-query-display') is nil, ERC uses
 'erc-join-buffer' in its place, much like it does for
 'erc-interactive-display'.  The old nil behavior can still be gotten
-via the new compatibility flag 'erc-receive-query-display-defer'.
+via the new compatibility flag 'erc-receive-query-display-defer'.  The
+relatively new option 'erc-reconnect-display' has likewise been
+renamed, this time for clarity, to 'erc-auto-reconnect-display'.
+
+This release also introduces a few subtleties affecting the display of
+new or reassociated buffers.  One involves buffers that already occupy
+the selected window.  ERC now treats these as deserving of an implicit
+'bury'.  An escape hatch for this and most other baked-in behaviors is
+now available in the form of a new type variant recognized by all such
+options.  That is, users can now specify their own function to
+exercise full control over nearly all buffer-display related
+decisions.  See the newly expanded doc strings of 'erc-buffer-display'
+and friends for details.
 
 ** Setting a module's mode variable via Customize earns a warning.
 Trying and failing to activate a module via its minor mode's Custom
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index f1b51f9234a..dcec817107f 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -101,6 +101,7 @@
 (eval-when-compile (require 'cl-lib))
 (require 'erc-common)
 
+(defvar erc--display-context)
 (defvar erc--target)
 (defvar erc--user-from-nick-function)
 (defvar erc-channel-list)
@@ -304,7 +305,7 @@ erc--server-reconnect-display-timer
   "Timer that resets `erc--server-last-reconnect-count' to zero.
 Becomes non-nil in all server buffers when an IRC connection is
 first \"established\" and carries out its duties
-`erc-reconnect-display-timeout' seconds later.")
+`erc-auto-reconnect-display-timeout' seconds later.")
 
 (defvar-local erc--server-last-reconnect-count 0
   "Snapshot of reconnect count when the connection was established.")
@@ -957,7 +958,7 @@ erc--server-last-reconnect-on-disconnect
   (erc--server-last-reconnect-display-reset (current-buffer)))
 
 (defun erc--server-last-reconnect-display-reset (buffer)
-  "Deactivate `erc-reconnect-display'."
+  "Deactivate `erc-auto-reconnect-display'."
   (when (buffer-live-p buffer)
     (with-current-buffer buffer
       (when erc--server-reconnect-display-timer
@@ -1684,6 +1685,12 @@ define-erc-response-handler
          parsed 'notice 'active
          'INVITE ?n nick ?u login ?h host ?c chnl)))))
 
+(cl-defmethod erc--server-determine-join-display-context (_channel alist)
+  "Determine `erc--display-context' for JOINs."
+  (if (assq 'erc-buffer-display alist)
+      alist
+    `((erc-buffer-display . JOIN) ,@alist)))
+
 (define-erc-response-handler (JOIN)
   "Handle join messages."
   nil
@@ -1698,7 +1705,11 @@ define-erc-response-handler
         (let* ((str (cond
                      ;; If I have joined a channel
                      ((erc-current-nick-p nick)
-                      (when (setq buffer (erc--open-target chnl))
+                      (let ((erc--display-context
+                             (erc--server-determine-join-display-context
+                              chnl erc--display-context)))
+                        (setq buffer (erc--open-target chnl)))
+                      (when buffer
                         (set-buffer buffer)
                         (with-suppressed-warnings
                             ((obsolete erc-add-default-channel))
@@ -1887,6 +1898,8 @@ define-erc-response-handler
              (noticep (string= cmd "NOTICE"))
              ;; S.B. downcase *both* tgt and current nick
              (privp (erc-current-nick-p tgt))
+             (erc--display-context `((erc-buffer-display . ,(intern cmd))
+                                     ,@erc--display-context))
              s buffer
              fnick)
         (setf (erc-response.contents parsed) msg)
@@ -1901,6 +1914,8 @@ define-erc-response-handler
                               (and erc-ensure-target-buffer-on-privmsg
                                    (or erc-receive-query-display
                                        erc-join-buffer)))))
+                (push `(erc-receive-query-display . ,(intern cmd))
+                      erc--display-context)
                 (setq buffer (erc--open-target nick)))
             ;; A channel buffer has been killed but is still joined.
             (when erc-ensure-target-buffer-on-privmsg
diff --git a/lisp/erc/erc-join.el b/lisp/erc/erc-join.el
index 45cfd565f89..4419b620cf5 100644
--- a/lisp/erc/erc-join.el
+++ b/lisp/erc/erc-join.el
@@ -48,7 +48,9 @@ autojoin
   ((remove-hook 'erc-after-connect #'erc-autojoin-channels)
    (remove-hook 'erc-nickserv-identified-hook #'erc-autojoin-after-ident)
    (remove-hook 'erc-server-JOIN-functions #'erc-autojoin-add)
-   (remove-hook 'erc-server-PART-functions #'erc-autojoin-remove)))
+   (remove-hook 'erc-server-PART-functions #'erc-autojoin-remove)
+   (erc-buffer-do (lambda ()
+                    (kill-local-variable 'erc-join--requested-channels)))))
 
 (defcustom erc-autojoin-channels-alist nil
   "Alist of channels to autojoin on IRC networks.
@@ -138,6 +140,18 @@ erc-autojoin-server-match
       (string-match-p candidate (or erc-server-announced-name
                                     erc-session-server)))))
 
+(defvar-local erc-join--requested-channels nil
+  "List of channels for which an outgoing JOIN was sent.")
+
+(cl-defmethod erc--server-determine-join-display-context
+  (channel alist &context (erc-autojoin-mode (eql t)))
+  "Add item to `erc-display-context' ALIST if CHANNEL was autojoined."
+  (when (member channel erc-join--requested-channels)
+    (setq erc-join--requested-channels
+          (delete channel erc-join--requested-channels))
+    (push (cons 'erc-autojoin-mode channel) alist))
+  (cl-call-next-method channel alist))
+
 (defun erc-autojoin--join ()
   ;; This is called in the server buffer
   (pcase-dolist (`(,name . ,channels) erc-autojoin-channels-alist)
@@ -146,6 +160,7 @@ erc-autojoin--join
         (let ((buf (erc-get-buffer chan erc-server-process)))
           (unless (and buf (with-current-buffer buf
                              (erc--current-buffer-joined-p)))
+            (push chan erc-join--requested-channels)
             (erc-server-join-channel nil chan)))))))
 
 (defun erc-autojoin-after-ident (_network _nick)
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index e23185934f7..c4c698c8273 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1548,9 +1548,26 @@ erc-default-port-tls
   "IRC port to use for encrypted connections if it cannot be \
 detected otherwise.")
 
+(defconst erc--buffer-display-choices
+  `(choice (const :tag "Use value of `erc-buffer-display'" nil)
+           (const :tag "Split window and select" window)
+           (const :tag "Split window but don't select" window-noselect)
+           (const :tag "New frame" frame)
+           (const :tag "Don't display" bury)
+           (const :tag "Use current window" buffer)
+           (choice :tag "Defer to a display function"
+                   (function-item display-buffer)
+                   (function-item pop-to-buffer)
+                   (function :tag "User-defined")))
+  "Common choices for buffer-display options.")
+
 (defvaralias 'erc-join-buffer 'erc-buffer-display)
 (defcustom erc-buffer-display 'bury
   "How to display a newly created ERC buffer.
+This determines ERC's baseline, \"catch-all\" buffer-display
+behavior.  It takes a backseat to more specific options, like
+`erc-interactive-display', `erc-auto-reconnect-display', and
+`erc-receive-query-display'.
 
 The available choices are:
 
@@ -1559,17 +1576,33 @@ erc-buffer-display
   `frame'           - in another frame,
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
-
-See related options `erc-interactive-display',
-`erc-reconnect-display', and `erc-receive-query-display'."
+  DISPLAY-FUNCTION  - a `display-buffer'-like function
+
+Here, DISPLAY-FUNCTION should accept a buffer and an ACTION of
+the kind described by the Info node `(elisp) Choosing Window'.
+At times, ERC may add hints about the calling context to the
+ACTION's alist.  Keys are symbols such as user options, like
+`erc-buffer-display', or module minor modes, like
+`erc-autojoin-mode'.  Values are non-nil constants specific to
+each.  For this particular option, possible values include the
+symbols
+
+  `JOIN', `PRIVMSG', `NOTICE', `erc', and `erc-tls'.
+
+The first three signify IRC commands received from the server and
+the rest entry-point commands responsible for the connection.
+When dealing with the latter two, users may prefer to call
+DISPLAY-FUNCTION directly on (server) buffers returned by these
+entry points because the context leading to their creation is
+plainly obvious.
+
+Note that when the selected window already shows the current
+buffer, ERC pretends this option's value is `bury' unless the
+variable `erc-skip-displaying-selected-window-buffer' is nil or
+the value of this option is DISPLAY-FUNCTION."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
-  :type '(choice (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+  :type (cons 'choice (nthcdr 2 erc--buffer-display-choices)))
 
 (defvaralias 'erc-query-display 'erc-interactive-display)
 (defcustom erc-interactive-display 'window
@@ -1578,38 +1611,57 @@ erc-interactive-display
 interactively at the prompt.  It does not apply when calling a
 handler for such a command, like `erc-cmd-JOIN', from lisp code.
 See `erc-buffer-display' for a full description of available
-values."
+values.
+
+When the value is a user-provided function, ERC may inject a hint
+about the invocation context as an extra item in the \"action
+alist\" included as part of the second argument.  The item's key
+is the symbol `erc-interactive-display' and its value one of
+
+  `/QUERY', `/JOIN', `/RECONNECT', `url', `erc', or `erc-tls'.
+
+All are symbols indicating an inciting user action, such as the
+issuance of a slash command, the clicking of a URL hyperlink, or
+the invocation of an entry-point command."
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury new and don't display existing" bury)
-                 (const :tag "Use current buffer" buffer)))
-
-(defcustom erc-reconnect-display nil
-  "How and whether to display a channel buffer when auto-reconnecting.
-This only affects automatic reconnections and is ignored, like
-all other buffer-display options, when issuing a /RECONNECT or
-successfully reinvoking `erc-tls' with similar arguments.  See
-`erc-buffer-display' for a description of possible values."
+  :type erc--buffer-display-choices)
+
+(defvaralias 'erc-reconnect-display 'erc-auto-reconnect-display)
+(defcustom erc-auto-reconnect-display nil
+  "How to display a channel buffer when automatically reconnecting.
+ERC ignores this option when a user issues a /RECONNECT or
+successfully reinvokes `erc-tls' with similar arguments to those
+from the prior connection.  See `erc-buffer-display' for a
+description of possible values.
+
+When the value is function, ERC may inject a hint about the
+calling context as an extra item in the alist making up the tail
+of the second, \"action\" argument.  The item's key is the symbol
+`erc-auto-reconnect-display' and its value something non-nil."
   :package-version '(ERC . "5.5")
   :group 'erc-buffers
-  :type '(choice (const :tag "Use value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)))
-
-(defcustom erc-reconnect-display-timeout 10
-  "Duration `erc-reconnect-display' remains active.
+  :type erc--buffer-display-choices)
+
+(defcustom erc-auto-reconnect-display-timeout 10
+  "Duration `erc-auto-reconnect-display' remains active.
 The countdown starts on MOTD and is canceled early by any
 \"slash\" command."
+  :package-version '(ERC . "5.6") ; FIXME sync on release
   :type 'integer
   :group 'erc-buffers)
 
+(defcustom erc-reconnect-display-server-buffers nil
+  "Apply buffer-display options to server buffers when reconnecting.
+By default, ERC does not consider `erc-auto-reconnect-display'
+for server buffers when automatically reconnecting, nor does it
+consider `erc-interactive-display' when users issue a /RECONNECT.
+Enabling this tells ERC to always display server buffers
+according to those options."
+  :package-version '(ERC . "5.6") ; FIXME sync on release
+  :type 'boolean
+  :group 'erc-buffers)
+
 (defcustom erc-frame-alist nil
   "Alist of frame parameters for creating erc frames.
 A value of nil means to use `default-frame-alist'."
@@ -1819,9 +1871,8 @@ erc-buffer-filter
 
 (defalias 'erc-buffer-do 'erc-buffer-filter
   "Call FUNCTION in all ERC buffers or only those for PROC.
-Expect users to prefer this alias to `erc-buffer-filter' in cases
-where the latter would only be called for effect and its return
-value thrown away.
+Expect to be preferred over `erc-buffer-filter' in cases where
+the return value goes unused.
 
 \(fn FUNCTION &optional PROC)")
 
@@ -2089,12 +2140,37 @@ erc--updating-modules-p
 (defvar erc--setup-buffer-hook nil
   "Internal hook for module setup involving windows and frames.")
 
+(defvar erc--display-context nil
+  "Extra action alist items passed to `display-buffer'.
+Non-nil when a user specifies a custom display action for certain
+display-options, like `erc-auto-reconnect-display'.  ERC pairs
+the option's symbol with a context-dependent value and adds the
+entry to the user-provided alist when calling `pop-to-buffer' or
+`display-buffer'.")
+
+(defvar erc-skip-displaying-selected-window-buffer t
+  "Whether to forgo showing a buffer that's already being displayed.
+But only in the selected window.")
+(make-obsolete 'erc-show-already-displayed-buffer
+               "non-nil behavior to be made permanent" "30.1")
+
+(defvar-local erc--display-buffer-overriding-action nil
+  "The value of `display-buffer-overriding-action' when non-nil.
+Influences the displaying of new or reassociated ERC buffers.
+Reserved for use by built-in modules.")
+
 (defun erc-setup-buffer (buffer)
   "Consults `erc-join-buffer' to find out how to display `BUFFER'."
   (pcase (if (zerop (erc-with-server-buffer
                       erc--server-last-reconnect-count))
              erc-join-buffer
-           (or erc-reconnect-display erc-join-buffer))
+           (or erc-auto-reconnect-display erc-join-buffer))
+    ((and (pred functionp) disp-fn (let context erc--display-context))
+     (unless (zerop erc--server-last-reconnect-count)
+       (push '(erc-auto-reconnect-display . t) context))
+     (funcall disp-fn buffer (cons nil context)))
+    ((guard (and erc-skip-displaying-selected-window-buffer
+                 (eq (window-buffer) buffer))))
     ('window
      (if (active-minibuffer-window)
          (display-buffer buffer)
@@ -2287,13 +2363,18 @@ erc-open
       (erc-update-mode-line))
 
     ;; Now display the buffer in a window as per user wishes.
-    (unless (eq buffer old-buffer)
+    (when (eq buffer old-buffer) (cl-assert (and connect (not target))))
+    (unless (and (not erc-reconnect-display-server-buffers)
+                 (eq buffer old-buffer))
       (when erc-log-p
         ;; we can't log to debug buffer, it may not exist yet
         (message "erc: old buffer %s, switching to %s"
                  old-buffer buffer))
-      (erc-setup-buffer buffer)
-      (run-hooks 'erc--setup-buffer-hook))
+      (let ((display-buffer-overriding-action
+             (or erc--display-buffer-overriding-action
+                 display-buffer-overriding-action)))
+        (erc-setup-buffer buffer)
+        (run-hooks 'erc--setup-buffer-hook)))
 
     buffer))
 
@@ -2401,6 +2482,8 @@ erc-select-read-args
          env)
     (when erc-interactive-display
       (push `(erc-join-buffer . ,erc-interactive-display) env))
+    (when erc--display-context
+      (push `(erc--display-context . ,erc--display-context) env))
     (when opener
       (push `(erc-server-connect-function . ,opener) env))
     (when (and passwd (string= "" passwd))
@@ -2454,7 +2537,12 @@ erc
 See `erc-tls' for the meaning of ID.
 
 \(fn &key SERVER PORT NICK USER PASSWORD FULL-NAME ID)"
-  (interactive (erc-select-read-args))
+  (interactive (let ((erc--display-context `((erc-interactive-display . erc)
+                                             ,@erc--display-context)))
+                 (erc-select-read-args)))
+  (unless (assq 'erc--display-context --interactive-env--)
+    (push '(erc--display-context . ((erc-buffer-display . erc)))
+          --interactive-env--))
   (erc--with-entrypoint-environment --interactive-env--
     (erc-open server port nick full-name t password nil nil nil nil user id)))
 
@@ -2519,8 +2607,11 @@ erc-tls
 interactively.
 
 \(fn &key SERVER PORT NICK USER PASSWORD FULL-NAME CLIENT-CERTIFICATE ID)"
-  (interactive (let ((erc-default-port erc-default-port-tls))
-		 (erc-select-read-args)))
+  (interactive
+   (let ((erc-default-port erc-default-port-tls)
+         (erc--display-context `((erc-interactive-display . erc-tls)
+                                 ,@erc--display-context)))
+     (erc-select-read-args)))
   ;; Bind `erc-server-connect-function' to `erc-open-tls-stream'
   ;; around `erc-open' when a non-default value hasn't been specified
   ;; by the user or the interactive form.  And don't bother checking
@@ -2529,6 +2620,9 @@ erc-tls
               (not (eq erc-server-connect-function #'erc-open-network-stream)))
     (push '(erc-server-connect-function . erc-open-tls-stream)
           --interactive-env--))
+  (unless (assq 'erc--display-context --interactive-env--)
+    (push '(erc--display-context . ((erc-buffer-display . erc-tls)))
+          --interactive-env--))
   (erc--with-entrypoint-environment --interactive-env--
     (erc-open server port nick full-name t password
               nil nil nil client-certificate user id)))
@@ -3683,7 +3777,10 @@ erc-cmd-JOIN
                         (sn (erc-extract-nick (erc-response.sender parsed)))
                         ((erc-nick-equal-p sn (erc-current-nick)))
                         (erc-join-buffer (or erc-interactive-display
-                                             erc-join-buffer)))
+                                             erc-join-buffer))
+                        (erc--display-context `((erc-interactive-display
+                                                 . /JOIN)
+                                                ,@erc--display-context)))
                      (run-hook-with-args-until-success
                       'erc-server-JOIN-functions proc parsed)
                      t))))
@@ -4067,7 +4164,9 @@ erc-cmd-QUERY
       ;; currently broken, evil hack to display help anyway
                                         ;(erc-delete-query))))
     (signal 'wrong-number-of-arguments '(erc-cmd-QUERY 0)))
-  (let ((erc-join-buffer erc-interactive-display))
+  (let ((erc-join-buffer erc-interactive-display)
+        (erc--display-context `((erc-interactive-display . /QUERY)
+                                ,@erc--display-context)))
     (erc-with-server-buffer
      (erc--open-target user))))
 
@@ -4187,6 +4286,9 @@ 'erc-cmd-GQ
 
 (defun erc--cmd-reconnect ()
   (let ((buffer (erc-server-buffer))
+        (erc-join-buffer erc-interactive-display)
+        (erc--display-context `((erc-interactive-display . /RECONNECT)
+                                ,@erc--display-context))
         (process nil))
     (unless (buffer-live-p buffer)
       (setq buffer (current-buffer)))
@@ -4851,13 +4953,7 @@ erc-receive-query-display
   :package-version '(ERC . "5.6")
   :group 'erc-buffers
   :group 'erc-query
-  :type '(choice (const :tag "Defer to value of `erc-buffer-display'" nil)
-                 (const :tag "Split window and select" window)
-                 (const :tag "Split window, don't select" window-noselect)
-                 (const :tag "New frame" frame)
-                 (const :tag "Bury in new buffer" bury)
-                 (const :tag "Use current buffer" buffer)
-                 (const :tag "Use current buffer" t)))
+  :type erc--buffer-display-choices)
 
 (defvar erc-receive-query-display-defer t
   "How to interpret a null `erc-receive-query-display'.
@@ -5285,7 +5381,7 @@ erc-connection-established
         (setq erc--server-last-reconnect-count erc-server-reconnect-count
               erc-server-reconnect-count 0)
         (setq erc--server-reconnect-display-timer
-              (run-at-time erc-reconnect-display-timeout nil
+              (run-at-time erc-auto-reconnect-display-timeout nil
                            #'erc--server-last-reconnect-display-reset
                            (current-buffer)))
         (add-hook 'erc-disconnected-hook
@@ -7857,6 +7953,8 @@ erc-handle-irc-url
 Customize `erc-url-connect-function' to override this."
   (when (eql port 0) (setq port nil))
   (let* ((net (erc-networks--determine host))
+         (erc--display-context `((erc-interactive-display . url)
+                                 ,@erc--display-context))
          (server-buffer
           ;; Viable matches may slip through the cracks for unknown
           ;; networks.  Additional passes could likely improve things.
diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el
index 548ad00e2d9..df292a8c113 100644
--- a/test/lisp/erc/erc-scenarios-base-buffer-display.el
+++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el
@@ -26,8 +26,8 @@
 
 (eval-when-compile (require 'erc-join))
 
-;; These first couple `erc-reconnect-display' tests used to live in
-;; erc-scenarios-base-reconnect but have since been renamed.
+;; These first couple `erc-auto-reconnect-display' tests used to live
+;; in erc-scenarios-base-reconnect but have since been renamed.
 
 (defun erc-scenarios-base-buffer-display--reconnect-common
     (assert-server assert-chan assert-rest)
@@ -80,11 +80,11 @@ erc-scenarios-base-buffer-display--defwin-recbury-intbuf
   :tags '(:expensive-test)
   (should (eq erc-buffer-display 'bury))
   (should (eq erc-interactive-display 'window))
-  (should-not erc-reconnect-display)
+  (should-not erc-auto-reconnect-display)
 
   (let ((erc-buffer-display 'window)
         (erc-interactive-display 'buffer)
-        (erc-reconnect-display 'bury))
+        (erc-auto-reconnect-display 'bury))
 
     (erc-scenarios-base-buffer-display--reconnect-common
 
@@ -104,7 +104,7 @@ erc-scenarios-base-buffer-display--defwin-recbury-intbuf
        ;; A manual /JOIN command tells ERC we're done auto-reconnecting
        (with-current-buffer "FooNet" (erc-scenarios-common-say "/JOIN #spam"))
 
-       (ert-info ("#spam ignores `erc-reconnect-display'")
+       (ert-info ("#spam ignores `erc-auto-reconnect-display'")
          ;; Uses `erc-interactive-display' instead.
          (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
            (should (eq (window-buffer) (get-buffer "#spam")))
@@ -115,10 +115,10 @@ erc-scenarios-base-buffer-display--defwino-recbury-intbuf
   :tags '(:expensive-test)
   (should (eq erc-buffer-display 'bury))
   (should (eq erc-interactive-display 'window))
-  (should-not erc-reconnect-display)
+  (should-not erc-auto-reconnect-display)
 
   (let ((erc-buffer-display 'window-noselect)
-        (erc-reconnect-display 'bury)
+        (erc-auto-reconnect-display 'bury)
         (erc-interactive-display 'buffer))
     (erc-scenarios-base-buffer-display--reconnect-common
 
@@ -155,7 +155,7 @@ erc-scenarios-base-buffer-display--defwino-recbury-intbuf
            (should (eq (window-buffer) (get-buffer "bob")))
            (should (frame-root-window-p (selected-window)))))
 
-       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+       (ert-info ("Newly joined chan ignores `erc-auto-reconnect-display'")
          (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
            (should (eq (window-buffer) (get-buffer "bob")))
            (should-not (frame-root-window-p (selected-window)))
@@ -165,13 +165,13 @@ erc-scenarios-base-buffer-display--count-reset-timeout
   :tags '(:expensive-test)
   (should (eq erc-buffer-display 'bury))
   (should (eq erc-interactive-display 'window))
-  (should (eq erc-reconnect-display-timeout 10))
-  (should-not erc-reconnect-display)
+  (should (eq erc-auto-reconnect-display-timeout 10))
+  (should-not erc-auto-reconnect-display)
 
   (let ((erc-buffer-display 'window-noselect)
-        (erc-reconnect-display 'bury)
+        (erc-auto-reconnect-display 'bury)
         (erc-interactive-display 'buffer)
-        (erc-reconnect-display-timeout 0.5))
+        (erc-auto-reconnect-display-timeout 0.5))
     (erc-scenarios-base-buffer-display--reconnect-common
      #'ignore #'ignore ; These two are identical to the previous test.
 
@@ -188,10 +188,10 @@ erc-scenarios-base-buffer-display--count-reset-timeout
            (erc-d-t-wait-for 1 (null erc--server-reconnect-display-timer))
            (erc-cmd-JOIN "#spam")))
 
-       (ert-info ("Newly joined chan ignores `erc-reconnect-display'")
+       (ert-info ("Newly joined chan ignores `erc-auto-reconnect-display'")
          (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
            (should (eq (window-buffer) (messages-buffer)))
-           ;; If `erc-reconnect-display-timeout' were left alone, this
+           ;; If `erc-auto-reconnect-display-timeout' were left alone, this
            ;; would be (frame-root-window-p #<window 1 on *scratch*>).
            (should-not (frame-root-window-p (selected-window)))
            (should (eq (current-buffer) (window-buffer (next-window))))))))))
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 80c7c708fc5..4596dd9845e 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -418,8 +418,9 @@ erc--initialize-markers
       (should (looking-at-p (regexp-quote "*** Welcome"))))
 
     (ert-info ("Reconnect")
-      (erc-open "localhost" 6667 "tester" "Tester" nil
-                "fake" nil "#chan" proc nil "user" nil)
+      (with-current-buffer (erc-server-buffer)
+        (erc-open "localhost" 6667 "tester" "Tester" nil
+                  "fake" nil "#chan" proc nil "user" nil))
       (should-not (get-buffer "#chan<2>")))
 
     (ert-info ("Existing prompt respected")
@@ -503,6 +504,50 @@ erc--switch-to-buffer
     (dolist (b '("server" "other" "#chan" "#foo" "#fake"))
       (kill-buffer b))))
 
+(ert-deftest erc-setup-buffer--custom-action ()
+  (erc-mode)
+  (erc-tests--set-fake-server-process "sleep" "1")
+  (setq erc--server-last-reconnect-count 0)
+  (let ((owin (selected-window))
+        (obuf (window-buffer))
+        (mbuf (messages-buffer))
+        calls)
+    (cl-letf (((symbol-function 'switch-to-buffer) ; regression
+               (lambda (&rest r) (push (cons 'switch-to-buffer r) calls)))
+              ((symbol-function 'erc--test-fun)
+               (lambda (&rest r) (push (cons 'erc--test-fun r) calls)))
+              ((symbol-function 'display-buffer)
+               (lambda (&rest r) (push (cons 'display-buffer r) calls))))
+
+      ;; Baseline
+      (let ((erc-join-buffer 'bury))
+        (erc-setup-buffer mbuf)
+        (should-not calls))
+
+      (should-not erc--display-context)
+
+      ;; `display-buffer'
+      (let ((erc--display-context '((erc-buffer-display . 1)))
+            (erc-join-buffer 'erc--test-fun))
+        (erc-setup-buffer mbuf)
+        (should (equal `(erc--test-fun ,mbuf (nil (erc-buffer-display . 1)))
+                       (pop calls)))
+        (should-not calls))
+
+      ;; `pop-to-buffer' with `erc-auto-reconnect-display'
+      (let* ((erc--server-last-reconnect-count 1)
+             (erc--display-context '((erc-buffer-display . 1)))
+             (erc-auto-reconnect-display 'erc--test-fun))
+        (erc-setup-buffer mbuf)
+        (should (equal `(erc--test-fun ,mbuf
+                                       (nil (erc-auto-reconnect-display . t)
+                                            (erc-buffer-display . 1)))
+                       (pop calls)))
+        (should-not calls)))
+
+    (should (eq owin (selected-window)))
+    (should (eq obuf (window-buffer)))))
+
 (ert-deftest erc-lurker-maybe-trim ()
   (let (erc-lurker-trim-nicks
         (erc-lurker-ignore-chars "_`"))
@@ -1439,14 +1484,18 @@ erc-select-read-args
                            (erc-join-buffer . window))))))
 
   (ert-info ("Switches to TLS when URL is ircs://")
-    (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
-                     (erc-select-read-args))
-                   (list :server "irc.gnu.org"
-                         :port 6697
-                         :nick (user-login-name)
-                         '&interactive-env
-                         '((erc-server-connect-function . erc-open-tls-stream)
-                           (erc-join-buffer . window))))))
+    (let ((erc--display-context '((erc-interactive-display . erc))))
+      (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r"
+                       (erc-select-read-args))
+                     (list :server "irc.gnu.org"
+                           :port 6697
+                           :nick (user-login-name)
+                           '&interactive-env
+                           '((erc-server-connect-function
+                              . erc-open-tls-stream)
+                             (erc--display-context
+                              . ((erc-interactive-display . erc)))
+                             (erc-join-buffer . window)))))))
 
   (setq-local erc-interactive-display nil) ; cheat to save space
 
@@ -1526,6 +1575,7 @@ erc-tls
               ((symbol-function 'erc-open)
                (lambda (&rest r)
                  (push `((erc-join-buffer ,erc-join-buffer)
+                         (erc--display-context ,@erc--display-context)
                          (erc-server-connect-function
                           ,erc-server-connect-function))
                        env)
@@ -1538,6 +1588,7 @@ erc-tls
                          nil nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Full")
@@ -1554,6 +1605,7 @@ erc-tls
                          "bob:changeme" nil nil nil t "bobo" GNU.org)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       ;; Values are often nil when called by lisp code, which leads to
@@ -1573,6 +1625,7 @@ erc-tls
                              "bob:changeme" nil nil nil nil "bobo" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Interactive")
@@ -1583,6 +1636,8 @@ erc-tls
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer window)
+                         (erc--display-context
+                          (erc-interactive-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Custom connect function")
@@ -1593,6 +1648,8 @@ erc-tls
                            nil nil nil nil nil "user" nil)))
           (should (equal (pop env)
                          '((erc-join-buffer bury)
+                           (erc--display-context
+                            (erc-buffer-display . erc-tls))
                            (erc-server-connect-function my-connect-func))))))
 
       (ert-info ("Advised default function overlooked") ; intentional
@@ -1604,6 +1661,7 @@ erc-tls
                          nil nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer bury)
+                         (erc--display-context (erc-buffer-display . erc-tls))
                          (erc-server-connect-function erc-open-tls-stream))))
         (advice-remove 'erc-server-connect-function 'erc-tests--erc-tls))
 
@@ -1617,6 +1675,8 @@ erc-tls
                            '("irc.libera.chat" 6697 "tester" "unknown" t
                              nil nil nil nil nil "user" nil)))
             (should (equal (pop env) `((erc-join-buffer bury)
+                                       (erc--display-context
+                                        (erc-buffer-display . erc-tls))
                                        (erc-server-connect-function ,f))))
             (advice-remove 'erc-server-connect-function
                            'erc-tests--erc-tls)))))))
@@ -1631,6 +1691,7 @@ erc--interactive
               ((symbol-function 'erc-open)
                (lambda (&rest r)
                  (push `((erc-join-buffer ,erc-join-buffer)
+                         (erc--display-context ,@erc--display-context)
                          (erc-server-connect-function
                           ,erc-server-connect-function))
                        env)
@@ -1643,8 +1704,9 @@ erc--interactive
                        '("irc.libera.chat" 6697 "tester" "unknown" t nil
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
-                       '((erc-join-buffer window) (erc-server-connect-function
-                                                   erc-open-tls-stream)))))
+                       '((erc-join-buffer window)
+                         (erc--display-context (erc-interactive-display . erc))
+                         (erc-server-connect-function erc-open-tls-stream)))))
 
       (ert-info ("Nick supplied, decline TLS upgrade")
         (ert-simulate-keys "\r\rdummy\r\rn\r"
@@ -1654,6 +1716,7 @@ erc--interactive
                          nil nil nil nil "user" nil)))
         (should (equal (pop env)
                        '((erc-join-buffer window)
+                         (erc--display-context (erc-interactive-display . erc))
                          (erc-server-connect-function
                           erc-open-network-stream))))))))
 
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior
       [not found] ` <87r0pi32po.fsf@neverwas.me>
@ 2023-07-14  2:11   ` J.P.
  0 siblings, 0 replies; 15+ messages in thread
From: J.P. @ 2023-07-14  2:11 UTC (permalink / raw)
  To: 62833-done; +Cc: emacs-erc

"J.P." <jp@neverwas.me> writes:

> v4. Add new section to manual. Alias `erc-reconnect-display' to
> `erc-auto-reconnect-display'. Add new contexts for `autojoin' module and
> manual /RECONNECT invocations. Add option to force displaying of server
> buffers when reconnecting.

I've installed a version of this as

  https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=b354b3a5

Because of the sweeping scope of these changes, it's quite likely we'll
have to reopen this at some point.

Thanks and closing (for now).





^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2023-07-14  2:11 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <87leiuy3cv.fsf@neverwas.me>
2023-04-21 14:03 ` bug#62833: 30.0.50; ERC 5.6: Rethink buffer-display options and behavior J.P.
     [not found] ` <87354tcoyk.fsf@neverwas.me>
2023-04-24 14:34   ` J.P.
2023-05-08 22:26 ` J.P.
     [not found] ` <87jzxie9yf.fsf@neverwas.me>
2023-05-10 21:43   ` Corwin Brust
     [not found]   ` <CAJf-WoTk1vT3gVSHdO7MRs6Rfn4PRcs8UWM=mw_NbzeCGHDfvQ@mail.gmail.com>
2023-05-13 14:03     ` J.P.
     [not found]     ` <87sfc08h19.fsf@neverwas.me>
2023-06-02 14:06       ` J.P.
2023-05-16 14:37 ` Phillip Susi
2023-06-04 14:52 ` J.P.
     [not found] ` <877csje0uz.fsf@neverwas.me>
2023-06-04 15:28   ` Eli Zaretskii
     [not found]   ` <837csj5jsh.fsf@gnu.org>
2023-06-04 21:36     ` J.P.
2023-06-09 13:50 ` J.P.
2023-06-22 13:48   ` J.P.
2023-07-08 14:19 ` J.P.
     [not found] ` <87r0pi32po.fsf@neverwas.me>
2023-07-14  2:11   ` J.P.
2023-04-14 13:56 J.P.

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).