all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: "J.P." <jp@neverwas.me>
To: 56514@debbugs.gnu.org
Cc: Lars Ingebrigtsen <larsi@gnus.org>,
	emacs-erc@gnu.org, Stefan Kangas <stefankangas@gmail.com>
Subject: bug#56514: 29.0.50; Improve ERC's URI scheme integration for irc:// links
Date: Tue, 08 Nov 2022 06:09:13 -0800	[thread overview]
Message-ID: <87pmdxlera.fsf@neverwas.me> (raw)
In-Reply-To: <874jzkuqk3.fsf@neverwas.me> (J. P.'s message of "Thu, 14 Jul 2022 00:00:28 -0700")

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

v4.

- Dropped the request-tracking POC stuff because those patches only
  benefit the Org integration, which needs special attention anyway.

- Added unifying changes to url-irc and browse-url that treat ERC and
  rcirc equally.


Questions (for anyone):

  1. I added a couple autoloads in lisp/url/url-irc.el to avoid having
     to create a url-ircs.el (or even a url-irc6{,s}.el). Is there a
     better alternate means of getting `url-scheme-get-property' to
     discover handlers that doesn't rely on autoloads?

  2. In the function `url-irc', I bind `url-current-object' around the
     call to `url-irc-function' to avoid adding another parameter to the
     latter's interface (which mainly benefits ERC). Any obvious problem
     with borrowing `url-current-object' for this purpose?

  3. In browse-url, I basically ignore what looks like the favored
     practice for adding handlers, namely, registering an internal
     function with `browse-url-default-handlers' that calls a public
     function assigned to a user option. An example of this pattern is:

       internal: browse-url--mailto
       option:   browse-url-mailto-function
       public:   browse-url-mail

     The reason for sidestepping the intervening indirection and adding
     a public function directly to `browse-url-default-handlers' is that
     I figure users wishing to override this can already do so via
     `browse-url-handlers'. Is that misguided somehow?

  4. Are any of these non-ERC changes newsworthy enough for etc/NEWS?

Thanks!


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

From 3658e89614cbe3b5b27f09271b7bc738a1c7ec38 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 7 Nov 2022 05:13:59 -0800
Subject: [PATCH 0/6] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (6):
  Teach thing-at-point to recognize bracketed IPv6 URLs
  Accommodate ircs:// URLs in url-irc and browse-url
  Refactor erc-select-read-args
  Default to TLS port when calling erc-tls from lisp
  Add optional server param to erc-networks--determine
  Improve new connections in erc-handle-irc-url

 doc/misc/erc.texi                   |  39 +++++
 lisp/erc/erc-backend.el             |   6 +
 lisp/erc/erc-compat.el              |  15 ++
 lisp/erc/erc-networks.el            |   9 +-
 lisp/erc/erc.el                     | 224 +++++++++++++++++++--------
 lisp/net/browse-url.el              |  11 ++
 lisp/thingatpt.el                   |   2 +-
 lisp/url/url-irc.el                 |  21 ++-
 test/lisp/erc/erc-networks-tests.el |  17 +++
 test/lisp/erc/erc-tests.el          | 225 ++++++++++++++++++++++++++++
 test/lisp/net/browse-url-tests.el   |   9 ++
 test/lisp/thingatpt-tests.el        |   3 +
 12 files changed, 510 insertions(+), 71 deletions(-)

Interdiff:
diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index 3db83197f9..d01eab1bbb 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -79,6 +79,7 @@ Top
 
 * Connecting::                  Ways of connecting to an IRC server.
 * Sample Configuration::        An example configuration file.
+* Integrations::                Integrations available for ERC.
 * Options::                     Options that are available for ERC.
 
 @end detailmenu
@@ -526,6 +527,7 @@ Advanced Usage
 @menu
 * Connecting::                  Ways of connecting to an IRC server.
 * Sample Configuration::        An example configuration file.
+* Integrations::                Integrations available for ERC.
 * Options::                     Options that are available for ERC.
 @end menu
 
@@ -990,6 +992,43 @@ Sample Configuration
 ;; (setq erc-kill-server-buffer-on-quit t)
 @end lisp
 
+@node Integrations
+@section Integrations
+@cindex integrations
+
+@subheading URL
+For anything to work, you'll want to set @code{url-irc-function} to
+@code{url-irc-erc}.  As a rule of thumb, libraries that rely directly
+on @code{url-retrieve} should be good to go out the box from Emacs
+29.1 onward.  On older versions of Emacs, you may need to
+@code{(require 'erc)} beforehand. @pxref{Retrieving URLs,,, url, URL}.
+
+For other apps and libraries, such as those relying on the
+higher-level @code{browse-url}, you'll oftentimes be asked to specify
+a pattern, sometimes paired with a function that accepts a string URL
+as a first argument.  For example, with EWW, you may need to tack
+something like @code{"\\|\\`irc6?s?:"} onto the end of
+@code{eww-use-browse-url}.  But with @code{gnus-button-alist}, you'll
+need a function as well:
+
+@lisp
+  '("\\birc6?s?://[][a-z0-9.,@@_:+%?&/#-]+"
+    0 t erc-browse-url-handler 0)
+@end lisp
+
+@defun erc-browse-url-handler url &rest args
+An autoloaded convenience function for use in options like those
+mentioned above.  @var{url} must be a string.  In Emacs 29 and above,
+the function @code{browse-url-irc} can be used instead.
+@end defun
+
+@noindent
+Keep in mind that when fiddling with these options, it may be easier
+(and more polite) to connect to a local server or a test network, like
+@samp{ircs://testnet.ergo.chat/#test}, since these generally don't
+require authentication.
+
+
 @node Options
 @section Options
 @cindex options
diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el
index 03bd8f1352..683d19dfc7 100644
--- a/lisp/erc/erc-compat.el
+++ b/lisp/erc/erc-compat.el
@@ -168,6 +168,21 @@ erc-compat--with-memoization
     `(cl--generic-with-memoization ,table ,@forms))
    (t `(progn ,@forms))))
 
+(declare-function browse-url-irc "browse-url" (url &rest _))
+
+(defun erc-compat--browse-url-irc (string &rest _)
+  "Parse STRING and call `url-irc'."
+  (require 'url-irc)
+  (if (< emacs-major-version 29)
+      ;; `url-irc' binds this in Emacs 29+.
+      (let ((url-current-object (url-generic-parse-url string)))
+        (url-irc url-current-object))
+    (browse-url-irc string)))
+
+(when (< emacs-major-version 29)
+  (add-to-list 'browse-url-default-handlers
+               '("\\`irc6?s?://" . erc-compat--browse-url-irc)))
+
 (provide 'erc-compat)
 
 ;;; erc-compat.el ends here
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index db2029580a..3c9293e28a 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1534,6 +1534,15 @@ erc-reuse-buffers
 (make-obsolete-variable 'erc-reuse-buffers
                         "old behavior when t now permanent" "29.1")
 
+(defcustom erc-legacy-port-names 'legacy
+  "Interpret \"irc\" and \"ircs\" using IANA service mappings.
+When non-nil, this yields 194 and 994 instead of 6667 and 6697.
+When set to `legacy', it also emits a warning saying that the
+default will change to nil in the future."
+  :group 'erc
+  :package-version '(ERC . "5.4.1") ; FIXME increment on ELPA release
+  :type '(choice (const nil) (const legacy) (const t)))
+
 (defun erc-normalize-port (port)
   "Normalize the port specification PORT to integer form.
 PORT may be an integer, a string or a symbol.  If it is a string or a
@@ -1557,8 +1566,14 @@ erc-normalize-port
         port-nr)
        ((string-equal port "ircu") 6667)
        ((string-equal port "ircs-u") 6697)
-       ((string-equal port "irc")
-        194)
+       ((member port '("irc" "ircs"))
+        (when (eq erc-legacy-port-names 'legacy)
+          (lwarn 'ERC 'warning
+                 (concat "`erc-legacy-port-names' will default to nil "
+                         "in a future version of ERC.")))
+        (if (string= port "irc")
+            (if erc-legacy-port-names 194 6667)
+          (if erc-legacy-port-names 994 6697)))
        ((string-equal port "ircs")
         994)
        ((string-equal port "ircd")
@@ -2119,29 +2134,33 @@ erc--ensure-url
 (defun erc-select-read-args ()
   "Prompt the user for values of nick, server, port, and password."
   (require 'url-parse)
-  (let ((input (read-string "IRC server: "
-                            (erc-compute-server)
-                            'erc-server-history-list))
-        server port nick passwd)
-    ;; For legacy reasons, also accept a URL without a scheme.
-    (let* ((url (url-generic-parse-url (erc--ensure-url input)))
-           (sp (and (string-suffix-p "s" (url-type url)) 'ircs-u)))
-      (setq server (url-host url)
-            port (or (url-portspec url)
-                     (erc-string-to-port
-                      (read-string "IRC port: " (erc-port-to-string
-                                                 (erc-compute-port sp)))))
-            nick (or (url-user url)
-                     (if (erc-already-logged-in server port nick)
-                         (read-string (erc-format-message 'nick-in-use ?n nick)
-                                      nick 'erc-nick-history-list)
-                       (read-string "Nickname: " (erc-compute-nick nick)
-                                    'erc-nick-history-list)))
-            passwd (or (url-password url)
-                       (if erc-prompt-for-password
-                           (read-passwd "Server password: ")
-                         (with-suppressed-warnings ((obsolete erc-password))
-                           erc-password)))))
+  (let* ((input (let ((d (erc-compute-server)))
+                  (read-string (format "Server (default is %S): " d)
+                               nil 'erc-server-history-list d)))
+         ;; For legacy reasons, also accept a URL without a scheme.
+         (url (url-generic-parse-url (erc--ensure-url input)))
+         (server (url-host url))
+         (sp (and (or (string-suffix-p "s" (url-type url))
+                      (and (equal server erc-default-server)
+                           (not (string-prefix-p "irc://" input))))
+                  'ircs-u))
+         (port (or (url-portspec url)
+                   (erc-compute-port
+                    (let ((d (erc-compute-port sp))) ; may be a string
+                      (read-string (format "Port (default is %s): " d)
+                                   nil nil d)))))
+         ;; Trust the user not to connect twice accidentally.  We
+         ;; can't use `erc-already-logged-in' to check for an existing
+         ;; connection without modifying it to consider USER and PASS.
+         (nick (or (url-user url)
+                   (let ((d (erc-compute-nick)))
+                     (read-string (format "Nickname (default is %S): " d)
+                                  nil 'erc-nick-history-list d))))
+         (passwd (or (url-password url)
+                     (if erc-prompt-for-password
+                         (read-passwd "Server password (optional): ")
+                       (with-suppressed-warnings ((obsolete erc-password))
+                         erc-password)))))
     (when (and passwd (string= "" passwd))
       (setq passwd nil))
     (list :server server :port port :nick nick :password passwd)))
@@ -6395,10 +6414,7 @@ erc-compute-port
 - PORT (the argument passed to this function)
 - The `erc-port' option
 - The `erc-default-port' variable"
-  (cond ((numberp port) port)
-	(erc-port (erc-normalize-port erc-port))
-	(port (erc-normalize-port port))
-	(t erc-default-port)))
+  (erc-normalize-port (or port erc-port erc-default-port)))
 
 ;; time routines
 
@@ -7168,21 +7184,47 @@ erc-get-parsed-vector-type
 ;; Teach url.el how to open irc:// URLs with ERC.
 ;; To activate, customize `url-irc-function' to `url-irc-erc'.
 
-;; FIXME update comment above once the URL business is fully settled.
-;; Also: the function `url-retrieve-internal' finds a "loader" by
-;; looking for a library providing a feature named "url-<scheme>", but
-;; no such file currently exists for "ircs".
+(defcustom erc-url-connect-function nil
+  "When non-nil, a function used to connect to an IRC URL.
+Called with any number of keyword arguments recognized by `erc'
+and `erc-tls'.  The variable `url-current-object', if non-nil,
+can be used to help determine whether to connect using TLS."
+  :group 'erc
+  :package-version '(ERC . "5.4.1") ; FIXME increment on release
+  :type '(choice (const nil) function))
+
+(defun erc--url-default-connect-function (&rest plist)
+  (let* ((scheme (and url-current-object (url-type url-current-object)))
+         (ircsp (if scheme
+                    (string-suffix-p "s" scheme)
+                  (or (eql 6697 (plist-get plist :port))
+                      (yes-or-no-p "Connect using TLS? "))))
+         (erc-server (plist-get plist :server))
+         (erc-port (or (plist-get plist :port)
+                       (and ircsp (erc-normalize-port 'ircs-u))
+                       erc-port))
+         (erc-nick (or (plist-get plist :nick) erc-nick))
+         (erc-password (plist-get plist :password))
+         (args (erc-select-read-args)))
+    (unless ircsp
+      (setq ircsp (eql 6697 erc-port)))
+    (apply (if ircsp #'erc-tls #'erc) args)))
+
+;; The current spec, unlike the 2003 Butcher draft, doesn't explicitly
+;; allow for an auth[:password]@ component (or trailing ,flags or
+;; &options).
+;;
+;; https://www.iana.org/assignments/uri-schemes
+;; https://datatracker.ietf.org/doc/html/draft-butcher-irc-url#section-6
 
 ;;;###autoload
-(defun erc-handle-irc-url (host port channel nick password
-                                &optional connect-fn)
+(defun erc-handle-irc-url (host port channel nick password)
   "Use ERC to IRC on HOST:PORT in CHANNEL.
 If ERC is already connected to HOST:PORT, simply /join CHANNEL.
 Otherwise, connect to HOST:PORT as NICK and /join CHANNEL.
 
-Note that ERC no longer attempts to establish new connections
-without human intervention, although opting in may eventually be
-allowed."
+Beginning with ERC 5.5, new connections require human intervention.
+Customize `erc-url-connect-function' to override this."
   (when (eql port 0) (setq port nil))
   (let* ((net (erc-networks--determine host))
          (server-buffer
@@ -7202,10 +7244,10 @@ erc-handle-irc-url
                                        port)))))))))
          key deferred)
     (unless server-buffer
-      (unless connect-fn
-        (user-error "Existing session for %s not found." host))
       (setq deferred t
-            server-buffer (apply connect-fn :server host
+            server-buffer (apply (or erc-url-connect-function
+                                     #'erc--url-default-connect-function)
+                                 :server host
                                  `(,@(and port (list :port port))
                                    ,@(and nick (list :nick nick))
                                    ,@(and password `(:password ,password))))))
@@ -7227,75 +7269,13 @@ erc-handle-irc-url
         (with-current-buffer server-buffer
           (erc-cmd-JOIN channel key))))))
 
-;; XXX ERASE ME (possibly use as basis for new section in info doc)
-;;
-;; For now, as a demo, users must require erc and do something like:
-;;
-;;   (add-to-list 'browse-url-default-handlers
-;;                '("\\`irc6?s?://" . erc--handle-ircs-url))
-;;
-;; Libraries that optionally depend on browse-url, like eww, etc. need
-;; an extra hand as well:
-;;
-;;   (setq eww-use-browse-url
-;;         (concat eww-use-browse-url "\\|\\`irc6?s?:"))
-;;
-;; Those that don't use browse-url get the same handler:
-;;
-;;   (add-to-list 'gnus-button-alist
-;;                '("\\birc6?s?://[][a-z0-9.,@_:+%?&/#-]+"
-;;                  0 t erc--handle-ircs-url 0))
-;;
-;; Finally, insert something like "ircs://testnet.ergo.chat/#test"
-;; where appropriate and perform a suitable action.
-
-;; The two variables below are contenders for exporting as user
-;; options.  The rationale for separate functions here instead of,
-;; say, a single option granting ERC permission to connect
-;; automatically is that ERC lacks a concept of configured server
-;; profiles and thus has no idea what values to give for connection
-;; parameters, like nick, user, etc.
-;;
-;; Also, the current spec was simplified from the 2003 Butcher draft
-;; and doesn't explicitly allow for an auth[:password]@ component (or
-;; trailing ,flags or &options, for that matter).  Regardless, even
-;; when provided, we shouldn't just connect and risk exposing
-;; whatever's returned by `user-login-name', right?
-;;
-;; https://www.iana.org/assignments/uri-schemes
-;; https://datatracker.ietf.org/doc/html/draft-butcher-irc-url#section-6
-
-(defvar erc--url-irc-connect-function nil)
-(defvar erc--url-ircs-connect-function nil)
-
-(defun erc--url-default-connect-function (ircs &rest plist)
-  (let ((erc-server (plist-get plist :server))
-        (erc-port (or (plist-get plist :port)
-                      (and ircs (erc-normalize-port 'ircs-u))
-                      erc-port))
-        (erc-nick (or (plist-get plist :nick) erc-nick)))
-    (call-interactively (if ircs #'erc-tls #'erc))))
-
 (defvar url-irc-function)
 
-;; FIXME rename this and autoload it
-(defun erc--handle-ircs-url (&optional url &rest _)
-  (unless url
-    (setq url (pop command-line-args-left))
-    (cl-assert url))
-  (require 'url-parse)
-  (unless (url-p url)
-    (setq url (url-generic-parse-url url)))
-  (let* ((ircsp (string-suffix-p "s" (url-type url)))
-         (fn (or (if ircsp
-                     erc--url-ircs-connect-function
-                   erc--url-irc-connect-function)
-                 (apply-partially #'erc--url-default-connect-function ircsp)))
-         (url-irc-function (lambda (&rest r)
-                             (apply #'erc-handle-irc-url `(,@r ,fn)))))
-    ;; Amazingly, this does TRT for /&chan, /#chan, /##chan, /#&chan
-    (url-irc url)))
-
+;;;###autoload
+(defun erc-browse-url-handler (url &rest _)
+  "Launch an ERC session when given an irc:// URL."
+  (let ((url-irc-function 'url-irc-erc))
+    (erc-compat--browse-url-irc url)))
 
 (provide 'erc)
 
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 1597f3651a..8d95c0667b 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -565,6 +565,7 @@ browse-url--non-html-file-url-p
 (defvar browse-url-default-handlers
   '(("\\`mailto:" . browse-url--mailto)
     ("\\`man:" . browse-url--man)
+    ("\\`irc6?s?://" . browse-url-irc)
     (browse-url--non-html-file-url-p . browse-url-emacs))
   "Like `browse-url-handlers' but populated by Emacs and packages.
 
@@ -1510,6 +1511,16 @@ browse-url-text-emacs
 
 (function-put 'browse-url-text-emacs 'browse-url-browser-kind 'internal)
 
+;; --- irc ---
+
+;;;###autoload
+(defun browse-url-irc (url &rest _)
+  "Call `url-irc' directly after parsing URL.
+This function is a fit for options like `gnus-button-alist'."
+  (url-irc (url-generic-parse-url url)))
+
+(function-put 'browse-url-irc 'browse-url-browser-kind 'internal)
+
 ;; --- mailto ---
 
 (autoload 'rfc6068-parse-mailto-url "rfc6068")
diff --git a/lisp/url/url-irc.el b/lisp/url/url-irc.el
index 9161f7d13e..0dd25b7f49 100644
--- a/lisp/url/url-irc.el
+++ b/lisp/url/url-irc.el
@@ -38,7 +38,12 @@ url-irc-function
     PORT - the port number of the IRC server to contact
  CHANNEL - What channel on the server to visit right away (can be nil)
     USER - What username to use
-PASSWORD - What password to use"
+PASSWORD - What password to use.
+
+The variable `url-current-object' is bound to the parsed `url'
+struct, but its members may not match the positional args above,
+which should take precedence.  For example, `:portspec' may be
+nil while PORT is 6667."
   :type '(choice (const :tag "rcirc" :value url-irc-rcirc)
 		 (const :tag "ERC" :value url-irc-erc)
 		 (const :tag "ZEN IRC" :value url-irc-zenirc)
@@ -80,7 +85,8 @@ url-irc
 	 (port (url-port url))
 	 (pass (url-password url))
 	 (user (url-user url))
-	 (chan (url-filename url)))
+         (chan (url-filename url))
+         (url-current-object url))
     (if (url-target url)
 	(setq chan (concat chan "#" (url-target url))))
     (if (string-match "^/" chan)
@@ -90,6 +96,17 @@ url-irc
     (funcall url-irc-function host port chan user pass)
     nil))
 
+;;;; ircs://
+
+;; The function `url-scheme-get-property' tries and fails to load the
+;; nonexistent url-ircs.el but falls back to using the following:
+
+;;;###autoload
+(defconst url-ircs-default-port 6697 "Default port for IRCS connections.")
+
+;;;###autoload
+(defalias 'url-ircs 'url-irc)
+
 (provide 'url-irc)
 
 ;;; url-irc.el ends here
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 3ca36c0abb..e097090e5d 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -976,26 +976,25 @@ erc--server-connect-dumb-ipv6-regexp
 
 (ert-deftest erc-select-read-args ()
 
-  (ert-info ("Default")
+  (ert-info ("Defaults to TLS")
     (should (equal (ert-simulate-keys "\r\r\r\r"
                      (erc-select-read-args))
                    (list :server "irc.libera.chat"
-                         :port 6667
+                         :port 6697
                          :nick (user-login-name)
                          :password nil))))
 
-  (ert-info ("Default TSL")
-    (should (equal (ert-simulate-keys "\r\r\r\r"
-                     (let ((erc-default-port erc-default-port-tls))
-                       (erc-select-read-args)))
+  (ert-info ("Override default TLS")
+    (should (equal (ert-simulate-keys "irc://irc.libera.chat\r\r\r\r"
+                     (erc-select-read-args))
                    (list :server "irc.libera.chat"
-                         :port 6697
+                         :port 6667
                          :nick (user-login-name)
                          :password nil))))
 
   (ert-info ("Address includes port")
     (should (equal (ert-simulate-keys
-                       "\C-a\C-klocalhost:6667\r\C-a\C-knick\r\r"
+                       "localhost:6667\rnick\r\r"
                      (erc-select-read-args))
                    (list :server "localhost"
                          :port 6667
@@ -1003,7 +1002,7 @@ erc-select-read-args
                          :password nil))))
 
   (ert-info ("Address includes nick, password skipped via option")
-    (should (equal (ert-simulate-keys "\C-a\C-knick@localhost:6667\r"
+    (should (equal (ert-simulate-keys "nick@localhost:6667\r"
                      (let (erc-prompt-for-password)
                        (erc-select-read-args)))
                    (list :server "localhost"
@@ -1012,7 +1011,7 @@ erc-select-read-args
                          :password nil))))
 
   (ert-info ("Addresss includes nick and password")
-    (should (equal (ert-simulate-keys "\C-a\C-knick:sesame@localhost:6667\r"
+    (should (equal (ert-simulate-keys "nick:sesame@localhost:6667\r"
                      (erc-select-read-args))
                    (list :server "localhost"
                          :port 6667
@@ -1020,7 +1019,7 @@ erc-select-read-args
                          :password "sesame"))))
 
   (ert-info ("IPv6 address plain")
-    (should (equal (ert-simulate-keys "\C-a\C-k::1\r\r\r\r"
+    (should (equal (ert-simulate-keys "::1\r\r\r\r"
                      (erc-select-read-args))
                    (list :server "[::1]"
                          :port 6667
@@ -1028,7 +1027,7 @@ erc-select-read-args
                          :password nil))))
 
   (ert-info ("IPv6 address with port")
-    (should (equal (ert-simulate-keys "\C-a\C-k[::1]:6667\r\r\r"
+    (should (equal (ert-simulate-keys "[::1]:6667\r\r\r"
                      (erc-select-read-args))
                    (list :server "[::1]"
                          :port 6667
@@ -1036,7 +1035,7 @@ erc-select-read-args
                          :password nil))))
 
   (ert-info ("IPv6 address includes nick")
-    (should (equal (ert-simulate-keys "\C-a\C-knick@[::1]:6667\r\r"
+    (should (equal (ert-simulate-keys "nick@[::1]:6667\r\r"
                      (erc-select-read-args))
                    (list :server "[::1]"
                          :port 6667
@@ -1109,14 +1108,14 @@ erc-tests--make-client-buf
     (current-buffer)))
 
 (ert-deftest erc-handle-irc-url ()
-  (should-error (erc-handle-irc-url "irc.gnu.org" 6667 nil nil nil))
   (let* (calls
          rvbuf
          erc-networks-alist
          erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook
-         (connect (lambda (&rest r)
-                    (push r calls)
-                    (if (functionp rvbuf) (funcall rvbuf) rvbuf))))
+         (erc-url-connect-function
+          (lambda (&rest r)
+            (push r calls)
+            (if (functionp rvbuf) (funcall rvbuf) rvbuf))))
 
     (cl-letf (((symbol-function 'erc-cmd-JOIN)
                (lambda (&rest r) (push r calls))))
@@ -1127,34 +1126,34 @@ erc-handle-irc-url
       (erc-tests--make-server-buf "baznet")
 
       (ert-info ("Unknown network")
-        (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil connect)
+        (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil)
         (should (equal '("#chan" nil) (pop calls)))
         (should-not calls))
 
       (ert-info ("Unknown network, no port")
-        (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil connect)
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil)
         (should (equal '("#chan" nil) (pop calls)))
         (should-not calls))
 
       (ert-info ("Known network, no port")
         (setq erc-networks-alist '((foonet "irc.foonet.org")))
-        (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil connect)
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil)
         (should (equal '("#chan" nil) (pop calls)))
         (should-not calls))
 
       (ert-info ("Known network, different port")
-        (erc-handle-irc-url "irc.foonet.org" 6697 "#chan" nil nil connect)
+        (erc-handle-irc-url "irc.foonet.org" 6697 "#chan" nil nil)
         (should (equal '("#chan" nil) (pop calls)))
         (should-not calls))
 
       (ert-info ("Known network, existing chan with key")
         (erc-tests--make-client-buf "foonet" "#chan")
-        (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil connect)
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil)
         (should (equal '("#chan" "sec") (pop calls)))
         (should-not calls))
 
       (ert-info ("Unknown network, connect, no chan")
-        (erc-handle-irc-url "irc.gnu.org" nil nil nil nil connect)
+        (erc-handle-irc-url "irc.gnu.org" nil nil nil nil)
         (should (equal '(:server "irc.gnu.org") (pop calls)))
         (should-not calls))
 
@@ -1162,7 +1161,7 @@ erc-handle-irc-url
         (with-current-buffer "foonet"
           (should-not (local-variable-p 'erc-after-connect)))
         (setq rvbuf (lambda () (erc-tests--make-server-buf "gnu")))
-        (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil connect)
+        (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil)
         (should (equal '(:server "irc.gnu.org") (pop calls)))
         (should-not calls)
         (with-current-buffer "gnu"
diff --git a/test/lisp/net/browse-url-tests.el b/test/lisp/net/browse-url-tests.el
index 1c993958b8..cf917802e0 100644
--- a/test/lisp/net/browse-url-tests.el
+++ b/test/lisp/net/browse-url-tests.el
@@ -56,6 +56,15 @@ browse-url-tests-select-handler-man
               'browse-url--man))
   (should-not (browse-url-select-handler "man:ls" 'external)))
 
+(ert-deftest browse-url-tests-select-handler-irc ()
+  (should (eq (browse-url-select-handler "irc://localhost" 'internal)
+              'browse-url-irc))
+  (should-not (browse-url-select-handler "irc://localhost" 'external))
+  (should (eq (browse-url-select-handler "irc6://localhost")
+              'browse-url-irc))
+  (should (eq (browse-url-select-handler "ircs://tester@irc.gnu.org/#chan")
+              'browse-url-irc)))
+
 (ert-deftest browse-url-tests-select-handler-file ()
   (should (eq (browse-url-select-handler "file://foo.txt")
               'browse-url-emacs))
-- 
2.38.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-Teach-thing-at-point-to-recognize-bracketed-IPv6-URL.patch --]
[-- Type: text/x-patch, Size: 2087 bytes --]

From 0d191d30b15ea2d5b6042f51c6cf421b82feb7e5 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Wed, 13 Jul 2022 01:54:19 -0700
Subject: [PATCH 1/6] Teach thing-at-point to recognize bracketed IPv6 URLs

* lisp/thingatpt.el (thing-at-point-bounds-of-url-at-point): Allow
IPv6 addresses as hosts.  Overshoots in the case of bracketed markup
but is rescued by `thing-at-point--bounds-of-well-formed-url'.

* test/lisp/thingatpt-tests.el (thing-at-point-test-data): Add cases
for IPv6 URLs.
---
 lisp/thingatpt.el            | 2 +-
 test/lisp/thingatpt-tests.el | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el
index 462f87d3c1..9dda3e1fcb 100644
--- a/lisp/thingatpt.el
+++ b/lisp/thingatpt.el
@@ -441,7 +441,7 @@ thing-at-point-bounds-of-url-at-point
       ;; Otherwise, find the bounds within which a URI may exist.  The
       ;; method is similar to `ffap-string-at-point'.  Note that URIs
       ;; may contain parentheses but may not contain spaces (RFC3986).
-      (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'")
+      (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'[]")
 	     (skip-before "^[0-9a-zA-Z]")
 	     (skip-after  ":;.,!?'")
 	     (pt (point))
diff --git a/test/lisp/thingatpt-tests.el b/test/lisp/thingatpt-tests.el
index b6d0b1446a..67dd00104b 100644
--- a/test/lisp/thingatpt-tests.el
+++ b/test/lisp/thingatpt-tests.el
@@ -44,6 +44,9 @@ thing-at-point-test-data
     ;; Non alphanumeric characters can be found in URIs
     ("ftp://example.net/~foo!;#bar=baz&goo=bob" 3 url "ftp://example.net/~foo!;#bar=baz&goo=bob")
     ("bzr+ssh://user@example.net:5/a%20d,5" 34 url "bzr+ssh://user@example.net:5/a%20d,5")
+    ;; IPv6 brackets enclosed in [markup]
+    ("[http://[::1]:8000/foo]" 10 url "http://[::1]:8000/foo")
+    ("[http://[fe08::7:8%eth0]]" 10 url "http://[fe08::7:8%eth0]")
     ;; <url:...> markup
     ("Url: <url:foo://1.example.com>..." 8 url "foo://1.example.com")
     ("Url: <url:foo://2.example.com>..." 30 url "foo://2.example.com")
-- 
2.38.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0002-Accommodate-ircs-URLs-in-url-irc-and-browse-url.patch --]
[-- Type: text/x-patch, Size: 4533 bytes --]

From 6fd2f75707f123abfbcfae2d4f2837efed5b7adc Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 11 Jul 2022 05:14:57 -0700
Subject: [PATCH 2/6] Accommodate ircs:// URLs in url-irc and browse-url

* lisp/url/url-irc.el: (url-irc-erc, url-irc): Add necessary
ingredients for `url-scheme-get-property' to recognize ircs:// URLs.
Bind `url-current-object' around calls to `url-irc-function'.
(url-ircs-default-port, url-ircs): Add new autoloaded constant and
alias for `url-scheme-get-property' to recognize.  Do this to avoid
having to add another file.
* lisp/net/browse-url.el (browse-url-default-handlers): Add "irc://"
entry.
(browse-url-irc): Add new function to serve as default hander for
"irc://" URLS.
* test/lisp/net/browse-url-tests.el
(browse-url-tests-select-handler-irc): Add test for "irc://" URL
pattern.
---
 lisp/net/browse-url.el            | 11 +++++++++++
 lisp/url/url-irc.el               | 21 +++++++++++++++++++--
 test/lisp/net/browse-url-tests.el |  9 +++++++++
 3 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 1597f3651a..8d95c0667b 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -565,6 +565,7 @@ browse-url--non-html-file-url-p
 (defvar browse-url-default-handlers
   '(("\\`mailto:" . browse-url--mailto)
     ("\\`man:" . browse-url--man)
+    ("\\`irc6?s?://" . browse-url-irc)
     (browse-url--non-html-file-url-p . browse-url-emacs))
   "Like `browse-url-handlers' but populated by Emacs and packages.
 
@@ -1510,6 +1511,16 @@ browse-url-text-emacs
 
 (function-put 'browse-url-text-emacs 'browse-url-browser-kind 'internal)
 
+;; --- irc ---
+
+;;;###autoload
+(defun browse-url-irc (url &rest _)
+  "Call `url-irc' directly after parsing URL.
+This function is a fit for options like `gnus-button-alist'."
+  (url-irc (url-generic-parse-url url)))
+
+(function-put 'browse-url-irc 'browse-url-browser-kind 'internal)
+
 ;; --- mailto ---
 
 (autoload 'rfc6068-parse-mailto-url "rfc6068")
diff --git a/lisp/url/url-irc.el b/lisp/url/url-irc.el
index 9161f7d13e..0dd25b7f49 100644
--- a/lisp/url/url-irc.el
+++ b/lisp/url/url-irc.el
@@ -38,7 +38,12 @@ url-irc-function
     PORT - the port number of the IRC server to contact
  CHANNEL - What channel on the server to visit right away (can be nil)
     USER - What username to use
-PASSWORD - What password to use"
+PASSWORD - What password to use.
+
+The variable `url-current-object' is bound to the parsed `url'
+struct, but its members may not match the positional args above,
+which should take precedence.  For example, `:portspec' may be
+nil while PORT is 6667."
   :type '(choice (const :tag "rcirc" :value url-irc-rcirc)
 		 (const :tag "ERC" :value url-irc-erc)
 		 (const :tag "ZEN IRC" :value url-irc-zenirc)
@@ -80,7 +85,8 @@ url-irc
 	 (port (url-port url))
 	 (pass (url-password url))
 	 (user (url-user url))
-	 (chan (url-filename url)))
+         (chan (url-filename url))
+         (url-current-object url))
     (if (url-target url)
 	(setq chan (concat chan "#" (url-target url))))
     (if (string-match "^/" chan)
@@ -90,6 +96,17 @@ url-irc
     (funcall url-irc-function host port chan user pass)
     nil))
 
+;;;; ircs://
+
+;; The function `url-scheme-get-property' tries and fails to load the
+;; nonexistent url-ircs.el but falls back to using the following:
+
+;;;###autoload
+(defconst url-ircs-default-port 6697 "Default port for IRCS connections.")
+
+;;;###autoload
+(defalias 'url-ircs 'url-irc)
+
 (provide 'url-irc)
 
 ;;; url-irc.el ends here
diff --git a/test/lisp/net/browse-url-tests.el b/test/lisp/net/browse-url-tests.el
index 1c993958b8..cf917802e0 100644
--- a/test/lisp/net/browse-url-tests.el
+++ b/test/lisp/net/browse-url-tests.el
@@ -56,6 +56,15 @@ browse-url-tests-select-handler-man
               'browse-url--man))
   (should-not (browse-url-select-handler "man:ls" 'external)))
 
+(ert-deftest browse-url-tests-select-handler-irc ()
+  (should (eq (browse-url-select-handler "irc://localhost" 'internal)
+              'browse-url-irc))
+  (should-not (browse-url-select-handler "irc://localhost" 'external))
+  (should (eq (browse-url-select-handler "irc6://localhost")
+              'browse-url-irc))
+  (should (eq (browse-url-select-handler "ircs://tester@irc.gnu.org/#chan")
+              'browse-url-irc)))
+
 (ert-deftest browse-url-tests-select-handler-file ()
   (should (eq (browse-url-select-handler "file://foo.txt")
               'browse-url-emacs))
-- 
2.38.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: 0003-Refactor-erc-select-read-args.patch --]
[-- Type: text/x-patch, Size: 11086 bytes --]

From e64b845f097590889109b6033f42cb5d68abf0b9 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 11 Jul 2022 05:14:57 -0700
Subject: [PATCH 3/6] Refactor erc-select-read-args

* lisp/erc/erc-backend.el (erc--server-connect-dumb-ipv6-regexp): Add
liberal pattern for matching bracketed IPv6 addresses.
(erc-server-connect): Remove brackets from IPv6 hosts before
connecting.
* lisp/erc/erc.el (erc--ensure-url): Add compat adapter to massage
partial URLs given as input that may be missing the scheme:// portion.
(erc-select-read-args): Keep bracketed IPv6 hosts
intact.  Make this function fully URL-aware (was only partially so).
Accept optional `input' argument.
* lisp/erc/erc-tests.el (erc-tests--ipv6-examples,
erc--server-connect-dumb-ipv6-regexp, erc-select-read-args): Add test
reading user input during interactive invocations of entry points.
---
 lisp/erc/erc-backend.el    |  6 +++
 lisp/erc/erc.el            | 83 ++++++++++++++++++-----------------
 test/lisp/erc/erc-tests.el | 89 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 136 insertions(+), 42 deletions(-)

diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 026b34849a..1cb0876367 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -625,12 +625,18 @@ erc-open-network-stream
   (let ((p (plist-put parameters :nowait t)))
     (apply #'open-network-stream name buffer host service p)))
 
+(defvar erc--server-connect-dumb-ipv6-regexp
+  ;; Not for validation (gives false positives).
+  (rx bot "[" (group (+ (any xdigit digit ":.")) (? "%" (+ alnum))) "]" eot))
+
 (defun erc-server-connect (server port buffer &optional client-certificate)
   "Perform the connection and login using the specified SERVER and PORT.
 We will store server variables in the buffer given by BUFFER.
 CLIENT-CERTIFICATE may optionally be used to specify a TLS client
 certificate to use for authentication when connecting over
 TLS (see `erc-session-client-certificate' for more details)."
+  (when (string-match erc--server-connect-dumb-ipv6-regexp server)
+    (setq server (match-string 1 server)))
   (let ((msg (erc-format-message 'connect ?S server ?p port)) process
         (args `(,(format "erc-%s-%s" server port) nil ,server ,port)))
     (when client-certificate
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 6b14cf87e2..7f25afa8c5 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -70,7 +70,7 @@
 (require 'auth-source)
 (require 'time-date)
 (require 'iso8601)
-(eval-when-compile (require 'subr-x))
+(eval-when-compile (require 'subr-x) (require 'url-parse))
 
 (defconst erc-version "5.4.1"
   "This version of ERC.")
@@ -2094,52 +2094,51 @@ erc-after-connect
   :group 'erc-hooks
   :type '(repeat function))
 
+(defun erc--ensure-url (input)
+  (unless (string-match (rx bot "irc" (? "6") (? "s") "://") input)
+    (when (and (string-match (rx (? (+ any) "@")
+                                 (or (group (* (not "[")) ":" (* any))
+                                     (+ any))
+                                 ":" (+ (not (any ":]"))) eot)
+                             input)
+               (match-beginning 1))
+      (setq input (concat "[" (substring input (match-beginning 1)) "]")))
+    (setq input (concat "irc://" input)))
+  input)
+
 ;;;###autoload
 (defun erc-select-read-args ()
   "Prompt the user for values of nick, server, port, and password."
-  (let (user-input server port nick passwd)
-    (setq user-input (read-string
-                      "IRC server: "
-                      (erc-compute-server) 'erc-server-history-list))
-
-    (if (string-match "\\(.*\\):\\(.*\\)\\'" user-input)
-        (setq port (erc-string-to-port (match-string 2 user-input))
-              user-input (match-string 1 user-input))
-      (setq port
-            (erc-string-to-port (read-string
-                                 "IRC port: " (erc-port-to-string
-                                               (erc-compute-port))))))
-
-    (if (string-match "\\`\\(.*\\)@\\(.*\\)" user-input)
-        (setq nick (match-string 1 user-input)
-              user-input (match-string 2 user-input))
-      (setq nick
-            (if (erc-already-logged-in server port nick)
-                (read-string
-                 (erc-format-message 'nick-in-use ?n nick)
-                 nick 'erc-nick-history-list)
-              (read-string
-               "Nickname: " (erc-compute-nick nick)
-               'erc-nick-history-list))))
-
-    (setq server user-input)
-
-    (setq passwd (if erc-prompt-for-password
-                     (read-passwd "Server password: ")
-                   (with-suppressed-warnings ((obsolete erc-password))
-                     erc-password)))
+  (require 'url-parse)
+  (let* ((input (let ((d (erc-compute-server)))
+                  (read-string (format "Server (default is %S): " d)
+                               nil 'erc-server-history-list d)))
+         ;; For legacy reasons, also accept a URL without a scheme.
+         (url (url-generic-parse-url (erc--ensure-url input)))
+         (server (url-host url))
+         (sp (and (or (string-suffix-p "s" (url-type url))
+                      (and (equal server erc-default-server)
+                           (not (string-prefix-p "irc://" input))))
+                  'ircs-u))
+         (port (or (url-portspec url)
+                   (erc-compute-port
+                    (let ((d (erc-compute-port sp))) ; may be a string
+                      (read-string (format "Port (default is %s): " d)
+                                   nil nil d)))))
+         ;; Trust the user not to connect twice accidentally.  We
+         ;; can't use `erc-already-logged-in' to check for an existing
+         ;; connection without modifying it to consider USER and PASS.
+         (nick (or (url-user url)
+                   (let ((d (erc-compute-nick)))
+                     (read-string (format "Nickname (default is %S): " d)
+                                  nil 'erc-nick-history-list d))))
+         (passwd (or (url-password url)
+                     (if erc-prompt-for-password
+                         (read-passwd "Server password (optional): ")
+                       (with-suppressed-warnings ((obsolete erc-password))
+                         erc-password)))))
     (when (and passwd (string= "" passwd))
       (setq passwd nil))
-
-    (while (erc-already-logged-in server port nick)
-      ;; hmm, this is a problem when using multiple connections to a bnc
-      ;; with the same nick. Currently this code prevents using more than one
-      ;; bnc with the same nick. actually it would be nice to have
-      ;; bncs transparent, so that erc-compute-buffer-name displays
-      ;; the server one is connected to.
-      (setq nick (read-string
-                  (erc-format-message 'nick-in-use ?n nick)
-                  nick 'erc-nick-history-list)))
     (list :server server :port port :nick nick :password passwd)))
 
 ;;;###autoload
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index c88dd9888d..f72db816af 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -953,4 +953,93 @@ erc-message
     (kill-buffer "ExampleNet")
     (kill-buffer "#chan")))
 
+(defvar erc-tests--ipv6-examples
+  '("1:2:3:4:5:6:7:8"
+    "::ffff:10.0.0.1" "::ffff:1.2.3.4" "::ffff:0.0.0.0"
+    "1:2:3:4:5:6:77:88" "::ffff:255.255.255.255"
+    "fe08::7:8" "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+    "1:2:3:4:5:6:7:8" "1::" "1:2:3:4:5:6:7::" "1::8"
+    "1:2:3:4:5:6::8" "1:2:3:4:5:6::8" "1::7:8" "1:2:3:4:5::7:8"
+    "1:2:3:4:5::8" "1::6:7:8" "1:2:3:4::6:7:8" "1:2:3:4::8"
+    "1::5:6:7:8" "1:2:3::5:6:7:8" "1:2:3::8" "1::4:5:6:7:8"
+    "1:2::4:5:6:7:8" "1:2::8" "1::3:4:5:6:7:8" "1::3:4:5:6:7:8"
+    "1::8" "::2:3:4:5:6:7:8" "::2:3:4:5:6:7:8" "::8"
+    "::" "fe08::7:8%eth0" "fe08::7:8%1" "::255.255.255.255"
+    "::ffff:255.255.255.255" "::ffff:0:255.255.255.255"
+    "2001:db8:3:4::192.0.2.33" "64:ff9b::192.0.2.33"))
+
+(ert-deftest erc--server-connect-dumb-ipv6-regexp ()
+  (dolist (a erc-tests--ipv6-examples)
+    (should-not (string-match erc--server-connect-dumb-ipv6-regexp a))
+    (should (string-match erc--server-connect-dumb-ipv6-regexp
+                          (concat "[" a "]")))))
+
+(ert-deftest erc-select-read-args ()
+
+  (ert-info ("Defaults to TLS")
+    (should (equal (ert-simulate-keys "\r\r\r\r"
+                     (erc-select-read-args))
+                   (list :server "irc.libera.chat"
+                         :port 6697
+                         :nick (user-login-name)
+                         :password nil))))
+
+  (ert-info ("Override default TLS")
+    (should (equal (ert-simulate-keys "irc://irc.libera.chat\r\r\r\r"
+                     (erc-select-read-args))
+                   (list :server "irc.libera.chat"
+                         :port 6667
+                         :nick (user-login-name)
+                         :password nil))))
+
+  (ert-info ("Address includes port")
+    (should (equal (ert-simulate-keys
+                       "localhost:6667\rnick\r\r"
+                     (erc-select-read-args))
+                   (list :server "localhost"
+                         :port 6667
+                         :nick "nick"
+                         :password nil))))
+
+  (ert-info ("Address includes nick, password skipped via option")
+    (should (equal (ert-simulate-keys "nick@localhost:6667\r"
+                     (let (erc-prompt-for-password)
+                       (erc-select-read-args)))
+                   (list :server "localhost"
+                         :port 6667
+                         :nick "nick"
+                         :password nil))))
+
+  (ert-info ("Addresss includes nick and password")
+    (should (equal (ert-simulate-keys "nick:sesame@localhost:6667\r"
+                     (erc-select-read-args))
+                   (list :server "localhost"
+                         :port 6667
+                         :nick "nick"
+                         :password "sesame"))))
+
+  (ert-info ("IPv6 address plain")
+    (should (equal (ert-simulate-keys "::1\r\r\r\r"
+                     (erc-select-read-args))
+                   (list :server "[::1]"
+                         :port 6667
+                         :nick (user-login-name)
+                         :password nil))))
+
+  (ert-info ("IPv6 address with port")
+    (should (equal (ert-simulate-keys "[::1]:6667\r\r\r"
+                     (erc-select-read-args))
+                   (list :server "[::1]"
+                         :port 6667
+                         :nick (user-login-name)
+                         :password nil))))
+
+  (ert-info ("IPv6 address includes nick")
+    (should (equal (ert-simulate-keys "nick@[::1]:6667\r\r"
+                     (erc-select-read-args))
+                   (list :server "[::1]"
+                         :port 6667
+                         :nick "nick"
+                         :password nil)))))
+
 ;;; erc-tests.el ends here
-- 
2.38.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: 0004-Default-to-TLS-port-when-calling-erc-tls-from-lisp.patch --]
[-- Type: text/x-patch, Size: 5926 bytes --]

From a9b47f5a6079fb3030c9e1514b4cbbda86dafff8 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 11 Jul 2022 05:14:57 -0700
Subject: [PATCH 4/6] Default to TLS port when calling erc-tls from lisp

* lisp/erc/erc.el (erc-legacy-port-names, erc-normalize-port): Add
standard IANA port-name mappings for 6667 and 6697, as well as an
option to opt in for saner but nonstandard behavior.
(erc-open): Add note to doc string explaining that params `connect'
and `channel' are mutually exclusive.
(erc-tls): Call `erc-compute-port' with override.
(erc-compute-port): Call `erc-normalize-port' with result'.

* test/lisp/erc/erc-tests.el (erc-tls): Add simplistic test focusing
on default parameters.
---
 lisp/erc/erc.el            | 34 ++++++++++++++++++++++++++-----
 test/lisp/erc/erc-tests.el | 41 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 7f25afa8c5..01bb6f9f45 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1534,6 +1534,15 @@ erc-reuse-buffers
 (make-obsolete-variable 'erc-reuse-buffers
                         "old behavior when t now permanent" "29.1")
 
+(defcustom erc-legacy-port-names 'legacy
+  "Interpret \"irc\" and \"ircs\" using IANA service mappings.
+When non-nil, this yields 194 and 994 instead of 6667 and 6697.
+When set to `legacy', it also emits a warning saying that the
+default will change to nil in the future."
+  :group 'erc
+  :package-version '(ERC . "5.4.1") ; FIXME increment on ELPA release
+  :type '(choice (const nil) (const legacy) (const t)))
+
 (defun erc-normalize-port (port)
   "Normalize the port specification PORT to integer form.
 PORT may be an integer, a string or a symbol.  If it is a string or a
@@ -1542,6 +1551,11 @@ erc-normalize-port
 * ircs        -> 994
 * ircd        -> 6667
 * ircd-dalnet -> 7000"
+  ;; These were updated in 2022 to reflect modern standards and
+  ;; practices.  See also:
+  ;;
+  ;; https://datatracker.ietf.org/doc/html/rfc7194#section-1
+  ;; https://www.iana.org/assignments/service-names-port-numbers
   (cond
    ((symbolp port)
     (erc-normalize-port (symbol-name port)))
@@ -1550,8 +1564,16 @@ erc-normalize-port
       (cond
        ((> port-nr 0)
         port-nr)
-       ((string-equal port "irc")
-        194)
+       ((string-equal port "ircu") 6667)
+       ((string-equal port "ircs-u") 6697)
+       ((member port '("irc" "ircs"))
+        (when (eq erc-legacy-port-names 'legacy)
+          (lwarn 'ERC 'warning
+                 (concat "`erc-legacy-port-names' will default to nil "
+                         "in a future version of ERC.")))
+        (if (string= port "irc")
+            (if erc-legacy-port-names 194 6667)
+          (if erc-legacy-port-names 994 6697)))
        ((string-equal port "ircs")
         994)
        ((string-equal port "ircd")
@@ -1924,7 +1946,9 @@ erc-open
 
 If CONNECT is non-nil, connect to the server.  Otherwise assume
 already connected and just create a separate buffer for the new
-target CHANNEL.
+target given by CHANNEL, meaning these parameters are mutually
+exclusive.  Note that CHANNEL may also be a query; its name has
+been retained for historical reasons.
 
 Use PASSWD as user password on the server.  If TGT-LIST is
 non-nil, use it to initialize `erc-default-recipients'.
@@ -2183,7 +2207,7 @@ 'erc-ssl
 
 ;;;###autoload
 (cl-defun erc-tls (&key (server (erc-compute-server))
-                        (port   (erc-compute-port))
+                        (port   (erc-compute-port 'ircs-u))
                         (nick   (erc-compute-nick))
                         (user   (erc-compute-user))
                         password
@@ -6390,7 +6414,7 @@ erc-compute-port
 - PORT (the argument passed to this function)
 - The `erc-port' option
 - The `erc-default-port' variable"
-  (or port erc-port erc-default-port))
+  (erc-normalize-port (or port erc-port erc-default-port)))
 
 ;; time routines
 
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index f72db816af..348c047b73 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1042,4 +1042,45 @@ erc-select-read-args
                          :nick "nick"
                          :password nil)))))
 
+(ert-deftest erc-tls ()
+  (let (calls)
+    (cl-letf (((symbol-function 'user-login-name)
+               (lambda (&optional _) "tester"))
+              ((symbol-function 'erc-open)
+               (lambda (&rest r) (push r calls))))
+
+      (ert-info ("Defaults")
+        (erc-tls)
+        (should (equal (pop calls)
+                       '("irc.libera.chat" 6697 "tester" "unknown" t
+                         nil nil nil nil nil "user" nil))))
+
+      (ert-info ("Full")
+        (erc-tls :server "irc.gnu.org"
+                 :port 7000
+                 :user "bobo"
+                 :nick "bob"
+                 :full-name "Bob's Name"
+                 :password "bob:changeme"
+                 :client-certificate t
+                 :id 'GNU.org)
+        (should (equal (pop calls)
+                       '("irc.gnu.org" 7000 "bob" "Bob's Name" t
+                         "bob:changeme" nil nil nil t "bobo" GNU.org))))
+
+      ;; Values are often nil when called by lisp code, which leads to
+      ;; null params.  This is why `erc-open' recomputes everything.
+      (ert-info ("Fallback")
+        (let ((erc-nick "bob")
+              (erc-server "irc.gnu.org")
+              (erc-email-userid "bobo")
+              (erc-user-full-name "Bob's Name"))
+          (erc-tls :server nil
+                   :port 7000
+                   :nick nil
+                   :password "bob:changeme"))
+        (should (equal (pop calls)
+                       '(nil 7000 nil "Bob's Name" t
+                             "bob:changeme" nil nil nil nil "bobo" nil)))))))
+
 ;;; erc-tests.el ends here
-- 
2.38.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #7: 0005-Add-optional-server-param-to-erc-networks-determine.patch --]
[-- Type: text/x-patch, Size: 2924 bytes --]

From 9a44991ef351274c45d300d825066dd805296454 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 11 Jul 2022 05:14:57 -0700
Subject: [PATCH 5/6] Add optional server param to erc-networks--determine

* lisp/erc/erc-networks.el (erc-networks--determine): Accept optional
`server' argument.

* test/lisp/erc/erc-networks-tests.el (erc-networks--determine): Add
test.
---
 lisp/erc/erc-networks.el            |  9 +++++----
 test/lisp/erc/erc-networks-tests.el | 17 +++++++++++++++++
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el
index dba6ead073..b3e5fcf1a3 100644
--- a/lisp/erc/erc-networks.el
+++ b/lisp/erc/erc-networks.el
@@ -1256,14 +1256,15 @@ erc-set-network-name
 (defconst erc-networks--name-missing-sentinel (gensym "Unknown ")
   "Value to cover rare case of a literal NETWORK=nil.")
 
-(defun erc-networks--determine ()
+(defun erc-networks--determine (&optional server)
   "Return the name of the network as a symbol.
-Search `erc-networks-alist' for a known entity matching
+Search `erc-networks-alist' for a known entity matching SERVER or
 `erc-server-announced-name'.  If that fails, use the display name
 given by the `RPL_ISUPPORT' NETWORK parameter."
   (or (cl-loop for (name matcher) in erc-networks-alist
-               when (and matcher (string-match (concat matcher "\\'")
-                                               erc-server-announced-name))
+               when (and matcher
+                         (string-match (concat matcher "\\'")
+                                       (or server erc-server-announced-name)))
                return name)
       (and-let* ((vanity (erc--get-isupport-entry 'NETWORK 'single))
                  ((intern vanity))))
diff --git a/test/lisp/erc/erc-networks-tests.el b/test/lisp/erc/erc-networks-tests.el
index 32bdfa11ff..fc12bf7ce3 100644
--- a/test/lisp/erc/erc-networks-tests.el
+++ b/test/lisp/erc/erc-networks-tests.el
@@ -1704,4 +1704,21 @@ erc-networks--update-server-identity--triple-new
 
   (erc-networks-tests--clean-bufs))
 
+(ert-deftest erc-networks--determine ()
+  (should (eq (erc-networks--determine "irc.libera.chat") 'Libera.Chat))
+  (should (eq (erc-networks--determine "irc.oftc.net") 'OFTC))
+  (should (eq (erc-networks--determine "irc.dal.net") 'DALnet))
+
+  (let ((erc-server-announced-name "zirconium.libera.chat"))
+    (should (eq (erc-networks--determine) 'Libera.Chat)))
+  (let ((erc-server-announced-name "weber.oftc.net"))
+    (should (eq (erc-networks--determine) 'OFTC)))
+  (let ((erc-server-announced-name "redemption.ix.us.dal.net"))
+    (should (eq (erc-networks--determine) 'DALnet)))
+
+  ;; Failure
+  (let ((erc-server-announced-name "irc-us2.alphachat.net"))
+    (should (eq (erc-networks--determine)
+                erc-networks--name-missing-sentinel))))
+
 ;;; erc-networks-tests.el ends here
-- 
2.38.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #8: 0006-Improve-new-connections-in-erc-handle-irc-url.patch --]
[-- Type: text/x-patch, Size: 14236 bytes --]

From 3658e89614cbe3b5b27f09271b7bc738a1c7ec38 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 11 Jul 2022 05:14:57 -0700
Subject: [PATCH 6/6] Improve new connections in erc-handle-irc-url

* lisp/erc/erc.el (erc-handle-irc-url): Fix `erc-open' invocation so
that the server buffer is named correctly.  Arrange for JOINing a
channel in a manner similar to ERC's autojoin module.
(erc-url-connect-function): Add new option for creating a new ERC
connection based on info parsed from a URL.
(erc--url-default-connect-function): New function to serve as an
interactive-only fallback when a user hasn't specified a URL connect
function.
(erc-browse-url-handler): Add autoloaded function.

* lisp/erc/erc-compat.el (erc-compat--browse-url--irc): Add new compat
function for `browse-url-irc'. Also add it to
`browse-url-default-handlers' on Emacs versions below 29.

* lisp/erc/erc-tests.el (erc-tests--make-server-buf,
erc-tests--make-client-buf): Add helpers for creating dummy ERC
buffers.
(erc-handle-irc-url): Add test.
---
 doc/misc/erc.texi          |  39 ++++++++++++++
 lisp/erc/erc-compat.el     |  15 ++++++
 lisp/erc/erc.el            | 107 +++++++++++++++++++++++++++++++------
 test/lisp/erc/erc-tests.el |  95 ++++++++++++++++++++++++++++++++
 4 files changed, 239 insertions(+), 17 deletions(-)

diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index 3db83197f9..d01eab1bbb 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -79,6 +79,7 @@ Top
 
 * Connecting::                  Ways of connecting to an IRC server.
 * Sample Configuration::        An example configuration file.
+* Integrations::                Integrations available for ERC.
 * Options::                     Options that are available for ERC.
 
 @end detailmenu
@@ -526,6 +527,7 @@ Advanced Usage
 @menu
 * Connecting::                  Ways of connecting to an IRC server.
 * Sample Configuration::        An example configuration file.
+* Integrations::                Integrations available for ERC.
 * Options::                     Options that are available for ERC.
 @end menu
 
@@ -990,6 +992,43 @@ Sample Configuration
 ;; (setq erc-kill-server-buffer-on-quit t)
 @end lisp
 
+@node Integrations
+@section Integrations
+@cindex integrations
+
+@subheading URL
+For anything to work, you'll want to set @code{url-irc-function} to
+@code{url-irc-erc}.  As a rule of thumb, libraries that rely directly
+on @code{url-retrieve} should be good to go out the box from Emacs
+29.1 onward.  On older versions of Emacs, you may need to
+@code{(require 'erc)} beforehand. @pxref{Retrieving URLs,,, url, URL}.
+
+For other apps and libraries, such as those relying on the
+higher-level @code{browse-url}, you'll oftentimes be asked to specify
+a pattern, sometimes paired with a function that accepts a string URL
+as a first argument.  For example, with EWW, you may need to tack
+something like @code{"\\|\\`irc6?s?:"} onto the end of
+@code{eww-use-browse-url}.  But with @code{gnus-button-alist}, you'll
+need a function as well:
+
+@lisp
+  '("\\birc6?s?://[][a-z0-9.,@@_:+%?&/#-]+"
+    0 t erc-browse-url-handler 0)
+@end lisp
+
+@defun erc-browse-url-handler url &rest args
+An autoloaded convenience function for use in options like those
+mentioned above.  @var{url} must be a string.  In Emacs 29 and above,
+the function @code{browse-url-irc} can be used instead.
+@end defun
+
+@noindent
+Keep in mind that when fiddling with these options, it may be easier
+(and more polite) to connect to a local server or a test network, like
+@samp{ircs://testnet.ergo.chat/#test}, since these generally don't
+require authentication.
+
+
 @node Options
 @section Options
 @cindex options
diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el
index 03bd8f1352..683d19dfc7 100644
--- a/lisp/erc/erc-compat.el
+++ b/lisp/erc/erc-compat.el
@@ -168,6 +168,21 @@ erc-compat--with-memoization
     `(cl--generic-with-memoization ,table ,@forms))
    (t `(progn ,@forms))))
 
+(declare-function browse-url-irc "browse-url" (url &rest _))
+
+(defun erc-compat--browse-url-irc (string &rest _)
+  "Parse STRING and call `url-irc'."
+  (require 'url-irc)
+  (if (< emacs-major-version 29)
+      ;; `url-irc' binds this in Emacs 29+.
+      (let ((url-current-object (url-generic-parse-url string)))
+        (url-irc url-current-object))
+    (browse-url-irc string)))
+
+(when (< emacs-major-version 29)
+  (add-to-list 'browse-url-default-handlers
+               '("\\`irc6?s?://" . erc-compat--browse-url-irc)))
+
 (provide 'erc-compat)
 
 ;;; erc-compat.el ends here
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 01bb6f9f45..3c9293e28a 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -7184,25 +7184,98 @@ erc-get-parsed-vector-type
 ;; Teach url.el how to open irc:// URLs with ERC.
 ;; To activate, customize `url-irc-function' to `url-irc-erc'.
 
-;; FIXME change user to nick, and use API to find server buffer
+(defcustom erc-url-connect-function nil
+  "When non-nil, a function used to connect to an IRC URL.
+Called with any number of keyword arguments recognized by `erc'
+and `erc-tls'.  The variable `url-current-object', if non-nil,
+can be used to help determine whether to connect using TLS."
+  :group 'erc
+  :package-version '(ERC . "5.4.1") ; FIXME increment on release
+  :type '(choice (const nil) function))
+
+(defun erc--url-default-connect-function (&rest plist)
+  (let* ((scheme (and url-current-object (url-type url-current-object)))
+         (ircsp (if scheme
+                    (string-suffix-p "s" scheme)
+                  (or (eql 6697 (plist-get plist :port))
+                      (yes-or-no-p "Connect using TLS? "))))
+         (erc-server (plist-get plist :server))
+         (erc-port (or (plist-get plist :port)
+                       (and ircsp (erc-normalize-port 'ircs-u))
+                       erc-port))
+         (erc-nick (or (plist-get plist :nick) erc-nick))
+         (erc-password (plist-get plist :password))
+         (args (erc-select-read-args)))
+    (unless ircsp
+      (setq ircsp (eql 6697 erc-port)))
+    (apply (if ircsp #'erc-tls #'erc) args)))
+
+;; The current spec, unlike the 2003 Butcher draft, doesn't explicitly
+;; allow for an auth[:password]@ component (or trailing ,flags or
+;; &options).
+;;
+;; https://www.iana.org/assignments/uri-schemes
+;; https://datatracker.ietf.org/doc/html/draft-butcher-irc-url#section-6
+
 ;;;###autoload
-(defun erc-handle-irc-url (host port channel user password)
-  "Use ERC to IRC on HOST:PORT in CHANNEL as USER with PASSWORD.
+(defun erc-handle-irc-url (host port channel nick password)
+  "Use ERC to IRC on HOST:PORT in CHANNEL.
 If ERC is already connected to HOST:PORT, simply /join CHANNEL.
-Otherwise, connect to HOST:PORT as USER and /join CHANNEL."
-  (let ((server-buffer
-         (car (erc-buffer-filter
-               (lambda ()
-                 (and (string-equal erc-session-server host)
-                      (= erc-session-port port)
-                      (erc-open-server-buffer-p)))))))
-    (with-current-buffer (or server-buffer (current-buffer))
-      (if (and server-buffer channel)
-          (erc-cmd-JOIN channel)
-        (erc-open host port (or user (erc-compute-nick)) (erc-compute-full-name)
-                  (not server-buffer) password nil channel
-                  (when server-buffer
-                    (get-buffer-process server-buffer)))))))
+Otherwise, connect to HOST:PORT as NICK and /join CHANNEL.
+
+Beginning with ERC 5.5, new connections require human intervention.
+Customize `erc-url-connect-function' to override this."
+  (when (eql port 0) (setq port nil))
+  (let* ((net (erc-networks--determine host))
+         (server-buffer
+          ;; Viable matches may slip through the cracks for unknown
+          ;; networks.  Additional passes could likely improve things.
+          (car (erc-buffer-filter
+                (lambda ()
+                  (and (not erc--target)
+                       (erc-server-process-alive)
+                       ;; Always trust a matched network.
+                       (or (and net (eq net (erc-network)))
+                           (and (string-equal erc-session-server host)
+                                ;; Ports only matter when dialed hosts
+                                ;; match and we have sufficient info.
+                                (or (not port)
+                                    (= (erc-normalize-port erc-session-port)
+                                       port)))))))))
+         key deferred)
+    (unless server-buffer
+      (setq deferred t
+            server-buffer (apply (or erc-url-connect-function
+                                     #'erc--url-default-connect-function)
+                                 :server host
+                                 `(,@(and port (list :port port))
+                                   ,@(and nick (list :nick nick))
+                                   ,@(and password `(:password ,password))))))
+    (when channel
+      ;; These aren't percent-decoded by default
+      (when (string-prefix-p "%" channel)
+        (setq channel (url-unhex-string channel)))
+      (cl-multiple-value-setq (channel key) (split-string channel "[?]"))
+      (if deferred
+          ;; Alternatively, we could make this a defmethod, so when
+          ;; autojoin is loaded, it can do its own thing.  Also, as
+          ;; with `erc-once-with-server-event', it's fine to set local
+          ;; hooks here because they're killed when reconnecting.
+          (with-current-buffer server-buffer
+            (letrec ((f (lambda (&rest _)
+                          (remove-hook 'erc-after-connect f t)
+                          (erc-cmd-JOIN channel key))))
+              (add-hook 'erc-after-connect f nil t)))
+        (with-current-buffer server-buffer
+          (erc-cmd-JOIN channel key))))))
+
+(defvar url-irc-function)
+
+;;;###autoload
+(defun erc-browse-url-handler (url &rest _)
+  "Launch an ERC session when given an irc:// URL."
+  (let ((url-irc-function 'url-irc-erc))
+    (erc-compat--browse-url-irc url)))
 
 (provide 'erc)
 
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 348c047b73..e097090e5d 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1083,4 +1083,99 @@ erc-tls
                        '(nil 7000 nil "Bob's Name" t
                              "bob:changeme" nil nil nil nil "bobo" nil)))))))
 
+(defun erc-tests--make-server-buf (name)
+  (with-current-buffer (get-buffer-create name)
+    (erc-mode)
+    (setq erc-server-process (start-process "sleep" (current-buffer)
+                                            "sleep" "1")
+          erc-session-server (concat "irc." name ".org")
+          erc-session-port 6667
+          erc-network (intern name))
+    (set-process-query-on-exit-flag erc-server-process nil)
+    (current-buffer)))
+
+(defun erc-tests--make-client-buf (server name)
+  (unless (bufferp server)
+    (setq server (get-buffer server)))
+  (with-current-buffer (get-buffer-create name)
+    (erc-mode)
+    (setq erc--target (erc--target-from-string name))
+    (dolist (v '(erc-server-process
+                 erc-session-server
+                 erc-session-port
+                 erc-network))
+      (set v (buffer-local-value v server)))
+    (current-buffer)))
+
+(ert-deftest erc-handle-irc-url ()
+  (let* (calls
+         rvbuf
+         erc-networks-alist
+         erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook
+         (erc-url-connect-function
+          (lambda (&rest r)
+            (push r calls)
+            (if (functionp rvbuf) (funcall rvbuf) rvbuf))))
+
+    (cl-letf (((symbol-function 'erc-cmd-JOIN)
+               (lambda (&rest r) (push r calls))))
+
+      (with-current-buffer (erc-tests--make-server-buf "foonet")
+        (setq rvbuf (current-buffer)))
+      (erc-tests--make-server-buf "barnet")
+      (erc-tests--make-server-buf "baznet")
+
+      (ert-info ("Unknown network")
+        (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil)
+        (should (equal '("#chan" nil) (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Unknown network, no port")
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil)
+        (should (equal '("#chan" nil) (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Known network, no port")
+        (setq erc-networks-alist '((foonet "irc.foonet.org")))
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil)
+        (should (equal '("#chan" nil) (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Known network, different port")
+        (erc-handle-irc-url "irc.foonet.org" 6697 "#chan" nil nil)
+        (should (equal '("#chan" nil) (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Known network, existing chan with key")
+        (erc-tests--make-client-buf "foonet" "#chan")
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil)
+        (should (equal '("#chan" "sec") (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Unknown network, connect, no chan")
+        (erc-handle-irc-url "irc.gnu.org" nil nil nil nil)
+        (should (equal '(:server "irc.gnu.org") (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Unknown network, connect, chan")
+        (with-current-buffer "foonet"
+          (should-not (local-variable-p 'erc-after-connect)))
+        (setq rvbuf (lambda () (erc-tests--make-server-buf "gnu")))
+        (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil)
+        (should (equal '(:server "irc.gnu.org") (pop calls)))
+        (should-not calls)
+        (with-current-buffer "gnu"
+          (should (local-variable-p 'erc-after-connect))
+          (funcall (car erc-after-connect))
+          (should (equal '("#spam" nil) (pop calls)))
+          (should-not erc-after-connect)
+          (should-not (local-variable-p 'erc-after-connect)))
+        (should-not calls))))
+
+  (when noninteractive
+    (kill-buffer "foonet")
+    (kill-buffer "barnet")
+    (kill-buffer "baznet")
+    (kill-buffer "#chan")))
+
 ;;; erc-tests.el ends here
-- 
2.38.1


  parent reply	other threads:[~2022-11-08 14:09 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <87pmiabvd5.fsf@neverwas.me>
2022-07-12 12:49 ` bug#56514: 29.0.50; Improve ERC's URI scheme integration for irc:// links Lars Ingebrigtsen
     [not found] ` <87edyqzeag.fsf@gnus.org>
2022-07-13 14:44   ` J.P.
     [not found]   ` <874jzl2hsv.fsf@neverwas.me>
2022-07-13 15:55     ` Stefan Kangas
     [not found]     ` <CADwFkmkgXKH3y2i1si76V_NOuSyJENVrCLdEJ1AfDHEv9qh8jw@mail.gmail.com>
2022-07-14  7:00       ` J.P.
     [not found]       ` <874jzkuqk3.fsf@neverwas.me>
2022-11-08 14:09         ` J.P. [this message]
2022-11-08 15:16           ` Stefan Kangas
     [not found]           ` <CADwFkm=d+8wb6o_EwvKZWR7yc4tbwscgZ-YPzBnSqty42W+_Pg@mail.gmail.com>
2022-11-09 13:41             ` J.P.
2022-11-08 14:41 ` bug#56514: ircs:// integration for rcirc (bug#56514) J.P.
2022-11-11 14:05 ` bug#56514: 29.0.50; Improve ERC's URI scheme integration for irc:// links J.P.
     [not found] ` <87iljl4meb.fsf@neverwas.me>
2022-11-16 14:22   ` J.P.
2022-12-30 14:20 ` J.P.
2023-11-06  2:34 ` J.P.
     [not found] ` <875y2flics.fsf@neverwas.me>
2023-11-11 10:15   ` Eli Zaretskii
2022-07-12  8:14 J.P.

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87pmdxlera.fsf@neverwas.me \
    --to=jp@neverwas.me \
    --cc=56514@debbugs.gnu.org \
    --cc=emacs-erc@gnu.org \
    --cc=larsi@gnus.org \
    --cc=stefankangas@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.