emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 3d25a9fccfa 3/5: Merge branch 'master' of git.sv.gnu.org:/srv/git


From: Michael Albinus
Subject: master 3d25a9fccfa 3/5: Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs
Date: Sun, 29 Oct 2023 10:29:48 -0400 (EDT)

branch: master
commit 3d25a9fccfa620aebc93947a1738a4553b1b8592
Merge: e2d2726db7c c79ea103efd
Author: Michael Albinus <michael.albinus@gmx.de>
Commit: Michael Albinus <michael.albinus@gmx.de>

    Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs
---
 ChangeLog.2                                        |  13 +-
 ChangeLog.3                                        |   2 +-
 admin/notes/emba                                   |   2 +-
 doc/emacs/custom.texi                              |   9 +
 doc/misc/gnus.texi                                 |  22 +-
 etc/ERC-NEWS                                       |  10 +-
 etc/NEWS                                           |   5 +
 etc/publicsuffix.txt                               |   2 +-
 java/org/gnu/emacs/EmacsWindow.java                |  33 ++-
 lisp/ChangeLog.12                                  |   4 +-
 lisp/ChangeLog.14                                  |   2 +-
 lisp/ChangeLog.15                                  |   2 +-
 lisp/ChangeLog.16                                  |   2 +-
 lisp/auth-source.el                                |   6 -
 lisp/cus-edit.el                                   | 289 +++++++++++++++++++++
 lisp/dired.el                                      |   4 +-
 lisp/dnd.el                                        |  14 +-
 lisp/erc/erc-fill.el                               |  30 ++-
 lisp/erc/erc-goodies.el                            |  27 +-
 lisp/erc/erc-stamp.el                              |  40 ++-
 lisp/erc/erc-track.el                              |  14 +-
 lisp/erc/erc.el                                    | 126 +++++++--
 lisp/files-x.el                                    |   3 +-
 lisp/gnus/ChangeLog.3                              |   2 +-
 lisp/gnus/gnus-art.el                              |  12 +-
 lisp/gnus/gnus-bookmark.el                         |   8 +-
 lisp/gnus/gnus-group.el                            |  20 +-
 lisp/gnus/gnus-sum.el                              |   5 +-
 lisp/gnus/message.el                               |  13 +-
 lisp/gnus/mml.el                                   |  59 +++--
 lisp/gnus/mml2015.el                               |   4 +-
 lisp/gnus/nnheader.el                              |   2 +-
 lisp/gnus/spam-report.el                           |   6 +-
 lisp/net/browse-url.el                             |   2 +-
 lisp/progmodes/cc-mode.el                          |   2 +-
 lisp/progmodes/octave.el                           |   4 +-
 lisp/subr.el                                       |  12 +-
 lisp/url/url-vars.el                               |   2 +-
 lisp/vc/log-view.el                                |  12 +-
 nextstep/ChangeLog.1                               |   2 +-
 src/androidterm.c                                  |  10 +-
 src/androidvfs.c                                   |  44 ++--
 src/haikuterm.c                                    |   8 +-
 src/keyboard.c                                     |   2 +-
 src/msdos.c                                        |   2 +-
 src/nsterm.m                                       |   4 +-
 src/pgtkterm.c                                     |   4 +-
 src/profiler.c                                     |  71 ++---
 src/w32.c                                          |   2 +-
 src/w32inevt.c                                     |   2 +-
 src/w32term.c                                      |   8 +-
 src/window.c                                       |  20 +-
 src/window.h                                       |   2 +-
 src/xdisp.c                                        |   4 +-
 src/xterm.c                                        |  17 +-
 test/lisp/dnd-tests.el                             |  64 ++++-
 test/lisp/erc/erc-fill-tests.el                    |  23 +-
 test/lisp/erc/erc-scenarios-stamp.el               |  28 +-
 test/lisp/erc/erc-tests.el                         |  84 +++++-
 test/lisp/erc/resources/erc-d/erc-d-t.el           |   1 +
 .../erc/resources/fill/snapshots/merge-wrap-01.eld |   2 +-
 test/lisp/gnus/gnus-group-tests.el                 |   2 +-
 test/lisp/subr-tests.el                            |  16 +-
 63 files changed, 949 insertions(+), 299 deletions(-)

diff --git a/ChangeLog.2 b/ChangeLog.2
index 11e6049b0bd..d40401093c5 100644
--- a/ChangeLog.2
+++ b/ChangeLog.2
@@ -10661,8 +10661,8 @@
 
        * lisp/gnus/nnir.el (nnir-request-update-mark):
        Default to the original mark.
-       cf. <http://thread.gmane.org/gmane.emacs.gnus.general/86583>
-       and <http://thread.gmane.org/gmane.emacs.gnus.general/86640>
+       cf. <http://thread.gmane.org/gmane.emacs.gnus.general/86583> [dead link]
+       and <http://thread.gmane.org/gmane.emacs.gnus.general/86640> [dead link]
 
 2016-01-19  Glenn Morris  <rgm@gnu.org>
 
@@ -12012,7 +12012,7 @@
        (Maybe this is the last merge from Gnus git to Emacs git)
 
        Cf. discussion on ding mailing list, messages in
-       <http://thread.gmane.org/gmane.emacs.gnus.general/86228>.
+       <http://thread.gmane.org/gmane.emacs.gnus.general/86228>. [dead link]
        Common code from the three files mml-smime.el, mml1991.el, and
        mml2015.el is moved to mml-sec.el.  Auxiliary functions are added
        to gnus-util.el.
@@ -15206,8 +15206,9 @@
        Remove nnml-retrieve-groups that is unnecessary and somewhat problematic
 
        * lisp/gnus/nnml.el (nnml-retrieve-groups): Remove.  See:
-       <http://thread.gmane.org/gmane.emacs.gnus.general/86308> and
-       <http://thread.gmane.org/gmane.emacs.gnus.general/86321>
+       <http://thread.gmane.org/gmane.emacs.gnus.general/86308> [dead link]
+       and
+       <http://thread.gmane.org/gmane.emacs.gnus.general/86321> [dead link]
 
 2015-11-25  Paul Eggert  <eggert@cs.ucla.edu>
 
@@ -30968,7 +30969,7 @@
 2015-05-28  Katsumi Yamaoka  <yamaoka@jpl.org>
 
        * lisp/gnus/gnus-art.el (gnus-button-alist): Re-revert last change.
-       cf. <http://news.gmane.org/group/gmane.emacs.devel/thread=186896>
+       cf. <http://news.gmane.org/group/gmane.emacs.devel/thread=186896> [dead 
link]
 
 2015-05-28  Samer Masterson  <samer@samertm.com>
 
diff --git a/ChangeLog.3 b/ChangeLog.3
index a67f50668c6..d831b14178c 100644
--- a/ChangeLog.3
+++ b/ChangeLog.3
@@ -234791,7 +234791,7 @@
        (Maybe this is the last merge from Gnus git to Emacs git)
 
        Cf. discussion on ding mailing list, messages in
-       <http://thread.gmane.org/gmane.emacs.gnus.general/86228>.
+       <http://thread.gmane.org/gmane.emacs.gnus.general/86228>. [dead link]
        Common code from the three files mml-smime.el, mml1991.el, and
        mml2015.el is moved to mml-sec.el.  Auxiliary functions are added
        to gnus-util.el.
diff --git a/admin/notes/emba b/admin/notes/emba
index cad7a2e121c..88ac3ebe351 100644
--- a/admin/notes/emba
+++ b/admin/notes/emba
@@ -21,7 +21,7 @@ If you want to receive these notifications, please subscribe 
at
 <https://lists.gnu.org/mailman/listinfo/emacs-buildstatus>.
 
 Alternatively, these notifications can be read via gmane at
-<nntp+news.gmane.org:gmane.emacs.buildstatus>.
+<nntp+news.gmane.io:gmane.emacs.buildstatus>.
 
 The messages contain a URL to the log file of the failed job, like
 <https://emba.gnu.org/emacs/emacs/-/jobs/739/raw>.
diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index adecc873163..e2d35863bd0 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -1515,6 +1515,11 @@ want to modify.  Although it doesn't have to exist, you 
must enter a
 valid filename, either @file{.dir-locals.el} or
 @file{.dir-locals-2.el}.
 
+@findex customize-dirlocals
+There's also a command to pop up an Easy Customization buffer
+(@pxref{Easy Customization}) to edit directory local variables,
+@code{customize-dirlocals}.
+
 @findex dir-locals-set-class-variables
 @findex dir-locals-set-directory-class
   Another method of specifying directory-local variables is to define
@@ -1597,6 +1602,10 @@ discriminate for the properties @code{:protocol} (this 
is the Tramp
 method) or @code{:user} (a remote user name).  The @code{nil} criteria
 matches all buffers with a remote default directory.
 
+  Be careful when declaring different profiles with the same variable,
+and setting these profiles to criteria which could match in parallel.
+It is unspecified which variable value is used then.
+
 @node Key Bindings
 @section Customizing Key Bindings
 @cindex key bindings
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index 4b1ef5c27b4..582038d973f 100644
--- a/doc/misc/gnus.texi
+++ b/doc/misc/gnus.texi
@@ -3216,7 +3216,7 @@ if address "sender" "sieve-admin@@extundo.com" @{
 @end example
 
 To generate tests for multiple email-addresses use a group parameter
-like @code{(sieve address "sender" ("name@@one.org" else@@two.org"))}.
+like @code{(sieve address "sender" ("name@@one.org" "else@@two.org"))}.
 When generating a sieve script (@pxref{Sieve Commands}) Sieve code
 like the following is generated:
 
@@ -3372,7 +3372,7 @@ You can define different sorting to different groups via
 group by reverse date to see the latest news at the top and an
 @acronym{RSS} group by subject.  In this example, the first group is the
 Debian daily news group @code{gmane.linux.debian.user.news} from
-news.gmane.org.  The @acronym{RSS} group corresponds to the Debian
+news.gmane.io.  The @acronym{RSS} group corresponds to the Debian
 weekly news RSS feed
 @url{https://packages.debian.org/unstable/newpkg_main.en.rdf},
 @xref{RSS}.
@@ -4426,7 +4426,7 @@ A select method can be very long, like:
 
 @lisp
 (nntp "gmane"
-      (nntp-address "news.gmane.org")
+      (nntp-address "news.gmane.io")
       (nntp-end-of-line "\n")
       (nntp-open-connection-function
        nntp-open-via-rlogin-and-telnet)
@@ -7279,7 +7279,7 @@ The server has to support @acronym{NOV} for any of this 
to work.
 @cindex Gmane, @code{gnus-fetch-old-headers}
 This feature can seriously impact performance it ignores all locally
 cached header entries.  Setting it to @code{t} for groups for a server
-that doesn't expire articles (such as news.gmane.org), leads to very
+that doesn't expire articles (such as news.gmane.io), leads to very
 slow summary generation.
 
 @item gnus-fetch-old-ephemeral-headers
@@ -18020,7 +18020,7 @@ Here is an example:
  (nnselect-args
   . [["nnimap+work:mail" 595 100]
      ["nnimap+home:sent" 223 100]
-     ["nntp+news.gmane.org:gmane.emacs.gnus.general" 23666 100]]))
+     ["nntp+news.gmane.io:gmane.emacs.gnus.general" 23666 100]]))
 @end lisp
 
 The function is the identity and the argument is just the list of
@@ -25155,9 +25155,9 @@ groups as spam and reports the to Gmane at group exit:
 @end lisp
 
 Additionally, I use @code{(setq spam-report-gmane-use-article-number nil)}
-because I don't read the groups directly from news.gmane.org, but
+because I don't read the groups directly from news.gmane.io, but
 through my local news server (leafnode).  I.e., the article numbers are
-not the same as on news.gmane.org, thus @code{spam-report.el} has to check
+not the same as on news.gmane.io, thus @code{spam-report.el} has to check
 the @code{X-Report-Spam} header to find the correct number.
 
 @node Spam Back Ends
@@ -25341,7 +25341,7 @@ added to a group's @code{spam-process} parameter, the 
spam-marked
 articles groups will be reported to the Gmane administrators via a
 HTTP request.
 
-Gmane was formerly found at @uref{http://gmane.org}.
+Gmane is at @uref{https://gmane.io}.
 
 @emph{WARNING}
 
@@ -28808,7 +28808,7 @@ be read correctly in Emacs 22 and below.  If you want 
to use Gnus across
 different Emacs versions, you may set @code{mm-auto-save-coding-system}
 to @code{emacs-mule}.
 @c FIXME: Untested.  (Or did anyone test it?)
-@c Cf. http://thread.gmane.org/gmane.emacs.gnus.general/66251/focus=66344
+@c Cf. http://thread.gmane.org/gmane.emacs.gnus.general/66251/focus=66344 
[dead link]
 
 @item Lisp files are now installed in @file{.../site-lisp/gnus/} by default.
 It defaulted to @file{.../site-lisp/} formerly.  In addition to this,
@@ -29321,13 +29321,13 @@ have names like @samp{gnu.emacs.gnus}.
 You can also have any number of foreign groups active at the same
 time.  These are groups that use non-native non-secondary back ends
 for getting news.  Foreign groups have names like
-@samp{nntp+news.gmane.org:gmane.emacs.gnus.devel}.
+@samp{nntp+news.gmane.io:gmane.emacs.gnus.devel}.
 
 @item secondary
 @cindex secondary
 Secondary back ends are somewhere half-way between being native and
 being foreign, but they mostly act like they are native, but they, too
-have names like @samp{nntp+news.gmane.org:gmane.emacs.gnus.devel}.
+have names like @samp{nntp+news.gmane.io:gmane.emacs.gnus.devel}.
 
 @item article
 @cindex article
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 41ab9cc4c5e..f59023eae62 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -228,6 +228,12 @@ with a legitimate use for this option likely also 
possesses the
 knowledge to rig up a suitable analog with minimal effort.  That said,
 the road to removal is long.
 
+** The 'track' module always ignores date stamps.
+Users of the stamp module who leave 'erc-insert-timestamp-function'
+set to its default of 'erc-insert-timestamp-left-and-right' will find
+that date stamps no longer affect the mode line, even for IRC commands
+not included in 'erc-track-exclude-types'.
+
 ** Option 'erc-warn-about-blank-lines' is more informative.
 Enabled by default, this option now produces more useful feedback
 whenever ERC rejects prompt input containing whitespace-only lines.
@@ -348,7 +354,9 @@ leading portion of message bodies as well as special casing 
to act on
 these areas without inflicting collateral damage.  It may also be
 worth noting that as consequence of these changes, the internally
 managed variable 'erc-timestamp-last-inserted-left' no longer records
-the final trailing newline in 'erc-timestamp-format-left'.
+the final trailing newline in 'erc-timestamp-format-left'.  If you
+must, see variable 'erc-stamp-prepend-date-stamps-p' for a temporary
+escape hatch.
 
 *** The role of a module's Custom group is now more clearly defined.
 Associating built-in modules with Custom groups and provided library
diff --git a/etc/NEWS b/etc/NEWS
index 05fd1b7a390..ed9f1a2124c 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -961,6 +961,11 @@ For links in 'webjump-sites' without an explicit URI 
scheme, it was
 previously assumed that they should be prefixed with "http://";.  Such
 URIs are now prefixed with "https://"; instead.
 
+** Customize
++++
+*** New command customize-dirlocals
+This command pops up a buffer to edit the settings in .dir-locals.el
+
 
 * New Modes and Packages in Emacs 30.1
 
diff --git a/etc/publicsuffix.txt b/etc/publicsuffix.txt
index 456b8aeaf0b..956110851a4 100644
--- a/etc/publicsuffix.txt
+++ b/etc/publicsuffix.txt
@@ -14414,7 +14414,7 @@ alpha.bounty-full.com
 beta.bounty-full.com
 
 // Smallregistry by Promopixel SARL: https://www.smallregistry.net
-// Former AFNIC's SLDs 
+// Former AFNIC's SLDs
 // Submitted by Jérôme Lipowicz <support@promopixel.com>
 aeroport.fr
 avocat.fr
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
index 7662186a0eb..d7a37a8d57f 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -152,6 +152,10 @@ public final class EmacsWindow extends EmacsHandleObject
   /* The position of this window relative to the root window.  */
   public int xPosition, yPosition;
 
+  /* The position of the last drag and drop event received; both
+     values are -1 if no drag and drop operation is under way.  */
+  private int dndXPosition, dndYPosition;
+
   public
   EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
               int width, int height, boolean overrideRedirect)
@@ -202,6 +206,9 @@ public final class EmacsWindow extends EmacsHandleObject
            return size () > 10;
          }
        };
+
+    dndXPosition = -1;
+    dndYPosition = -1;
   }
 
   public void
@@ -1617,11 +1624,26 @@ public final class EmacsWindow extends EmacsHandleObject
        return true;
 
       case DragEvent.ACTION_DRAG_LOCATION:
-       /* Send this drag motion event to Emacs.  */
-       EmacsNative.sendDndDrag (handle, x, y);
+       /* Send this drag motion event to Emacs.  Skip this when the
+          integer position hasn't changed, for Android sends events
+          even if the movement from the previous position of the drag
+          is less than 1 pixel on either axis.  */
+
+       if (x != dndXPosition || y != dndYPosition)
+         {
+           EmacsNative.sendDndDrag (handle, x, y);
+           dndXPosition = x;
+           dndYPosition = y;
+         }
+
        return true;
 
       case DragEvent.ACTION_DROP:
+       /* Reset this view's record of the previous drag and drop
+          event's position.  */
+       dndXPosition = -1;
+       dndYPosition = -1;
+
        /* Judge whether this is plain text, or if it's a file URI for
           which permissions must be requested.  */
 
@@ -1706,8 +1728,13 @@ public final class EmacsWindow extends EmacsHandleObject
 
        if (builder.length () > 0)
          EmacsNative.sendDndUri (handle, x, y, builder.toString ());
-
        return true;
+
+      default:
+       /* Reset this view's record of the previous drag and drop
+          event's position.  */
+       dndXPosition = -1;
+       dndYPosition = -1;
       }
 
     return true;
diff --git a/lisp/ChangeLog.12 b/lisp/ChangeLog.12
index 9bc440626dc..88d3a41461c 100644
--- a/lisp/ChangeLog.12
+++ b/lisp/ChangeLog.12
@@ -6789,8 +6789,8 @@
 2006-09-04  Daiki Ueno  <ueno@unixuser.org>
 
        * pgg-gpg.el (pgg-gpg-process-region): Revert two patches from Satyaki
-       Das.  http://article.gmane.org/gmane.emacs.gnus.general/49947
-       http://article.gmane.org/gmane.emacs.gnus.general/50457
+       Das.  http://article.gmane.org/gmane.emacs.gnus.general/49947 [dead 
link]
+       http://article.gmane.org/gmane.emacs.gnus.general/50457 [dead link]
 
 2006-09-03  Chong Yidong  <cyd@stupidchicken.com>
 
diff --git a/lisp/ChangeLog.14 b/lisp/ChangeLog.14
index 8c965abd98a..efaac2869c8 100644
--- a/lisp/ChangeLog.14
+++ b/lisp/ChangeLog.14
@@ -15985,7 +15985,7 @@
        (imap-message-copyuid-1): Use it.
        (imap-message-appenduid-1): Likewise.  Based on patch by Nathan
        J. Williams in
-       <http://permalink.gmane.org/gmane.emacs.gnus.general/65855>.
+       <http://permalink.gmane.org/gmane.emacs.gnus.general/65855>. [dead link]
 
 2008-04-02  Alan Mackenzie  <acm@muc.de>
 
diff --git a/lisp/ChangeLog.15 b/lisp/ChangeLog.15
index f0c613f37c7..c6026b8b9a8 100644
--- a/lisp/ChangeLog.15
+++ b/lisp/ChangeLog.15
@@ -14019,7 +14019,7 @@
        * font-lock.el (font-lock-refresh-defaults): New function, which
        can be used to let font-lock react to external changes in
        variables like font-lock-defaults and keywords.
-       See http://thread.gmane.org/gmane.emacs.devel/118777/focus=118802
+       See http://thread.gmane.org/gmane.emacs.devel/118777/focus=118802 [dead 
link]
 
 2009-12-28  Dan Nicolaescu  <dann@ics.uci.edu>
 
diff --git a/lisp/ChangeLog.16 b/lisp/ChangeLog.16
index 0006383c1fb..39649ec8ae0 100644
--- a/lisp/ChangeLog.16
+++ b/lisp/ChangeLog.16
@@ -2573,7 +2573,7 @@
 
        * progmodes/grep.el (rgrep): Escape command line.  Sometimes, it
        is too long for Tramp.  See discussion in
-       <http://thread.gmane.org/gmane.emacs.tramp/8233/focus=8244>.
+       <http://thread.gmane.org/gmane.emacs.tramp/8233/focus=8244>. [dead link]
 
        * progmodes/compile.el (compilation-start): Remove line escape
        template.
diff --git a/lisp/auth-source.el b/lisp/auth-source.el
index 365f6697ec8..583b6e57897 100644
--- a/lisp/auth-source.el
+++ b/lisp/auth-source.el
@@ -387,7 +387,6 @@ soon as a function returns non-nil.")
       (cond
        ((equal extension "plist")
         (auth-source-backend
-         source
          :source source
          :type 'plstore
          :search-function #'auth-source-plstore-search
@@ -395,13 +394,11 @@ soon as a function returns non-nil.")
          :data (plstore-open source)))
        ((member-ignore-case extension '("json"))
         (auth-source-backend
-         source
          :source source
          :type 'json
          :search-function #'auth-source-json-search))
        (t
         (auth-source-backend
-         source
          :source source
          :type 'netrc
          :search-function #'auth-source-netrc-search
@@ -449,7 +446,6 @@ soon as a function returns non-nil.")
         (setq source (symbol-name source)))
 
       (auth-source-backend
-       (format "Mac OS Keychain (%s)" source)
        :source source
        :type keychain-type
        :search-function #'auth-source-macos-keychain-search
@@ -490,7 +486,6 @@ soon as a function returns non-nil.")
 
       (if (featurep 'secrets)
           (auth-source-backend
-           (format "Secrets API (%s)" source)
            :source source
            :type 'secrets
            :search-function #'auth-source-secrets-search
@@ -498,7 +493,6 @@ soon as a function returns non-nil.")
         (auth-source-do-warn
          "auth-source-backend-parse: no Secrets API, ignoring spec: %S" entry)
         (auth-source-backend
-         (format "Ignored Secrets API (%s)" source)
          :source ""
          :type 'ignore))))))
 
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index 953b8b8b80f..6442ffeac24 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -512,6 +512,13 @@ WIDGET is the widget to apply the filter entries of MENU 
on."
        (push name result)))
     (nreverse result)))
 
+(defun custom--editable-field-p (widget)
+  "Non-nil if WIDGET is an editable-field widget, or inherits from it."
+  (let ((type (widget-type widget)))
+    (while (and type (not (eq type 'editable-field)))
+      (setq type (widget-type (get type 'widget-type))))
+    type))
+
 ;;; Unlispify.
 
 (defvar custom-prefix-list nil
@@ -5692,6 +5699,288 @@ This stores EXP (without evaluating it) as the saved 
spec for SYMBOL."
           (prin1 value (current-buffer)))
         (insert ")\n")))))
 
+;;; Directory Local Variables.
+;; The following code provides an Easy Customization interface to manage
+;; `.dir-locals.el' files.
+;; The main command is `customize-dirlocals'.  It presents a Custom-like buffer
+;; but with a few tweaks.  Variables are inserted in a repeat widget, and
+;; update its associated widget (the one for editing the value) upon the user
+;; hitting RET or TABbing out of it.
+;; This is unlike the `cus-theme.el' interface for editing themes, that prompts
+;; the user for the variable to then create the appropriate widget.
+(defvar-local custom-dirlocals-widget nil
+  "Widget that holds the dir-locals customizations.")
+
+(defvar-local custom-dirlocals-file-widget nil
+  "Widget that holds the name of the dir-locals file being customized.")
+
+(defvar-keymap custom-dirlocals-map
+  :doc "Keymap used in the \"*Customize Dirlocals*\" buffer."
+  :full t
+  :parent widget-keymap
+  "SPC"     #'scroll-up-command
+  "S-SPC"   #'scroll-down-command
+  "DEL"     #'scroll-down-command
+  "C-x C-s" #'Custom-dirlocals-save
+  "q"       #'Custom-buffer-done
+  "n"       #'widget-forward
+  "p"       #'widget-backward)
+
+(defvar custom-dirlocals-field-map
+  (let ((map (copy-keymap custom-field-keymap)))
+    (define-key map "\C-x\C-s" #'Custom-dirlocals-save)
+    (define-key map "\C-m" #'widget-field-activate)
+    map)
+  "Keymap for the editable fields in the \"*Customize Dirlocals*\" buffer .")
+
+(defvar custom-dirlocals-commands
+  '((" Save Settings " Custom-dirlocals-save t
+     "Save Settings to the dir-locals file." "save" "Save" t)
+    (" Undo Edits " Custom-dirlocals-revert-buffer t
+     "Revert buffer, undoing any editions."
+     "refresh" "Undo" t)
+    (" Help for Customize " Custom-help t "Get help for using Customize."
+     "help" "Help" t)
+    (" Exit " Custom-buffer-done t "Exit Customize." "exit" "Exit" t))
+  "Alist of specifications for Customize menu items, tool bar icons and 
buttons.
+See `custom-commands' for further explanation.")
+
+(easy-menu-define
+  Custom-dirlocals-menu (list custom-dirlocals-map
+                              custom-dirlocals-field-map)
+  "Menu used in dirlocals customization buffers."
+  (nconc (list "Custom"
+               (customize-menu-create 'customize))
+         (mapcar (lambda (arg)
+                   (let ((tag     (nth 0 arg))
+                         (command (nth 1 arg))
+                         (visible (nth 2 arg))
+                         (help    (nth 3 arg))
+                         (active  (nth 6 arg)))
+                     (vector tag command :visible (eval visible)
+                             :active `(eq t ',active)
+                             :help help)))
+                 custom-dirlocals-commands)))
+
+(defvar custom-dirlocals-tool-bar-map nil
+  "Keymap for the toolbar in \"*Customize Dirlocals*\" buffer.")
+
+(define-widget 'custom-dirlocals-key 'menu-choice
+  "Menu to choose between possible keys in a dir-locals file.
+
+Possible values are nil, a symbol (standing for a major mode) or a directory
+name."
+  :tag "Specification"
+  :value nil
+  :help-echo "Select a key for the dir-locals specification."
+  :args '((const :tag "All modes" nil)
+          (symbol :tag "Major mode" fundamental-mode)
+          (directory :tag "Subdirectory")))
+
+(define-widget 'custom-dynamic-cons 'cons
+  "A cons widget that changes its 2nd type based on the 1st type."
+  :value-create #'custom-dynamic-cons-value-create)
+
+(defun custom-dynamic-cons-value-create (widget)
+  "Select an appropriate 2nd type for the cons WIDGET and create WIDGET.
+
+The appropriate types are:
+- A symbol, if the value to represent is a minor-mode.
+- A boolean, if the value to represent is either the unibyte value or the
+  subdirs value.
+- A widget type suitable for editing a variable, in case of specifying a
+  variable's value.
+- A sexp widget, if none of the above happens."
+  (let* ((args (widget-get widget :args))
+         (value (widget-get widget :value))
+         (val (car value)))
+    (cond
+     ((eq val 'mode) (setf (nth 1 args)
+                           '(symbol :keymap custom-dirlocals-field-map
+                                    :tag "Minor mode")))
+     ((eq val 'unibyte) (setf (nth 1 args) '(boolean)))
+     ((eq val 'subdirs) (setf (nth 1 args) '(boolean)))
+     ((custom-variable-p val)
+      (let ((w (widget-convert (custom-variable-type val))))
+        (when (custom--editable-field-p w)
+          (widget-put w :keymap custom-dirlocals-field-map))
+        (setf (nth 1 args) w)))
+     (t (setf (nth 1 args) '(sexp :keymap custom-dirlocals-field-map))))
+    (widget-put (nth 0 args) :keymap custom-dirlocals-field-map)
+    (widget-group-value-create widget)))
+
+(defun custom-dirlocals-maybe-update-cons ()
+  "If focusing out from the first widget in a cons widget, update its value."
+  (when-let ((w (widget-at)))
+    (when (widget-get w :custom-dirlocals-symbol)
+      (widget-value-set (widget-get w :parent)
+                        (cons (widget-value w) ""))
+      (widget-setup))))
+
+(define-widget 'custom-dirlocals 'editable-list
+  "An editable list to edit settings in a dir-locals file."
+  :entry-format "%i %d %v"
+  :insert-button-args '(:help-echo "Insert new specification here.")
+  :append-button-args '(:help-echo "Append new specification here.")
+  :delete-button-args '(:help-echo "Delete this specification.")
+  :args '((group :format "%v"
+                 custom-dirlocals-key
+                 (repeat
+                  :tag "Settings"
+                  :inline t
+                  (custom-dynamic-cons
+                   :tag "Setting"
+                   (symbol :action custom-dirlocals-symbol-action
+                           :custom-dirlocals-symbol t)
+                   ;; Will change according to the option being customized.
+                   (sexp :tag "Value"))))))
+
+(defun custom-dirlocals-symbol-action (widget &optional _event)
+  "Action for the symbol WIDGET.
+
+Sets the value of its parent, a cons widget, in order to create an
+appropriate widget to edit the value of WIDGET.
+
+Moves point into the widget that holds the value."
+  (setq widget (or widget (widget-at)))
+  (widget-value-set (widget-get widget :parent)
+                    (cons (widget-value widget) ""))
+  (widget-setup)
+  (widget-forward 1))
+
+(defun custom-dirlocals-change-file (widget &optional _event)
+  "Switch to a buffer to customize the dir-locals file in WIDGET."
+  (customize-dirlocals (expand-file-name (widget-value widget))))
+
+(defun custom-dirlocals--set-widget-vars ()
+  "Set local variables for the Widget library."
+  (custom--initialize-widget-variables)
+  (add-hook 'widget-forward-hook #'custom-dirlocals-maybe-update-cons nil t))
+
+(defmacro custom-dirlocals-with-buffer (&rest body)
+  "Arrange to execute BODY in a \"*Customize Dirlocals*\" buffer."
+  ;; We don't use `custom-buffer-create' because the settings here
+  ;; don't go into the `custom-file'.
+  `(progn
+     (switch-to-buffer "*Customize Dirlocals*")
+     (kill-all-local-variables)
+     (let ((inhibit-read-only t))
+       (erase-buffer))
+     (remove-overlays)
+     (custom-dirlocals--set-widget-vars)
+     ,@body
+     (setq-local tool-bar-map
+                 (or custom-dirlocals-tool-bar-map
+                     ;; Set up `custom-dirlocals-tool-bar-map'.
+                     (let ((map (make-sparse-keymap)))
+                       (mapc
+                        (lambda (arg)
+                          (tool-bar-local-item-from-menu
+                           (nth 1 arg) (nth 4 arg) map custom-dirlocals-map
+                           :label (nth 5 arg)))
+                        custom-dirlocals-commands)
+                       (setq custom-dirlocals-tool-bar-map map))))
+     (setq-local revert-buffer-function #'Custom-dirlocals-revert-buffer)
+     (use-local-map custom-dirlocals-map)
+     (widget-setup)))
+
+(defun custom-dirlocals-get-options ()
+  "Return all options inside a custom-dirlocals widget."
+  (let* ((groups (widget-get custom-dirlocals-widget :children))
+         (repeats (mapcar (lambda (group)
+                            (nth 1 (widget-get group :children)))
+                          groups)))
+    (mapcan (lambda (repeat)
+              (mapcar (lambda (w)
+                        (nth 1 (widget-get w :children)))
+                      (widget-get repeat :children)))
+            repeats)))
+
+(defun custom-dirlocals-validate ()
+  "Non-nil if all customization options validate.
+
+If at least an option doesn't validate, signals an error and moves point
+to the widget with the invalid value."
+  (dolist (opt (custom-dirlocals-get-options))
+    (when-let ((w (widget-apply opt :validate)))
+      (goto-char (widget-get w :from))
+      (error "%s" (widget-get w :error))))
+  t)
+
+(defun Custom-dirlocals-revert-buffer (&rest _ignored)
+  "Revert the buffer for Directory Local Variables customization."
+  (interactive)
+  (customize-dirlocals (widget-get custom-dirlocals-file-widget :value)))
+
+(defun Custom-dirlocals-save (&rest _ignore)
+  "Save the settings to the dir-locals file being customized."
+  (interactive)
+  (when (custom-dirlocals-validate)
+    (let* ((file (widget-value custom-dirlocals-file-widget))
+           (old (widget-get custom-dirlocals-widget :value))
+           (dirlocals (widget-value custom-dirlocals-widget)))
+      (dolist (spec old)
+        (let ((mode (car spec))
+              (settings (cdr spec)))
+          (dolist (setting settings)
+            (delete-dir-local-variable mode (car setting) file))))
+      (dolist (spec dirlocals)
+        (let ((mode (car spec))
+              (settings (cdr spec)))
+          (dolist (setting (reverse settings))
+            (when (memq (car setting) '(mode eval))
+              (delete-dir-local-variable mode (car setting) file))
+            (add-dir-local-variable mode (car setting) (cdr setting) file)))))
+    ;; Write the dir-locals file and kill its buffer, to come back to
+    ;; our own buffer.
+    (write-file (expand-file-name buffer-file-name) nil)
+    (kill-buffer)))
+
+;;;###autoload
+(defun customize-dirlocals (&optional filename)
+  "Customize Directory Local Variables in the current directory.
+
+With optional argument FILENAME non-nil, customize the `.dir-locals.el' file
+that FILENAME specifies."
+  (interactive)
+  (let* ((file (or filename (expand-file-name ".dir-locals.el")))
+         (dirlocals (when (file-exists-p file)
+                      (with-current-buffer (find-file-noselect file)
+                        (goto-char (point-min))
+                        (prog1
+                            (condition-case _
+                                (read (current-buffer))
+                              (end-of-file nil))
+                          (kill-buffer))))))
+    (custom-dirlocals-with-buffer
+     (widget-insert
+      "This buffer is for customizing the Directory Local Variables in:\n")
+     (setq custom-dirlocals-file-widget
+           (widget-create `(file :action ,#'custom-dirlocals-change-file
+                                 ,file)))
+     (widget-insert
+      (substitute-command-keys
+       "
+To select another file, edit the above field and hit RET.
+
+After you enter a user option name under the symbol field,
+be sure to press \\`RET' or \\`TAB', so that the field that holds the
+value changes to an appropriate field for the option.
+
+Type \\`C-x C-s' when you've finished editing it, to save the
+settings to the file."))
+     (widget-insert "\n\n\n")
+     (widget-create 'push-button :tag " Revert "
+                    :action #'Custom-dirlocals-revert-buffer)
+     (widget-insert " ")
+     (widget-create 'push-button :tag " Save Settings "
+                    :action #'Custom-dirlocals-save)
+     (widget-insert "\n\n")
+     (setq custom-dirlocals-widget
+           (widget-create 'custom-dirlocals :value dirlocals))
+     (setq default-directory (file-name-directory file))
+     (goto-char (point-min)))))
+
 (provide 'cus-edit)
 
 ;;; cus-edit.el ends here
diff --git a/lisp/dired.el b/lisp/dired.el
index cc8c74839b9..231d305210b 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -1681,7 +1681,9 @@ see `dired-use-ls-dired' for more details.")
       (cond (dir-wildcard
              (setq switches (concat "-d " switches))
              (let* ((default-directory (car dir-wildcard))
-                    (script (format "ls %s %s" switches (cdr dir-wildcard)))
+                    (script (format "%s %s %s"
+                                    insert-directory-program
+                                    switches (cdr dir-wildcard)))
                     (sh (or (and remotep "/bin/sh")
                             (executable-find shell-file-name)
                             (executable-find "sh")))
diff --git a/lisp/dnd.el b/lisp/dnd.el
index c27fdeb7745..936534fa32c 100644
--- a/lisp/dnd.el
+++ b/lisp/dnd.el
@@ -42,10 +42,11 @@
 
 ;;;###autoload
 (defcustom dnd-protocol-alist
-  `((,(purecopy "^file:///")  . dnd-open-local-file)   ; XDND format.
-    (,(purecopy "^file://")   . dnd-open-file)         ; URL with host
-    (,(purecopy "^file:")     . dnd-open-local-file)   ; Old KDE, Motif, Sun
-    (,(purecopy "^\\(https?\\|ftp\\|file\\|nfs\\)://") . dnd-open-file))
+  `((,(purecopy "^file:///")    . dnd-open-local-file) ; XDND format.
+    (,(purecopy "^file://[^/]") . dnd-open-file)       ; URL with host
+    (,(purecopy "^file:/[^/]")  . dnd-open-local-file) ; Old KDE, Motif, Sun
+    (,(purecopy "^file:[^/]")   . dnd-open-local-file) ; MS-Windows
+    (,(purecopy "^\\(https?\\|ftp\\|nfs\\)://") . dnd-open-file))
   "The functions to call for different protocols when a drop is made.
 This variable is used by `dnd-handle-multiple-urls'.
 The list contains of (REGEXP . FUNCTION) pairs.
@@ -59,7 +60,7 @@ is a pair of (REGEXP . FUNCTION), those regexps are tried for 
a match.
 If no match is found, the URL is inserted as text by calling `dnd-insert-text'.
 The function shall return the action done (move, copy, link or private)
 if some action was made, or nil if the URL is ignored."
-  :version "22.1"
+  :version "30.1"
   :type '(repeat (cons (regexp) (function)))
   :group 'dnd)
 
@@ -223,7 +224,8 @@ for it will be modified."
                               (let ((cell (cons handler nil)))
                                 (push cell list)
                                 cell))))
-                (setcdr cell (cons uri (cdr cell))))))))
+                (unless (memq uri cell)
+                  (setcdr cell (cons uri (cdr cell)))))))))
       (setq list (nreverse list))
       ;; While unassessed handlers still exist...
       (while list
diff --git a/lisp/erc/erc-fill.el b/lisp/erc/erc-fill.el
index e28c3563ebf..e8f3f624ff1 100644
--- a/lisp/erc/erc-fill.el
+++ b/lisp/erc/erc-fill.el
@@ -145,10 +145,6 @@ Its value should be larger than that of the variable
   :package-version '(ERC . "5.6") ; FIXME sync on release
   :type '(choice (const nil) number))
 
-(defvar erc-fill--spaced-commands '(PRIVMSG NOTICE)
-  "Types of messages to add space between on graphical displays.
-Only considered when `erc-fill-line-spacing' is non-nil.")
-
 (defvar-local erc-fill--function nil
   "Internal copy of `erc-fill-function'.
 Takes precedence over the latter when non-nil.")
@@ -175,11 +171,11 @@ You can put this on `erc-insert-modify-hook' and/or 
`erc-send-modify-hook'."
           (when-let* ((erc-fill-line-spacing)
                       (p (point-min)))
             (widen)
-            (when (or (erc--check-msg-prop 'erc-cmd erc-fill--spaced-commands)
-                      (and-let* ((cmd (save-excursion
-                                        (forward-line -1)
-                                        (get-text-property (point) 'erc-cmd))))
-                        (memq cmd erc-fill--spaced-commands)))
+            (when (or (erc--check-msg-prop 'erc-msg 'msg)
+                      (and-let* ((m (save-excursion
+                                      (forward-line -1)
+                                      (erc--get-inserted-msg-prop 'erc-msg))))
+                        (eq 'msg m)))
               (put-text-property (1- p) p
                                  'line-spacing erc-fill-line-spacing))))))))
 
@@ -463,6 +459,7 @@ is not recommended."
    (kill-local-variable 'erc-fill--wrap-value)
    (kill-local-variable 'erc-fill--function)
    (kill-local-variable 'erc-fill--wrap-visual-keys)
+   (kill-local-variable 'erc-fill--wrap-last-msg)
    (remove-hook 'erc-button--prev-next-predicate-functions
                 #'erc-fill--wrap-merged-button-p t))
   'local)
@@ -479,13 +476,17 @@ variable can be converted to a public one if needed by 
third
 parties.")
 
 (defvar-local erc-fill--wrap-last-msg nil)
-(defvar-local erc-fill--wrap-max-lull (* 24 60 60))
+(defvar erc-fill--wrap-max-lull (* 24 60 60))
 
 (defun erc-fill--wrap-continued-message-p ()
   "Return non-nil when the current speaker hasn't changed.
 That is, indicate whether the text just inserted is from the same
 sender as that of the previous \"PRIVMSG\"."
-  (prog1 (and-let*
+  (and
+   (not (erc--check-msg-prop 'erc-ephemeral))
+   (progn ; preserve blame for now, unprogn on next major change
+     (prog1
+         (and-let*
              ((m (or erc-fill--wrap-last-msg
                      (setq erc-fill--wrap-last-msg (point-min-marker))
                      nil))
@@ -493,8 +494,9 @@ sender as that of the previous \"PRIVMSG\"."
               (props (save-restriction
                        (widen)
                        (and-let*
-                           (((eq 'PRIVMSG (get-text-property m 'erc-cmd)))
-                            ((not (eq (get-text-property m 'erc-msg) 'ACTION)))
+                           (((eq 'msg (get-text-property m 'erc-msg)))
+                            ((not (eq (get-text-property m 'erc-ctcp)
+                                      'ACTION)))
                             ((not (invisible-p m)))
                             (spr (next-single-property-change m 'erc-speaker)))
                          (cons (get-text-property m 'erc-ts)
@@ -509,7 +511,7 @@ sender as that of the previous \"PRIVMSG\"."
               ((not (erc--check-msg-prop 'erc-ctcp 'ACTION)))
               (nick (get-text-property speaker 'erc-speaker))
               ((erc-nick-equal-p props nick))))
-    (set-marker erc-fill--wrap-last-msg (point-min))))
+       (set-marker erc-fill--wrap-last-msg (point-min))))))
 
 (defun erc-fill--wrap-measure (beg end)
   "Return display spec width for inserted region between BEG and END.
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index 9d70c644429..4cc81dd9378 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -119,28 +119,20 @@ may be nil, is the number of lines between `window-start' 
and
   "Commands to skip instead of force-scroll on `post-command-hook'.")
 
 (defun erc--scrolltobottom-on-post-command ()
-  "Restore window start or scroll to prompt and recenter.
-When `erc--scrolltobottom-window-info' is non-nil and its first
-item is associated with the selected window, restore start of
-window so long as prompt hasn't moved.  Expect buffer to be
-unnarrowed."
+  "Scroll selected window unless `this-command' is exempted."
   (when (eq (selected-window) (get-buffer-window))
     (unless (memq this-command erc--scrolltobottom-post-ignore-commands)
-      (erc--scrolltobottom-confirm))
-    (setq erc--scrolltobottom-window-info nil)))
+      (setq erc--scrolltobottom-window-info nil)
+      (erc--scrolltobottom-confirm))))
 
 ;; It may be desirable to also restore the relative line position of
 ;; window point after changing dimensions.  Perhaps stashing the
 ;; previous ratio of window line to body height and later recentering
 ;; proportionally would achieve this.
-(defun erc--scrolltobottom-at-prompt-minibuffer-active ()
+(defun erc--scrolltobottom-on-win-conf-change ()
   "Scroll window to bottom when at prompt and using the minibuffer."
-  ;; This is redundant or ineffective in the selected window if at
-  ;; prompt or if only one window exists.
-  (unless (or (input-pending-p)
-              (and (minibuffer-window-active-p (minibuffer-window))
-                   (eq (old-selected-window) (minibuffer-window))))
-    (erc--scrolltobottom-confirm)))
+  (setq erc--scrolltobottom-window-info nil)
+  (erc--scrolltobottom-confirm))
 
 (defun erc--scrolltobottom-all (&rest _)
   "Maybe put prompt on last line in all windows displaying current buffer.
@@ -176,17 +168,20 @@ function used `window-scroll-functions', which was 
replaced by
       (if erc-scrolltobottom-all
           (progn
             (setq-local read-minibuffer-restore-windows nil)
+            (when (zerop scroll-conservatively)
+              (setq-local scroll-step 1))
             (unless (eq erc-scrolltobottom-all 'relaxed)
               (add-hook 'window-configuration-change-hook
-                        #'erc--scrolltobottom-at-prompt-minibuffer-active 50 t)
+                        #'erc--scrolltobottom-on-win-conf-change 50 t)
               (add-hook 'post-command-hook
                         #'erc--scrolltobottom-on-post-command 50 t)))
         (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t))
     (remove-hook 'post-command-hook #'erc-scroll-to-bottom t)
     (remove-hook 'post-command-hook #'erc--scrolltobottom-on-post-command t)
     (remove-hook 'window-configuration-change-hook
-                 #'erc--scrolltobottom-at-prompt-minibuffer-active t)
+                 #'erc--scrolltobottom-on-win-conf-change t)
     (kill-local-variable 'read-minibuffer-restore-windows)
+    (kill-local-variable 'scroll-step)
     (kill-local-variable 'erc--scrolltobottom-window-info)))
 
 (defun erc--scrolltobottom-on-pre-insert (_)
diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el
index b515513dcb7..b3812470a4d 100644
--- a/lisp/erc/erc-stamp.el
+++ b/lisp/erc/erc-stamp.el
@@ -243,7 +243,8 @@ or `erc-send-modify-hook'."
            (erc-stamp--invisible-property
             ;; FIXME on major version bump, make this `erc-' prefixed.
             (if invisible `(timestamp ,@(ensure-list invisible)) 'timestamp))
-           (skipp (and erc-stamp--skip-when-invisible invisible))
+           (skipp (or (and erc-stamp--skip-when-invisible invisible)
+                      (erc--check-msg-prop 'erc-ephemeral)))
            (erc-stamp--current-time ct))
       (when erc--msg-props
         (puthash 'erc-ts ct erc--msg-props))
@@ -638,7 +639,8 @@ printed just after each line's text (no alignment)."
 (defun erc-stamp--propertize-left-date-stamp ()
   (add-text-properties (point-min) (1- (point-max))
                        '(field erc-timestamp erc-stamp-type date-left))
-  (erc--hide-message 'timestamp))
+  (erc--hide-message 'timestamp)
+  (run-hooks 'erc-stamp--insert-date-hook))
 
 ;; A kludge to pass state from insert hook to nested insert hook.
 (defvar erc-stamp--current-datestamp-left nil)
@@ -665,19 +667,18 @@ printed just after each line's text (no alignment)."
   (cl-assert string)
   (let ((erc-stamp--skip t)
         (erc--msg-props (map-into `((erc-msg . datestamp)
-                                    (erc-ts . ,erc-stamp--current-time))
+                                    (erc-ts . ,(erc-stamp--current-time)))
                                   'hash-table))
-        (erc-send-modify-hook `(,@erc-send-modify-hook
-                                erc-stamp--propertize-left-date-stamp
-                                ,@erc-stamp--insert-date-hook))
         (erc-insert-modify-hook `(,@erc-insert-modify-hook
-                                  erc-stamp--propertize-left-date-stamp
-                                  ,@erc-stamp--insert-date-hook)))
+                                  erc-stamp--propertize-left-date-stamp))
+        ;; Don't run hooks that aren't expecting a narrowed buffer.
+        (erc-insert-pre-hook nil)
+        (erc-insert-done-hook nil))
     (erc-display-message nil nil (current-buffer) string)
     (setq erc-timestamp-last-inserted-left string)))
 
 (defun erc-stamp--lr-date-on-pre-modify (_)
-  (when-let ((ct (or erc-stamp--current-time (erc-stamp--current-time)))
+  (when-let ((ct (erc-stamp--current-time))
              (rendered (erc-stamp--format-date-stamp ct))
              ((not (string-equal rendered erc-timestamp-last-inserted-left)))
              (erc-stamp--current-datestamp-left rendered)
@@ -689,6 +690,16 @@ printed just after each line's text (no alignment)."
       (let (erc-timestamp-format erc-away-timestamp-format)
         (erc-add-timestamp)))))
 
+(defvar erc-stamp-prepend-date-stamps-p nil
+  "When non-nil, date stamps are not independent messages.
+Users should think twice about enabling this escape hatch.  It
+will likely degraded the user experience by causing post-5.5
+features, like `fill-wrap', dynamic invisibility, etc., to
+malfunction.  Basic support for the default configuration may
+expire earlier than normally expected.")
+(make-obsolete-variable 'erc-stamp-prepend-date-stamps-p
+                        "unsupported legacy behavior" "30.1")
+
 (defun erc-insert-timestamp-left-and-right (string)
   "Insert a stamp on either side when it changes.
 When the deprecated option `erc-timestamp-format-right' is nil,
@@ -703,7 +714,7 @@ requirements related to `erc-legacy-invisible-bounds-p'.
 Additionally, ensure every date stamp is identifiable as such so
 that internal modules can easily distinguish between other
 left-sided stamps and date stamps inserted by this function."
-  (unless erc-stamp--date-format-end
+  (unless (or erc-stamp--date-format-end erc-stamp-prepend-date-stamps-p)
     (add-hook 'erc-insert-pre-hook #'erc-stamp--lr-date-on-pre-modify -95 t)
     (add-hook 'erc-send-pre-functions #'erc-stamp--lr-date-on-pre-modify -95 t)
     (let ((erc--insert-marker (point-min-marker))
@@ -713,12 +724,19 @@ left-sided stamps and date stamps inserted by this 
function."
       (narrow-to-region erc--insert-marker end-marker)
       (set-marker end-marker nil)
       (set-marker erc--insert-marker nil)))
-  (let* ((ct (or erc-stamp--current-time (erc-stamp--current-time)))
+  (let* ((ct (erc-stamp--current-time))
          (ts-right (with-suppressed-warnings
                        ((obsolete erc-timestamp-format-right))
                      (if erc-timestamp-format-right
                          (erc-format-timestamp ct erc-timestamp-format-right)
                        string))))
+    ;; Maybe insert legacy date stamp.
+    (when-let ((erc-stamp-prepend-date-stamps-p)
+               (ts-left (erc-format-timestamp ct erc-timestamp-format-left))
+               ((not (string= ts-left erc-timestamp-last-inserted-left))))
+      (goto-char (point-min))
+      (erc-put-text-property 0 (length ts-left) 'field 'erc-timestamp ts-left)
+      (insert (setq erc-timestamp-last-inserted-left ts-left)))
     ;; insert right timestamp
     (let ((erc-timestamp-only-if-changed-flag t)
          (erc-timestamp-last-inserted erc-timestamp-last-inserted-right))
diff --git a/lisp/erc/erc-track.el b/lisp/erc/erc-track.el
index c8f2e04c3eb..a36b781e04d 100644
--- a/lisp/erc/erc-track.el
+++ b/lisp/erc/erc-track.el
@@ -785,6 +785,9 @@ that face with highest priority in NEW-FACES is also a 
member of
               choice))
         choice))))
 
+(defvar erc-track--skipped-msgs '(datestamp)
+  "Values of `erc-msg' text prop to ignore.")
+
 (defun erc-track-modified-channels ()
   "Hook function for `erc-insert-post-hook'.
 Check if the current buffer should be added to the mode line as a
@@ -798,10 +801,13 @@ the current buffer is in `erc-mode'."
                        ;; FIXME either use `erc--server-buffer-p' or
                        ;; explain why that's unwise.
                        (erc-server-or-unjoined-channel-buffer-p)))
-            (not (erc-message-type-member
-                  (or (erc-find-parsed-property)
-                      (point-min))
-                  erc-track-exclude-types)))
+             (not (let ((parsed (erc-find-parsed-property)))
+                    (or (erc-message-type-member (or parsed (point-min))
+                                                 erc-track-exclude-types)
+                        ;; Skip certain non-server-sent messages.
+                        (and (not parsed)
+                             (erc--check-msg-prop 'erc-msg
+                                                  erc-track--skipped-msgs))))))
        ;; If the active buffer is not visible (not shown in a
        ;; window), and not to be excluded, determine the kinds of
        ;; faces used in the current message, and unless the user
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 7d75ec49ccd..0471ee0bbb8 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -136,8 +136,62 @@ concerning buffers."
   :group 'erc)
 
 (defvar erc-message-parsed) ; only known to this file
-(defvar erc--msg-props nil)
-(defvar erc--msg-prop-overrides nil)
+
+(defvar erc--msg-props nil
+  "Hash table containing metadata properties for current message.
+Provided by the insertion functions `erc-display-message' and
+`erc-display-msg' while running their modification hooks.
+Initialized when null for each visitation round from function
+parameters and environmental factors, as well as the alist
+`erc--msg-prop-overrides'.  Keys are symbols.  Values are opaque
+objects, unless otherwise specified.  Items present after running
+`erc-insert-post-hook' or `erc-send-post-hook' become text
+properties added to the first character of an inserted message.
+A given message therefore spans the interval extending from one
+set of such properties to the newline before the next (or
+`erc-insert-marker').  As of ERC 5.6, this forms the basis for
+visiting and editing inserted messages.  Modules should align
+their markers accordingly.  The following properties have meaning
+as of ERC 5.6:
+
+ - `erc-msg': a symbol, guaranteed present; values include:
+
+   - `msg', signifying a `PRIVMSG' or an incoming `NOTICE'
+   - `self', a fallback used by `erc-display-msg' for callers
+     that don't specify an `erc-msg'
+   - `unknown', a similar fallback for `erc-display-message'
+   - a catalog key, such as `s401' or `finished'
+   - an `erc-display-message' TYPE parameter, like `notice'
+
+ - `erc-cmd': a message's associated IRC command, as read by
+   `erc--get-eq-comparable-cmd'; currently either a symbol, like
+   `PRIVMSG', or a number, like 5, which represents the numeric
+   \"005\"; absent on \"local\" messages, such as simple warnings
+   and help text, and on outgoing messages unless echoed back by
+   the server (assuming future support)
+
+ - `erc-ctcp': a CTCP command, like `ACTION'
+
+ - `erc-ts': a timestamp, possibly provided by the server; as of
+   5.6, a ticks/hertz pair on Emacs 29 and above, and a \"list\"
+   type otherwise; managed by the `stamp' module
+
+ - `erc-ephemeral': a symbol prefixed by or matching a module
+   name; indicates to other modules and members of modification
+   hooks that the current message should not affect stateful
+   operations, such as recording a channel's most recent speaker
+
+This is an internal API, and the selection of related helper
+utilities is fluid and provisional.  As of ERC 5.6, see the
+functions `erc--check-msg-prop' and `erc--get-inserted-msg-prop'.")
+
+(defvar erc--msg-prop-overrides nil
+  "Alist of \"message properties\" for populating `erc--msg-props'.
+These override any defaults normally shown to modification hooks
+by `erc-display-msg' and `erc-display-message'.  Modules should
+accommodate existing overrides when applicable.  Items toward the
+front shadow any that follow.  Ignored when `erc--msg-props' is
+already non-nil.")
 
 ;; Forward declarations
 (defvar tabbar--local-hlf)
@@ -2490,7 +2544,6 @@ ERC calls members with `erc-server-announced-name', 
falling back
 to the 376/422 message's \"sender\", as well as the current nick,
 as given by the 376/422 message's \"target\" parameter, which is
 typically the same as that reported by `erc-current-nick'."
-  :package-version '(ERC . "5.6") ; FIXME sync on release
   :group 'erc-hooks
   :type '(repeat function))
 
@@ -2899,9 +2952,9 @@ If ARG is non-nil, show the *erc-protocol* buffer."
   "Send CTCP ACTION information described by STR to TGT."
   (erc-send-ctcp-message tgt (format "ACTION %s" str) force)
   ;; Allow hooks that act on inserted PRIVMSG and NOTICES to process us.
-  (let ((erc--msg-prop-overrides '((erc-msg . msg)
-                                   (erc-cmd . PRIVMSG)
-                                   (erc-ctcp . ACTION)))
+  (let ((erc--msg-prop-overrides `((erc-msg . msg)
+                                   (erc-ctcp . ACTION)
+                                   ,@erc--msg-prop-overrides))
         (nick (erc-current-nick)))
     (setq nick (propertize nick 'erc-speaker nick))
     (erc-display-message nil '(t action input) (current-buffer)
@@ -2981,7 +3034,7 @@ POINT, search from POINT instead of `point'."
                           (and-let*
                               ((p (previous-single-property-change point
                                                                    'erc-msg)))
-                            (if (= p (1- point)) point (1- p)))))))
+                            (if (= p (1- point)) p (1- p)))))))
           ,@(and (member only '(nil 'end))
                  '((e (1- (next-single-property-change
                            (if at-start-p (1+ point) point)
@@ -3006,8 +3059,12 @@ Expect callers to know that this doesn't wrap BODY in
        ,@body)))
 
 (defun erc--traverse-inserted (beg end fn)
-  "Visit messages between BEG and END and run FN in narrowed buffer."
-  (setq end (min end (marker-position erc-insert-marker)))
+  "Visit messages between BEG and END and run FN in narrowed buffer.
+If END is a marker, possibly update its position."
+  (unless (markerp end)
+    (setq end (set-marker (make-marker) (or end erc-insert-marker))))
+  (unless (eq end erc-insert-marker)
+    (set-marker end (min erc-insert-marker end)))
   (save-excursion
     (goto-char beg)
     (let ((b (if (get-text-property (point) 'erc-msg)
@@ -3019,7 +3076,9 @@ Expect callers to know that this doesn't wrap BODY in
         (save-restriction
           (narrow-to-region b e)
           (funcall fn))
-        (setq b e)))))
+        (setq b e))))
+  (unless (eq end erc-insert-marker)
+    (set-marker end nil)))
 
 (defvar erc--insert-marker nil
   "Internal override for `erc-insert-marker'.")
@@ -3241,6 +3300,27 @@ don't bother including the preceding newline."
           (cl-incf beg))
         (erc--merge-prop (1- beg) (1- end) 'invisible value)))))
 
+(defun erc--delete-inserted-message (beg-or-point &optional end)
+  "Remove message between BEG and END.
+Expect BEG and END to match bounds as returned by the macro
+`erc--get-inserted-msg-bounds'.  Ensure all markers residing at
+the start of the deleted message end up at the beginning of the
+subsequent message."
+  (let ((beg beg-or-point))
+    (save-restriction
+      (widen)
+      (unless end
+        (setq end (erc--get-inserted-msg-bounds nil beg-or-point)
+              beg (pop end)))
+      (with-silent-modifications
+        (if erc-legacy-invisible-bounds-p
+            (delete-region beg (1+ end))
+          (save-excursion
+            (goto-char beg)
+            (insert-before-markers
+             (substring (delete-and-extract-region (1- (point)) (1+ end))
+                        -1))))))))
+
 (defvar erc--ranked-properties '(erc-msg erc-ts erc-cmd))
 
 (defun erc--order-text-properties-from-hash (table)
@@ -3528,9 +3608,9 @@ filling, and other effects."
                         table)
                (when cmd
                  (puthash 'erc-cmd cmd table))
-               (and erc--msg-prop-overrides
-                    (pcase-dolist (`(,k . ,v) erc--msg-prop-overrides)
-                      (puthash k v table)))
+               (and-let* ((ovs erc--msg-prop-overrides))
+                 (pcase-dolist (`(,k . ,v) (reverse ovs))
+                   (puthash k v table)))
                table)))
         (erc-message-parsed parsed))
     (setq string
@@ -5804,7 +5884,8 @@ See also `erc-display-message'."
           (let* ((type (upcase (car (split-string (car queries)))))
                  (hook (intern-soft (concat "erc-ctcp-query-" type "-hook")))
                  (erc--msg-prop-overrides `((erc-msg . msg)
-                                            (erc-ctcp . ,(intern type)))))
+                                            (erc-ctcp . ,(intern type))
+                                            ,@erc--msg-prop-overrides)))
             (if (and hook (boundp hook))
                 (if (string-equal type "ACTION")
                     (run-hook-with-args-until-success
@@ -6809,8 +6890,8 @@ ERC prints them as a single message joined by newlines.")
             (when-let (((not (erc--input-split-abortp state)))
                        (inhibit-read-only t)
                        (old-buf (current-buffer)))
-              (let ((erc--msg-prop-overrides '((erc-cmd . PRIVMSG)
-                                               (erc-msg . msg))))
+              (let ((erc--msg-prop-overrides `((erc-msg . msg)
+                                               ,@erc--msg-prop-overrides)))
                 (erc-set-active-buffer (current-buffer))
                 ;; Kill the input and the prompt
                 (delete-region erc-input-marker (erc-end-of-input-line))
@@ -6952,15 +7033,18 @@ Return non-nil only if we actually send anything."
           t)))))
 
 (defun erc-display-msg (line)
-  "Display LINE as a message of the user to the current target at point."
+  "Insert LINE into current buffer and run \"send\" hooks.
+Expect LINE to originate from input submitted interactively at
+the prompt, such as outgoing chat messages or echoed slash
+commands."
   (when erc-insert-this
     (save-excursion
       (erc--assert-input-bounds)
       (let ((insert-position (marker-position (goto-char erc-insert-marker)))
-            (erc--msg-props (or erc--msg-props
-                                (map-into (cons '(erc-msg . self)
-                                                erc--msg-prop-overrides)
-                                          'hash-table)))
+            (erc--msg-props (or erc--msg-props ; prefer `self' to `unknown'
+                                (let ((ovs erc--msg-prop-overrides))
+                                  (map-into `((erc-msg . self) ,@(reverse ovs))
+                                            'hash-table))))
             beg)
         (insert (erc-format-my-nick))
         (setq beg (point))
diff --git a/lisp/files-x.el b/lisp/files-x.el
index f6fbd44ce21..a8d525ec5ff 100644
--- a/lisp/files-x.el
+++ b/lisp/files-x.el
@@ -700,7 +700,8 @@ Return a reordered plist."
   "Return the connection profiles list for CRITERIA.
 CRITERIA is a plist identifying a connection and the application
 using this connection, see `connection-local-criteria-alist'."
-  (let (profiles)
+  (let ((criteria (connection-local-normalize-criteria criteria))
+        profiles)
     (dolist (crit-alist connection-local-criteria-alist)
       (let ((crit criteria)
             (match t))
diff --git a/lisp/gnus/ChangeLog.3 b/lisp/gnus/ChangeLog.3
index d0b195e5f13..0fc5c093371 100644
--- a/lisp/gnus/ChangeLog.3
+++ b/lisp/gnus/ChangeLog.3
@@ -578,7 +578,7 @@
 
        * gnus-start.el (gnus-dribble-read-file): Don't stop the auto-saving of
        the dribble buffer even when it is shrunk a lot.
-       <http://thread.gmane.org/gmane.emacs.gnus.user/16923>
+       <http://thread.gmane.org/gmane.emacs.gnus.user/16923> [dead link]
 
 2014-06-26  Glenn Morris  <rgm@gnu.org>
 
diff --git a/lisp/gnus/gnus-art.el b/lisp/gnus/gnus-art.el
index 6f201f9c3df..bd9a49eb6a5 100644
--- a/lisp/gnus/gnus-art.el
+++ b/lisp/gnus/gnus-art.el
@@ -1622,7 +1622,8 @@ predicate.  See Info node `(gnus)Customizing Articles'."
   "The protocol used for encrypt articles.
 It is a string, such as \"PGP\".  If nil, ask user."
   :version "22.1"
-  :type 'string
+  :type '(choice (const :tag "Ask me" nil)
+                 string)
   :group 'mime-security)
 
 (defcustom gnus-use-idna t
@@ -7560,10 +7561,11 @@ must return `mid', `mail', `invalid' or `ask'."
   :version "22.1"
   :group 'gnus-article-buttons
   :type '(choice (function-item :tag "Heuristic function"
-                               gnus-button-mid-or-mail-heuristic)
-                (const ask)
-                (const mid)
-                (const mail)))
+                                gnus-button-mid-or-mail-heuristic)
+                 (const :tag "Query me" ask)
+                 (const :tag "Assume it's a message ID" mid)
+                 (const :tag "Assume it's a mail address" mail)
+                 function))
 
 (defcustom gnus-button-mid-or-mail-heuristic-alist
   '((-10.0 . ".+\\$.+@")
diff --git a/lisp/gnus/gnus-bookmark.el b/lisp/gnus/gnus-bookmark.el
index 1a926619e14..aee122aa557 100644
--- a/lisp/gnus/gnus-bookmark.el
+++ b/lisp/gnus/gnus-bookmark.el
@@ -61,12 +61,12 @@
 ;; (define-key global-map "\C-crl" 'gnus-bookmark-bmenu-list)
 
 ;; FIXME: Add keybindings, see
-;; http://thread.gmane.org/gmane.emacs.gnus.general/63101/focus=63379
-;; http://thread.gmane.org/v9fxx9fkm4.fsf@marauder.physik.uni-ulm.de
+;; http://thread.gmane.org/gmane.emacs.gnus.general/63101/focus=63379 [dead 
link]
+;; http://thread.gmane.org/v9fxx9fkm4.fsf@marauder.physik.uni-ulm.de [dead 
link]
 
 ;; FIXME: Check if `gnus-bookmark.el' should use
 ;; `bookmark-make-record-function'.
-;; Cf. http://article.gmane.org/gmane.emacs.gnus.general/66076
+;; Cf. http://article.gmane.org/gmane.emacs.gnus.general/66076 [dead link]
 
 (defgroup gnus-bookmark nil
   "Setting, annotation and jumping to Gnus bookmarks."
@@ -112,7 +112,7 @@ You can toggle whether details are shown with 
\\<gnus-bookmark-bmenu-mode-map>\\
 
 (defcustom gnus-bookmark-bookmark-inline-details '(author)
   "Details to be shown with `gnus-bookmark-bmenu-toggle-infos'.
-The default value is \(subject)."
+The default value is (author)."
   :type '(list :tag "Gnus bookmark details"
               (set :inline t
                    (const :tag "Author" author)
diff --git a/lisp/gnus/gnus-group.el b/lisp/gnus/gnus-group.el
index 8c1d7e3c86a..01e6a8f317f 100644
--- a/lisp/gnus/gnus-group.el
+++ b/lisp/gnus/gnus-group.el
@@ -286,10 +286,10 @@ If you want to modify the group buffer, you can use this 
hook."
   :type 'hook)
 
 (defcustom gnus-useful-groups
-  '(("(ding) mailing list mirrored at gmane.org"
+  '(("(ding) mailing list mirrored at gmane.io"
      "gmane.emacs.gnus.general"
      (nntp "Gmane"
-          (nntp-address "news.gmane.org")))
+           (nntp-address "news.gmane.io")))
     ("Gnus bug archive"
      "gnus.gnus-bug"
      (nntp "news.gnus.org"
@@ -1436,14 +1436,8 @@ if it is a string, only list groups matching REGEXP."
 
 ;; Moving through the Group buffer (in topic mode) e.g. with C-n doesn't
 ;; update the state (enabled/disabled) of the icon `gnus-group-describe-group'
-;; automatically.  After `C-l' the state is correct.  See the following report
-;; on emacs-devel
-;; <http://thread.gmane.org/v9acdmrcse.fsf@marauder.physik.uni-ulm.de>:
-;; From: Reiner Steib
-;; Subject: tool bar icons not updated according to :active condition
-;; Newsgroups: gmane.emacs.devel
-;; Date: Mon, 23 Jan 2006 19:59:13 +0100
-;; Message-ID: <v9acdmrcse.fsf@marauder.physik.uni-ulm.de>
+;; automatically.  After `C-l' the state is correct.
+;; See: https://lists.gnu.org/r/emacs-devel/2006-01/msg00853.html
 
 ;; Using `redraw-frame' (see `gnus-tool-bar-update') in Emacs might
 ;; be confusing, so maybe we shouldn't call it by default.
@@ -2336,7 +2330,7 @@ Valid input formats include:
     (cond
      ;; URLs providing `group', `start' and `range':
      ((string-match
-       ;; http://thread.gmane.org/gmane.emacs.devel/86326/focus=86525
+       ;; http://thread.gmane.org/gmane.emacs.devel/86326/focus=86525 [dead 
link]
        
"^http://thread\\.gmane\\.org/\\([^/]+\\)/\\([0-9]+\\)/focus=\\([0-9]+\\)$"
        url)
       (setq group (match-string 1 url)
@@ -2347,7 +2341,7 @@ Valid input formats include:
                     start -1)))
      ;; URLs providing `group' and `start':
      ((or (string-match
-          ;; http://article.gmane.org/gmane.comp.gnu.make.bugs/3584
+           ;; http://article.gmane.org/gmane.comp.gnu.make.bugs/3584 [dead 
link]
           
"^http://\\(?:thread\\|article\\|permalink\\)\\.gmane\\.org/\\([^/]+\\)/\\([0-9]+\\)"
           url)
          (string-match
@@ -2355,7 +2349,7 @@ Valid input formats include:
           "^\\(?:nntp\\|news\\)://news\\.gmane\\.org/\\([^/]+\\)/\\([0-9]+\\)"
           url)
          (string-match
-          ;; 
http://news.gmane.org/group/gmane.emacs.gnus.general/thread=65099/force_load=t
+           ;; 
http://news.gmane.org/group/gmane.emacs.gnus.general/thread=65099/force_load=t 
[dead link]
           "^http://news\\.gmane\\.org/group/\\([^/]+\\)/thread=\\([0-9]+\\)"
           url))
       (setq group (match-string 1 url)
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index f576d4e6147..1639c062471 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -122,7 +122,7 @@ The server has to support NOV for any of this to work.
 
 This feature can seriously impact performance it ignores all
 locally cached header entries.  Setting it to t for groups for a
-server that doesn't expire articles (such as news.gmane.org),
+server that doesn't expire articles (such as news.gmane.io),
 leads to very slow summary generation."
   :group 'gnus-thread
   :type '(choice (const :tag "off" nil)
@@ -322,7 +322,8 @@ This can either be a regular expression or list of regular 
expressions
 that will be removed from subject strings if fuzzy subject
 simplification is selected."
   :group 'gnus-thread
-  :type '(repeat regexp))
+  :type '(choice regexp
+                 (repeat regexp)))
 
 (defcustom gnus-show-threads t
   "If non-nil, display threads in summary mode."
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 0071c02c081..0f5d253bc96 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -1145,7 +1145,8 @@ Note that these functions use `mail-citation-hook' if 
that is non-nil."
 This can also be a list of functions.  Each function can find the
 citation between (point) and (mark t).  And each function should leave
 point and mark around the citation text as modified."
-  :type 'function
+  :type '(choice function
+                 (repeat function))
   :link '(custom-manual "(message)Insertion Variables")
   :group 'message-insertion)
 
@@ -1406,8 +1407,9 @@ This can also be a list of values."
   :group 'message
   :link '(custom-manual "(message)Mail Aliases")
   :type '(choice (const :tag "Use Mailabbrev" abbrev)
-                (const :tag "Use ecomplete" ecomplete)
-                (const :tag "No expansion" nil)))
+                 (const :tag "Use ecomplete" ecomplete)
+                 (set (const :tag "Use Mailabbrev" abbrev)
+                      (const :tag "Use ecomplete" ecomplete))))
 
 (defcustom message-self-insert-commands '(self-insert-command)
   "List of `self-insert-command's used to trigger ecomplete.
@@ -1451,8 +1453,9 @@ If a function email is passed as the argument."
   :group 'message
   :link '(custom-manual "(message)Wide Reply")
   :type '(choice (const :tag "Yourself" nil)
-                regexp
-                (repeat :tag "Regexp List" regexp)))
+                 regexp
+                 (repeat :tag "Regexp List" regexp)
+                 function))
 
 (defsubst message-dont-reply-to-names ()
   (if (functionp message-dont-reply-to-names)
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index 6025ca7e72a..24cd5eb83d3 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -1369,9 +1369,9 @@ If not set, `default-directory' will be used."
 ;;; Attachment functions.
 
 (defcustom mml-dnd-protocol-alist
-  '(("^file:///" . mml-dnd-attach-file)
-    ("^file://"  . dnd-open-file)
-    ("^file:"    . mml-dnd-attach-file))
+  '(("^file:///" . mml-dnd-attach-file) ; GNOME, KDE, and suchlike.
+    ("^file:/[^/]" . mml-dnd-attach-file) ; Motif, other systems.
+    ("^file:[^/]" . mml-dnd-attach-file)) ; MS-Windows.
   "The functions to call when a drop in `mml-mode' is made.
 See `dnd-protocol-alist' for more information.  When nil, behave
 as in other buffers."
@@ -1460,29 +1460,36 @@ will be computed and used."
                 (file-name-nondirectory file)))
       (goto-char at-end))))
 
-(defun mml-dnd-attach-file (uri _action)
-  "Attach a drag and drop file.
-
-Ask for type, description or disposition according to
-`mml-dnd-attach-options'."
-  (let ((file (dnd-get-local-file-name uri t)))
-    (when (and file (file-regular-p file))
-      (let ((mml-dnd-attach-options mml-dnd-attach-options)
-           type description disposition)
-       (setq mml-dnd-attach-options
-             (when (and (eq mml-dnd-attach-options t)
-                        (not
-                         (y-or-n-p
-                          "Use default type, disposition and description? ")))
-               '(type description disposition)))
-       (when (or (memq 'type mml-dnd-attach-options)
-                 (memq 'disposition mml-dnd-attach-options))
-         (setq type (mml-minibuffer-read-type file)))
-       (when (memq 'description mml-dnd-attach-options)
-         (setq description (mml-minibuffer-read-description)))
-       (when (memq 'disposition mml-dnd-attach-options)
-         (setq disposition (mml-minibuffer-read-disposition type nil file)))
-       (mml-attach-file file type description disposition)))))
+(defun mml-dnd-attach-file (uris _action)
+  "Attach a drag and drop URIS, a list of local file URIs.
+
+Query whether to use the types, dispositions and descriptions
+default for each URL, subject to `mml-dnd-attach-options'.
+
+Return the action `private', communicating to the drop source
+that the file has been attached."
+  (let (file (mml-dnd-attach-options mml-dnd-attach-options))
+    (setq mml-dnd-attach-options
+         (when (and (eq mml-dnd-attach-options t)
+                    (not
+                     (y-or-n-p
+                      "Use default type, disposition and description? ")))
+           '(type description disposition)))
+    (dolist (uri uris)
+      (setq file (dnd-get-local-file-name uri t))
+      (when (and file (file-regular-p file))
+        (let (type description disposition)
+         (when (or (memq 'type mml-dnd-attach-options)
+                   (memq 'disposition mml-dnd-attach-options))
+           (setq type (mml-minibuffer-read-type file)))
+         (when (memq 'description mml-dnd-attach-options)
+           (setq description (mml-minibuffer-read-description)))
+         (when (memq 'disposition mml-dnd-attach-options)
+           (setq disposition (mml-minibuffer-read-disposition type nil file)))
+         (mml-attach-file file type description disposition)))))
+  'private)
+
+(put 'mml-dnd-attach-file 'dnd-multiple-handler t)
 
 (defun mml-attach-buffer (buffer &optional type description disposition 
filename)
   "Attach a buffer to the outgoing MIME message.
diff --git a/lisp/gnus/mml2015.el b/lisp/gnus/mml2015.el
index 21bb46b8fa7..df3dd434b0a 100644
--- a/lisp/gnus/mml2015.el
+++ b/lisp/gnus/mml2015.el
@@ -148,8 +148,8 @@ If set, it overrides the setting of 
`mml2015-sign-with-sender'."
   ;;
   ;; This function doesn't handle NotDashEscaped correctly.  EasyPG handles it
   ;; correctly.
-  ;; http://thread.gmane.org/gmane.emacs.gnus.general/66062/focus=66082
-  ;; http://thread.gmane.org/gmane.emacs.gnus.general/65087/focus=65109
+  ;; http://thread.gmane.org/gmane.emacs.gnus.general/66062/focus=66082 [dead 
link]
+  ;; http://thread.gmane.org/gmane.emacs.gnus.general/65087/focus=65109 [dead 
link]
   (goto-char (point-min))
   (forward-line)
   ;; We need to be careful not to strip beyond the armor headers.
diff --git a/lisp/gnus/nnheader.el b/lisp/gnus/nnheader.el
index cdba6e663bf..5dc5bf1fd75 100644
--- a/lisp/gnus/nnheader.el
+++ b/lisp/gnus/nnheader.el
@@ -85,7 +85,7 @@ Integer values will in effect be rounded up to the nearest 
multiple of
 
 (defvar nnheader-read-timeout
   (if (memq system-type '(windows-nt cygwin))
-      ;; http://thread.gmane.org/v9655t3pjo.fsf@marauder.physik.uni-ulm.de
+      ;; http://thread.gmane.org/v9655t3pjo.fsf@marauder.physik.uni-ulm.de 
[dead link]
       ;;
       ;; IIRC, values lower than 1.0 didn't/don't work on Windows/DOS.
       ;;
diff --git a/lisp/gnus/spam-report.el b/lisp/gnus/spam-report.el
index 7e0392797f9..8435d2d0124 100644
--- a/lisp/gnus/spam-report.el
+++ b/lisp/gnus/spam-report.el
@@ -49,7 +49,7 @@ instead."
   "Whether the article number (faster!) or the header should be used.
 
 You must set this to nil if you don't read Gmane groups directly
-from news.gmane.org, e.g. when using local newsserver such as
+from news.gmane.io, e.g. when using local newsserver such as
 leafnode."
   :type 'boolean)
 
@@ -64,7 +64,7 @@ The function must accept the arguments `host' and `report'."
                 spam-report-url-ping-mm-url)
          (const :tag "Store request URLs in `spam-report-requests-file'"
                 spam-report-url-to-file)
-         (function :tag "User defined function" nil)))
+         (function :tag "User defined function")))
 
 (defcustom spam-report-requests-file
   (nnheader-concat gnus-directory "spam/" "spam-report-requests.url")
@@ -149,6 +149,8 @@ submitted at once.  Internal variable.")
   (when (and gnus-newsgroup-name
             (or (null spam-report-gmane-regex)
                 (string-match spam-report-gmane-regex gnus-newsgroup-name)))
+    ;; FIXME: These addresses are down.  There is also no
+    ;;        unspam.gmane.io or spam.gmane.io.
     (let ((rpt-host (if unspam "unspam.gmane.org" "spam.gmane.org")))
       (gnus-message 6 "Reporting article %d to %s..." article rpt-host)
       (cond
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index df3f538a354..dfb2243988d 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -244,7 +244,7 @@ be used instead."
      (concat
       "\\(?:"
       ;; Match paired parentheses, e.g. in Wikipedia URLs:
-      ;; http://thread.gmane.org/47B4E3B2.3050402@gmail.com
+      ;; http://thread.gmane.org/47B4E3B2.3050402@gmail.com [dead link]
       "[" chars punct "]+" "(" "[" chars punct "]+" ")"
       "\\(?:" "[" chars punct "]+" "[" chars "]" "\\)?"
       "\\|"
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index 8dea599ed98..227a6af2a6b 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -257,7 +257,7 @@ control).  See \"cc-mode.el\" for more info."
 
   ;; Set up text conversion, for Emacs >= 30.0
   (when (boundp 'post-text-conversion-hook)
-    (add-hook 'post-text-conversion-hook #'c-post-text-conversion))
+    (add-hook 'post-text-conversion-hook #'c-post-text-conversion nil t))
 
   (unless new-style-init
     (c-init-language-vars-for 'c-mode)))
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index f45490ef6c3..6fd59ed3f93 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -768,7 +768,7 @@ Key bindings:
   (setq-local comint-prompt-read-only inferior-octave-prompt-read-only)
   (add-hook 'comint-input-filter-functions
             'inferior-octave-directory-tracker nil t)
-  ;; http://thread.gmane.org/gmane.comp.gnu.octave.general/48572
+  ;; http://thread.gmane.org/gmane.comp.gnu.octave.general/48572 [dead link]
   (add-hook 'window-configuration-change-hook
             'inferior-octave-track-window-width-change nil t)
   (setq-local compilation-error-regexp-alist 
inferior-octave-error-regexp-alist)
@@ -1007,7 +1007,7 @@ directory and makes this the current buffer's default 
directory."
 (defvar inferior-octave-last-column-width nil)
 
 (defun inferior-octave-track-window-width-change ()
-  ;; http://thread.gmane.org/gmane.comp.gnu.octave.general/48572
+  ;; http://thread.gmane.org/gmane.comp.gnu.octave.general/48572 [dead link]
   (let ((width (max inferior-octave-minimal-columns (window-width))))
     (unless (eq inferior-octave-last-column-width width)
       (setq-local inferior-octave-last-column-width width)
diff --git a/lisp/subr.el b/lisp/subr.el
index 12e33380260..d4173b4daba 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -2682,17 +2682,15 @@ The variable list SPEC is the same as in `if-let*'."
   "Non-nil if MODE is derived from one of MODES.
 Uses the `derived-mode-parent' property of the symbol to trace backwards.
 If you just want to check `major-mode', use `derived-mode-p'."
-  ;; If MODE is an alias, then look up the real mode function first.
   (declare (side-effect-free t))
-  (when-let ((alias (symbol-function mode)))
-    (when (symbolp alias)
-      (setq mode alias)))
   (while
       (and
        (not (memq mode modes))
-       (let* ((parent (get mode 'derived-mode-parent))
-              (parentfn (symbol-function parent)))
-         (setq mode (if (and parentfn (symbolp parentfn)) parentfn parent)))))
+       (let* ((parent (get mode 'derived-mode-parent)))
+        (setq mode (or parent
+                       ;; If MODE is an alias, then follow the alias.
+                       (let ((alias (symbol-function mode)))
+                         (and (symbolp alias) alias)))))))
   mode)
 
 (defun derived-mode-p (&rest modes)
diff --git a/lisp/url/url-vars.el b/lisp/url/url-vars.el
index ef4b8b2841b..630de7f4e43 100644
--- a/lisp/url/url-vars.el
+++ b/lisp/url/url-vars.el
@@ -146,7 +146,7 @@ variable."
                           (const :tag "Emacs version" :value emacs)
                           (const :tag "Last location" :value lastloc)
                           (const :tag "Browser identification" :value agent)
-                          (const :tag "No cookies" :value cookie)))
+                           (const :tag "No cookies" :value cookies)))
   :group 'url)
 
 (defcustom url-lastloc-privacy-level 'domain-match
diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el
index e6eb6a5b973..af24fcfd398 100644
--- a/lisp/vc/log-view.el
+++ b/lisp/vc/log-view.el
@@ -163,14 +163,14 @@
      :help "Go to the previous count'th log message"]
     ["Next File"  log-view-file-next
      :help "Go to the next count'th file"
-     :active (derived-mode-p vc-cvs-log-view-mode
-                             vc-rcs-log-view-mode
-                             vc-sccs-log-view-mode)]
+     :active (derived-mode-p 'vc-cvs-log-view-mode
+                             'vc-rcs-log-view-mode
+                             'vc-sccs-log-view-mode)]
     ["Previous File"  log-view-file-prev
      :help "Go to the previous count'th file"
-     :active (derived-mode-p vc-cvs-log-view-mode
-                             vc-rcs-log-view-mode
-                             vc-sccs-log-view-mode)]))
+     :active (derived-mode-p 'vc-cvs-log-view-mode
+                             'vc-rcs-log-view-mode
+                             'vc-sccs-log-view-mode)]))
 
 (defvar log-view-mode-hook nil
   "Hook run at the end of `log-view-mode'.")
diff --git a/nextstep/ChangeLog.1 b/nextstep/ChangeLog.1
index 26def2266fe..cd6bfbfbbbe 100644
--- a/nextstep/ChangeLog.1
+++ b/nextstep/ChangeLog.1
@@ -6,7 +6,7 @@
 
        * Makefile.in (links): New phony target to create a fake
        installation pointing back to the source tree to run GUI Emacs
-       in-place (http://article.gmane.org/gmane.emacs.devel:178330).
+       in-place (http://article.gmane.org/gmane.emacs.devel:178330). [dead 
link]
 
 2014-11-22  Glenn Morris  <rgm@gnu.org>
 
diff --git a/src/androidterm.c b/src/androidterm.c
index e87f7ca2d14..4a479daf452 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -1141,7 +1141,7 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
              Lisp_Object window
                = window_from_coordinates (f, event->xmotion.x,
                                           event->xmotion.y, 0,
-                                          false, false);
+                                          false, false, false);
 
              /* A window will be autoselected only when it is not
                 selected now and the last mouse movement event was
@@ -1290,7 +1290,7 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
              int x = event->xbutton.x;
              int y = event->xbutton.y;
 
-             window = window_from_coordinates (f, x, y, 0, true, true);
+             window = window_from_coordinates (f, x, y, 0, true, true, true);
              tab_bar_p = EQ (window, f->tab_bar_window);
 
              if (tab_bar_p)
@@ -1312,7 +1312,7 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
              int x = event->xbutton.x;
              int y = event->xbutton.y;
 
-             window = window_from_coordinates (f, x, y, 0, true, true);
+             window = window_from_coordinates (f, x, y, 0, true, true, true);
              tool_bar_p = (EQ (window, f->tool_bar_window)
                            && ((event->xbutton.type
                                 != ANDROID_BUTTON_RELEASE)
@@ -1408,7 +1408,7 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
          int y = event->touch.y;
 
          window = window_from_coordinates (any, x, y, 0, true,
-                                           true);
+                                           true, true);
 
          /* If this touch has started in the tool bar, do not
             send it to Lisp.  Instead, simulate a tool bar
@@ -1605,7 +1605,7 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
          /* Figure out how much to scale the deltas by.  */
          window = window_from_coordinates (any, event->wheel.x,
                                            event->wheel.y, NULL,
-                                           false, false);
+                                           false, false, false);
 
          if (WINDOWP (window))
            scroll_height = XWINDOW (window)->pixel_height;
diff --git a/src/androidvfs.c b/src/androidvfs.c
index b3d644e21a2..51558d2a375 100644
--- a/src/androidvfs.c
+++ b/src/androidvfs.c
@@ -403,6 +403,16 @@ android_init_fd_class (JNIEnv *env)
 
 
 
+/* Account for SAF file names two times as large as PATH_MAX; larger
+   values are prohibitively slow, but smaller values can't face up to
+   some long file names within several nested layers of directories.
+
+   Buffers holding components or other similar file name constitutents
+   which don't represent SAF files must continue to use PATH_MAX, for
+   that is the restriction imposed by the Unix file system.  */
+
+#define EMACS_PATH_MAX (PATH_MAX * 2)
+
 /* Delete redundant instances of `.' and `..' from NAME in-place.
    NAME must be *LENGTH long, excluding a mandatory trailing NULL
    byte.
@@ -4990,7 +5000,7 @@ android_saf_tree_rename (struct android_vnode *src,
 {
   char *last, *dst_last;
   struct android_saf_tree_vnode *vp, *vdst;
-  char path[PATH_MAX], path1[PATH_MAX];
+  char path[EMACS_PATH_MAX], path1[EMACS_PATH_MAX];
   char *fill, *dst_id;
   int rc;
 
@@ -5076,8 +5086,8 @@ android_saf_tree_rename (struct android_vnode *src,
       /* The names of the source and destination directories will have
         to be copied to path.  */
 
-      if (last - vp->name >= PATH_MAX
-         || dst_last - vdst->name >= PATH_MAX)
+      if (last - vp->name >= EMACS_PATH_MAX
+         || dst_last - vdst->name >= EMACS_PATH_MAX)
        {
          errno = ENAMETOOLONG;
          return -1;
@@ -5191,7 +5201,7 @@ android_saf_tree_rename (struct android_vnode *src,
      directory is required, as it provides the directory whose entries
      will be modified.  */
 
-  if (last - vp->name >= PATH_MAX)
+  if (last - vp->name >= EMACS_PATH_MAX)
     {
       errno = ENAMETOOLONG;
       return -1;
@@ -5480,7 +5490,7 @@ android_saf_tree_opendir (struct android_vnode *vnode)
   struct android_saf_tree_vdir *dir;
   char *fill, *end;
   jobject cursor;
-  char component[PATH_MAX];
+  char component[EMACS_PATH_MAX];
 
   vp = (struct android_saf_tree_vnode *) vnode;
 
@@ -5510,7 +5520,7 @@ android_saf_tree_opendir (struct android_vnode *vnode)
   if (!end)
     emacs_abort ();
 
-  if (end - fill >= PATH_MAX)
+  if (end - fill >= EMACS_PATH_MAX)
     {
       errno = ENAMETOOLONG;
       xfree (dir);
@@ -6455,7 +6465,7 @@ android_root_name (struct android_vnode *vnode, char 
*name,
    least N bytes.
 
    NAME may be either an absolute file name or a name relative to the
-   current working directory.  It must not be longer than PATH_MAX
+   current working directory.  It must not be longer than EMACS_PATH_MAX
    bytes.
 
    Value is NULL upon failure with errno set accordingly, or the
@@ -6464,14 +6474,14 @@ android_root_name (struct android_vnode *vnode, char 
*name,
 static struct android_vnode *
 android_name_file (const char *name)
 {
-  char buffer[PATH_MAX + 1], *head;
+  char buffer[EMACS_PATH_MAX + 1], *head;
   const char *end;
   size_t len;
   int nslash, c;
   struct android_vnode *vp;
 
   len = strlen (name);
-  if (len > PATH_MAX)
+  if (len > EMACS_PATH_MAX)
     {
       errno = ENAMETOOLONG;
       return NULL;
@@ -7009,7 +7019,7 @@ int
 android_fstatat (int dirfd, const char *restrict pathname,
                 struct stat *restrict statbuf, int flags)
 {
-  char buffer[PATH_MAX + 1];
+  char buffer[EMACS_PATH_MAX + 1];
   struct android_vnode *vp;
   int rc;
 
@@ -7023,7 +7033,7 @@ android_fstatat (int dirfd, const char *restrict pathname,
   /* Now establish whether DIRFD is a file descriptor corresponding to
      an open VFS directory stream.  */
 
-  if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1))
+  if (!android_fstatat_1 (dirfd, pathname, buffer, EMACS_PATH_MAX + 1))
     {
       pathname = buffer;
       goto vfs;
@@ -7049,7 +7059,7 @@ int
 android_faccessat (int dirfd, const char *restrict pathname,
                   int mode, int flags)
 {
-  char buffer[PATH_MAX + 1];
+  char buffer[EMACS_PATH_MAX + 1];
   struct android_vnode *vp;
   int rc;
 
@@ -7063,7 +7073,7 @@ android_faccessat (int dirfd, const char *restrict 
pathname,
   /* Now establish whether DIRFD is a file descriptor corresponding to
      an open VFS directory stream.  */
 
-  if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1))
+  if (!android_fstatat_1 (dirfd, pathname, buffer, EMACS_PATH_MAX + 1))
     {
       pathname = buffer;
       goto vfs;
@@ -7089,7 +7099,7 @@ int
 android_fchmodat (int dirfd, const char *pathname, mode_t mode,
                  int flags)
 {
-  char buffer[PATH_MAX + 1];
+  char buffer[EMACS_PATH_MAX + 1];
   struct android_vnode *vp;
   int rc;
 
@@ -7099,7 +7109,7 @@ android_fchmodat (int dirfd, const char *pathname, mode_t 
mode,
   /* Now establish whether DIRFD is a file descriptor corresponding to
      an open VFS directory stream.  */
 
-  if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1))
+  if (!android_fstatat_1 (dirfd, pathname, buffer, EMACS_PATH_MAX + 1))
     {
       pathname = buffer;
       goto vfs;
@@ -7125,7 +7135,7 @@ ssize_t
 android_readlinkat (int dirfd, const char *restrict pathname,
                    char *restrict buf, size_t bufsiz)
 {
-  char buffer[PATH_MAX + 1];
+  char buffer[EMACS_PATH_MAX + 1];
   struct android_vnode *vp;
   ssize_t rc;
 
@@ -7135,7 +7145,7 @@ android_readlinkat (int dirfd, const char *restrict 
pathname,
   /* Now establish whether DIRFD is a file descriptor corresponding to
      an open VFS directory stream.  */
 
-  if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1))
+  if (!android_fstatat_1 (dirfd, pathname, buffer, EMACS_PATH_MAX + 1))
     {
       pathname = buffer;
       goto vfs;
diff --git a/src/haikuterm.c b/src/haikuterm.c
index b1a016b49a9..bcb5055ea42 100644
--- a/src/haikuterm.c
+++ b/src/haikuterm.c
@@ -3472,7 +3472,7 @@ haiku_read_socket (struct terminal *terminal, struct 
input_event *hold_quit)
                if (!NILP (Vmouse_autoselect_window))
                  {
                    static Lisp_Object last_mouse_window;
-                   Lisp_Object window = window_from_coordinates (f, b->x, 
b->y, 0, 0, 0);
+                   Lisp_Object window = window_from_coordinates (f, b->x, 
b->y, 0, 0, 0, 0);
 
                    if (WINDOWP (window)
                        && !EQ (window, last_mouse_window)
@@ -3555,7 +3555,7 @@ haiku_read_socket (struct terminal *terminal, struct 
input_event *hold_quit)
                int x = b->x;
                int y = b->y;
 
-               window = window_from_coordinates (f, x, y, 0, true, true);
+               window = window_from_coordinates (f, x, y, 0, true, true, true);
                tab_bar_p = EQ (window, f->tab_bar_window);
 
                if (tab_bar_p)
@@ -3573,7 +3573,7 @@ haiku_read_socket (struct terminal *terminal, struct 
input_event *hold_quit)
                int x = b->x;
                int y = b->y;
 
-               window = window_from_coordinates (f, x, y, 0, true, true);
+               window = window_from_coordinates (f, x, y, 0, true, true, true);
                tool_bar_p = (EQ (window, f->tool_bar_window)
                              && (type != BUTTON_UP
                                  || f->last_tool_bar_item != -1));
@@ -3834,7 +3834,7 @@ haiku_read_socket (struct terminal *terminal, struct 
input_event *hold_quit)
 
            BView_get_mouse (FRAME_HAIKU_VIEW (f), &x, &y);
 
-           wheel_window = window_from_coordinates (f, x, y, 0, false, false);
+           wheel_window = window_from_coordinates (f, x, y, 0, false, false, 
false);
 
            if (NILP (wheel_window))
              {
diff --git a/src/keyboard.c b/src/keyboard.c
index dc2f78a7c26..c00f48d7836 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -5562,7 +5562,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, 
Lisp_Object y,
   int xret = 0, yret = 0;
   /* The window or frame under frame pixel coordinates (x,y)  */
   Lisp_Object window_or_frame = f
-    ? window_from_coordinates (f, mx, my, &part, true, true)
+    ? window_from_coordinates (f, mx, my, &part, true, true, true)
     : Qnil;
 #ifdef HAVE_WINDOW_SYSTEM
   bool tool_bar_p = false;
diff --git a/src/msdos.c b/src/msdos.c
index 1b7f2d4ae21..5dd7c1573c4 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -2662,7 +2662,7 @@ dos_rawgetc (void)
              static Lisp_Object last_mouse_window;
 
              mouse_window = window_from_coordinates
-               (SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0);
+               (SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0, 0);
              /* A window will be selected only when it is not
                 selected now, and the last mouse movement event was
                 not in it.  A minibuffer window will be selected iff
diff --git a/src/nsterm.m b/src/nsterm.m
index 11535f071eb..46a5e8870e8 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -7412,7 +7412,7 @@ ns_in_echo_area (void)
          int x = lrint (p.x);
          int y = lrint (p.y);
 
-         window = window_from_coordinates (emacsframe, x, y, 0, true, true);
+         window = window_from_coordinates (emacsframe, x, y, 0, true, true, 
true);
          tab_bar_p = EQ (window, emacsframe->tab_bar_window);
 
          if (tab_bar_p)
@@ -7518,7 +7518,7 @@ ns_in_echo_area (void)
       NSTRACE_MSG ("mouse_autoselect_window");
       static Lisp_Object last_mouse_window;
       Lisp_Object window
-       = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0, 0);
+       = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0, 0, 0);
 
       if (WINDOWP (window)
           && !EQ (window, last_mouse_window)
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index a7c687d811d..461c9d6d899 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -5894,7 +5894,7 @@ motion_notify_event (GtkWidget *widget, GdkEvent *event,
        {
          static Lisp_Object last_mouse_window;
          Lisp_Object window = window_from_coordinates
-           (f, event->motion.x, event->motion.y, 0, false, false);
+           (f, event->motion.x, event->motion.y, 0, false, false, false);
 
          /* A window will be autoselected only when it is not
             selected now and the last mouse movement event was
@@ -6047,7 +6047,7 @@ button_event (GtkWidget *widget, GdkEvent *event,
          int x = event->button.x;
          int y = event->button.y;
 
-         window = window_from_coordinates (f, x, y, 0, true, true);
+         window = window_from_coordinates (f, x, y, 0, true, true, true);
          tab_bar_p = EQ (window, f->tab_bar_window);
 
          if (tab_bar_p)
diff --git a/src/profiler.c b/src/profiler.c
index 6217071ef9c..b494ad783dc 100644
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -55,6 +55,8 @@ struct profiler_log {
   EMACS_INT discarded; /* Samples evicted during table overflow.  */
 };
 
+static Lisp_Object export_log (struct profiler_log *);
+
 static struct profiler_log
 make_log (void)
 {
@@ -213,6 +215,23 @@ record_backtrace (struct profiler_log *plog, EMACS_INT 
count)
 
 /* Sampling profiler.  */
 
+/* Signal handler for sampling profiler.  */
+
+static void
+add_sample (struct profiler_log *plog, EMACS_INT count)
+{
+  if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */
+    /* Special case the time-count inside GC because the hash-table
+       code is not prepared to be used while the GC is running.
+       More specifically it uses ASIZE at many places where it does
+       not expect the ARRAY_MARK_FLAG to be set.  We could try and
+       harden the hash-table code, but it doesn't seem worth the
+       effort.  */
+    plog->gc_count = saturated_add (plog->gc_count, count);
+  else
+    record_backtrace (plog, count);
+}
+
 #ifdef PROFILER_CPU_SUPPORT
 
 /* The profiler timer and whether it was properly initialized, if
@@ -235,30 +254,9 @@ static enum profiler_cpu_running
 /* Hash-table log of CPU profiler.  */
 static struct profiler_log cpu;
 
-/* Hash-table log of Memory profiler.  */
-static struct profiler_log memory;
-
 /* The current sampling interval in nanoseconds.  */
 static EMACS_INT current_sampling_interval;
 
-/* Signal handler for sampling profiler.  */
-
-static void
-add_sample (struct profiler_log *plog, EMACS_INT count)
-{
-  if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */
-    /* Special case the time-count inside GC because the hash-table
-       code is not prepared to be used while the GC is running.
-       More specifically it uses ASIZE at many places where it does
-       not expect the ARRAY_MARK_FLAG to be set.  We could try and
-       harden the hash-table code, but it doesn't seem worth the
-       effort.  */
-    plog->gc_count = saturated_add (plog->gc_count, count);
-  else
-    record_backtrace (plog, count);
-}
-
-
 static void
 handle_profiler_signal (int signal)
 {
@@ -421,6 +419,19 @@ DEFUN ("profiler-cpu-running-p",
   return profiler_cpu_running ? Qt : Qnil;
 }
 
+DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log,
+       0, 0, 0,
+       doc: /* Return the current cpu profiler log.
+The log is a hash-table mapping backtraces to counters which represent
+the amount of time spent at those points.  Every backtrace is a vector
+of functions, where the last few elements may be nil.
+Before returning, a new log is allocated for future samples.  */)
+  (void)
+{
+  return (export_log (&cpu));
+}
+#endif /* PROFILER_CPU_SUPPORT */
+
 static Lisp_Object
 export_log (struct profiler_log *log)
 {
@@ -433,29 +444,21 @@ export_log (struct profiler_log *log)
     Fputhash (CALLN (Fvector, QDiscarded_Samples, Qnil),
              make_fixnum (log->discarded),
              result);
+#ifdef PROFILER_CPU_SUPPORT
   /* Here we're making the log visible to Elisp, so it's not safe any
      more for our use afterwards since we can't rely on its special
      pre-allocated keys anymore.  So we have to allocate a new one.  */
   if (profiler_cpu_running)
     *log = make_log ();
+#endif /* PROFILER_CPU_SUPPORT */
   return result;
 }
-
-DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log,
-       0, 0, 0,
-       doc: /* Return the current cpu profiler log.
-The log is a hash-table mapping backtraces to counters which represent
-the amount of time spent at those points.  Every backtrace is a vector
-of functions, where the last few elements may be nil.
-Before returning, a new log is allocated for future samples.  */)
-  (void)
-{
-  return (export_log (&cpu));
-}
-#endif /* PROFILER_CPU_SUPPORT */
 
 /* Memory profiler.  */
 
+/* Hash-table log of Memory profiler.  */
+static struct profiler_log memory;
+
 /* True if memory profiler is running.  */
 bool profiler_memory_running;
 
diff --git a/src/w32.c b/src/w32.c
index c75beb630e5..81201509e2d 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -9387,7 +9387,7 @@ sys_write (int fd, const void * buffer, unsigned int 
count)
         break them into smaller chunks.  See the Comments section of
         the MSDN documentation of WriteFile for details behind the
         choice of the value of CHUNK below.  See also the thread
-        http://thread.gmane.org/gmane.comp.version-control.git/145294
+        http://thread.gmane.org/gmane.comp.version-control.git/145294 [dead 
link]
         in the git mailing list.  */
       const unsigned char *p = buffer;
       const bool is_pipe = (fd < MAXDESC
diff --git a/src/w32inevt.c b/src/w32inevt.c
index 29717954cfd..630a9f4e5fb 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -496,7 +496,7 @@ do_mouse_event (MOUSE_EVENT_RECORD *event,
            if (!NILP (Vmouse_autoselect_window))
              {
                Lisp_Object mouse_window = window_from_coordinates (f, mx, my,
-                                                                   0, 0, 0);
+                                                                   0, 0, 0, 0);
                /* A window will be selected only when it is not
                   selected now, and the last mouse movement event was
                   not in it.  A minibuffer window will be selected iff
diff --git a/src/w32term.c b/src/w32term.c
index a5f17a18213..301d8f4ef12 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -3376,7 +3376,7 @@ w32_construct_mouse_wheel (struct input_event *result, 
W32Msg *msg,
       if (w32_wheel_scroll_lines == UINT_MAX)
        {
          Lisp_Object window = window_from_coordinates (f, p.x, p.y, NULL,
-                                                       false, false);
+                                                       false, false, false);
          if (!WINDOWP (window))
            {
              result->kind = NO_EVENT;
@@ -5335,7 +5335,7 @@ w32_read_socket (struct terminal *terminal,
                {
                  static Lisp_Object last_mouse_window;
                  Lisp_Object window = window_from_coordinates
-                   (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0, 
0);
+                   (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0, 
0, 0);
 
                  /* Window will be selected only when it is not
                     selected now and last mouse movement event was
@@ -5407,7 +5407,7 @@ w32_read_socket (struct terminal *terminal,
                    int x = XFIXNAT (inev.x);
                    int y = XFIXNAT (inev.y);
 
-                    window = window_from_coordinates (f, x, y, 0, 1, 1);
+                    window = window_from_coordinates (f, x, y, 0, 1, 1, 1);
 
                     if (EQ (window, f->tab_bar_window))
                       {
@@ -5435,7 +5435,7 @@ w32_read_socket (struct terminal *terminal,
                    int x = XFIXNAT (inev.x);
                    int y = XFIXNAT (inev.y);
 
-                    window = window_from_coordinates (f, x, y, 0, 1, 1);
+                    window = window_from_coordinates (f, x, y, 0, 1, 1, 1);
 
                     if (EQ (window, f->tool_bar_window)
                        /* Make sure the tool bar was previously
diff --git a/src/window.c b/src/window.c
index 968b982c135..e802ffb3fe2 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1680,7 +1680,8 @@ check_window_containing (struct window *w, void 
*user_data)
 
 Lisp_Object
 window_from_coordinates (struct frame *f, int x, int y,
-                        enum window_part *part, bool tab_bar_p, bool 
tool_bar_p)
+                        enum window_part *part, bool menu_bar_p,
+                        bool tab_bar_p, bool tool_bar_p)
 {
   Lisp_Object window;
   struct check_window_data cw;
@@ -1693,6 +1694,21 @@ window_from_coordinates (struct frame *f, int x, int y,
   cw.window = &window, cw.x = x, cw.y = y; cw.part = part;
   foreach_window (f, check_window_containing, &cw);
 
+#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_MENU_BAR)
+  /* If not found above, see if it's in the menu bar window, if a menu
+     bar exists.  */
+  if (NILP (window)
+      && menu_bar_p
+      && WINDOWP (f->menu_bar_window)
+      && WINDOW_TOTAL_LINES (XWINDOW (f->menu_bar_window)) > 0
+      && (coordinates_in_window (XWINDOW (f->menu_bar_window), x, y)
+         != ON_NOTHING))
+    {
+      *part = ON_TEXT;
+      window = f->menu_bar_window;
+    }
+#endif
+
 #if defined (HAVE_WINDOW_SYSTEM)
   /* If not found above, see if it's in the tab bar window, if a tab
      bar exists.  */
@@ -1746,7 +1762,7 @@ function returns nil.  */)
                                   + FRAME_INTERNAL_BORDER_WIDTH (f)),
                                  (FRAME_PIXEL_Y_FROM_CANON_Y (f, y)
                                   + FRAME_INTERNAL_BORDER_WIDTH (f)),
-                                 0, false, false);
+                                 0, false, false, false);
 }
 
 ptrdiff_t
diff --git a/src/window.h b/src/window.h
index 413293420fd..9ef8434af18 100644
--- a/src/window.h
+++ b/src/window.h
@@ -1111,7 +1111,7 @@ extern Lisp_Object minibuf_selected_window;
 
 extern Lisp_Object make_window (void);
 extern Lisp_Object window_from_coordinates (struct frame *, int, int,
-                                            enum window_part *, bool, bool);
+                                            enum window_part *, bool, bool, 
bool);
 extern void resize_frame_windows (struct frame *, int, bool);
 extern void restore_window_configuration (Lisp_Object);
 extern void delete_all_child_windows (Lisp_Object);
diff --git a/src/xdisp.c b/src/xdisp.c
index b9009df5df9..578131a4005 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2778,7 +2778,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, 
NativeRectangle *rect)
       goto virtual_glyph;
     }
   else if (!f->glyphs_initialized_p
-          || (window = window_from_coordinates (f, gx, gy, &part, false, 
false),
+          || (window = window_from_coordinates (f, gx, gy, &part, false, 
false, false),
               NILP (window)))
     {
       width = FRAME_SMALLEST_CHAR_WIDTH (f);
@@ -35438,7 +35438,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
     return;
 
   /* Which window is that in?  */
-  window = window_from_coordinates (f, x, y, &part, true, true);
+  window = window_from_coordinates (f, x, y, &part, true, true, true);
 
   /* If displaying active text in another window, clear that.  */
   if (! EQ (window, hlinfo->mouse_face_window)
diff --git a/src/xterm.c b/src/xterm.c
index 5d491e63778..d01c4da0564 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -21171,7 +21171,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                  }
 
                Lisp_Object window = window_from_coordinates
-                 (f, xmotion.x, xmotion.y, 0, false, false);
+                 (f, xmotion.x, xmotion.y, 0, false, false, false);
 
                /* A window will be autoselected only when it is not
                   selected now and the last mouse movement event was
@@ -21902,7 +21902,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 int x = event->xbutton.x;
                 int y = event->xbutton.y;
 
-                window = window_from_coordinates (f, x, y, 0, true, true);
+                window = window_from_coordinates (f, x, y, 0, true, true, 
true);
                 tab_bar_p = EQ (window, f->tab_bar_window);
 
                 if (tab_bar_p)
@@ -21923,7 +21923,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 int x = event->xbutton.x;
                 int y = event->xbutton.y;
 
-                window = window_from_coordinates (f, x, y, 0, true, true);
+                window = window_from_coordinates (f, x, y, 0, true, true, 
true);
                 tool_bar_p = (EQ (window, f->tool_bar_window)
                              && (event->xbutton.type != ButtonRelease
                                  || f->last_tool_bar_item != -1));
@@ -22656,7 +22656,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                            continue;
 
                          window = window_from_coordinates (f, real_x, real_y, 
NULL,
-                                                           false, false);
+                                                           false, false, 
false);
 
                          if (WINDOWP (window))
                            scroll_height = XWINDOW (window)->pixel_height;
@@ -23099,7 +23099,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                          || !NILP (focus_follows_mouse)))
                    {
                      static Lisp_Object last_mouse_window;
-                     Lisp_Object window = window_from_coordinates (f, ev.x, 
ev.y, 0, false, false);
+                     Lisp_Object window = window_from_coordinates (f, ev.x, 
ev.y, 0, false, false,
+                                                                   false);
 
                      /* A window will be autoselected only when it is not
                         selected now and the last mouse movement event was
@@ -23677,7 +23678,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      int x = bv.x;
                      int y = bv.y;
 
-                     window = window_from_coordinates (f, x, y, 0, true, true);
+                     window = window_from_coordinates (f, x, y, 0, true, true, 
true);
                      tab_bar_p = EQ (window, f->tab_bar_window);
 
                      if (tab_bar_p)
@@ -23698,7 +23699,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      int x = bv.x;
                      int y = bv.y;
 
-                     window = window_from_coordinates (f, x, y, 0, true, true);
+                     window = window_from_coordinates (f, x, y, 0, true, true, 
true);
                      /* Ignore button release events if the mouse
                         wasn't previously pressed on the tool bar.
                         We do this because otherwise selecting some
@@ -24704,7 +24705,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                  int x = xev->event_x;
                  int y = xev->event_y;
 
-                 window = window_from_coordinates (f, x, y, 0, true, true);
+                 window = window_from_coordinates (f, x, y, 0, true, true, 
true);
                  /* Ignore button release events if the mouse
                     wasn't previously pressed on the tool bar.
                     We do this because otherwise selecting some
diff --git a/test/lisp/dnd-tests.el b/test/lisp/dnd-tests.el
index 342b6e49be4..7a7f54ba0bb 100644
--- a/test/lisp/dnd-tests.el
+++ b/test/lisp/dnd-tests.el
@@ -531,7 +531,69 @@ ACTION is ignored.  Return the symbol `private' otherwise."
                                                 dnd-tests-list-4)
                                                'copy)
                      'private))
-      (should (equal (buffer-string) (nth 4 dnd-tests-list-4))))))
+      (should (equal (buffer-string) (nth 4 dnd-tests-list-4))))
+    ;; Check that a handler enumerated twice in the handler list
+    ;; receives URIs assigned to it only once.
+    (let* ((received-p nil)
+           (lambda (lambda (uri _action)
+                     (should (equal uri "scheme1://test"))
+                     (should (null received-p))
+                     (setq received-p 'copy))))
+      (setq dnd-protocol-alist (list (cons "scheme1://" lambda)
+                                     (cons "scheme1://" lambda)))
+      (should (equal (dnd-handle-multiple-urls (selected-window)
+                                               (list "scheme1://test")
+                                               'copy)
+                     'copy)))))
+
+(ert-deftest dnd-tests-default-file-name-handlers ()
+  (let* ((local-files-opened nil)
+         (remote-files-opened nil)
+         (function-1 (lambda (file _uri)
+                       (push file local-files-opened)
+                       'copy))
+         (function-2 (lambda (file _uri)
+                       (push file remote-files-opened)
+                       'copy)))
+    (unwind-protect
+        (progn
+          (advice-add #'dnd-open-local-file :override
+                      function-1)
+          (advice-add #'dnd-open-file :override
+                      function-2)
+          ;; Guarantee that file names are properly categorized as either
+          ;; local or remote by the default dnd-protocol-alist.
+          (dnd-handle-multiple-urls
+           (selected-window)
+           (list
+            ;; These are run-of-the-mill local file URIs.
+            "file:///usr/include/sys/acct.h"
+            "file:///usr/include/sys/acctctl.h"
+            ;; These URIs incorporate a host; they should match
+            ;; function-2 but never function-1.
+            "file://remotehost/usr/src/emacs/configure.ac"
+            "file://remotehost/usr/src/emacs/configure"
+            ;; These URIs are generated by drag-and-drop event
+            ;; handlers from local file names alone; they are not
+            ;; echt URIs in and of themselves, but a product of our
+            ;; drag and drop code.
+            "file:/etc/vfstab"
+            "file:/etc/dfs/sharetab"
+            ;; These URIs are generated under MS-Windows.
+            "file:c:/path/to/file/name"
+            "file:d:/path/to/file/name")
+           'copy)
+          (should (equal (sort local-files-opened #'string<)
+                         '("file:///usr/include/sys/acct.h"
+                           "file:///usr/include/sys/acctctl.h"
+                           "file:/etc/dfs/sharetab"
+                           "file:/etc/vfstab"
+                           "file:c:/path/to/file/name"
+                           "file:d:/path/to/file/name")))
+          (should (equal (sort remote-files-opened #'string<)
+                         '("file://remotehost/usr/src/emacs/configure"
+                           "file://remotehost/usr/src/emacs/configure.ac"))))
+      (advice-remove #'dnd-open-local-file function-2))))
 
 (provide 'dnd-tests)
 ;;; dnd-tests.el ends here
diff --git a/test/lisp/erc/erc-fill-tests.el b/test/lisp/erc/erc-fill-tests.el
index 80f5fd22ac6..92424d1e556 100644
--- a/test/lisp/erc/erc-fill-tests.el
+++ b/test/lisp/erc/erc-fill-tests.el
@@ -33,7 +33,6 @@
   (declare (indent 1))
   (let* ((msg (erc-format-privmessage speaker
                                       (apply #'concat msg-parts) nil t))
-         ;; (erc--msg-prop-overrides '((erc-msg . msg) (erc-cmd . PRIVMSG)))
          (parsed (make-erc-response :unparsed msg :sender speaker
                                     :command "PRIVMSG"
                                     :command-args (list "#chan" msg)
@@ -129,10 +128,10 @@
       (should (equal (get-text-property (1- (pos-eol)) 'wrap-prefix)
                      '(space :width erc-fill--wrap-value))))))
 
-;; Set this variable to t to generate new snapshots after carefully
+;; Use this variable to generate new snapshots after carefully
 ;; reviewing the output of *each* snapshot (not just first and last).
 ;; Obviously, only run one test at a time.
-(defvar erc-fill-tests--save-p nil)
+(defvar erc-fill-tests--save-p (getenv "ERC_TESTS_FILL_SAVE"))
 
 ;; On graphical displays, echo .graphic >> .git/info/exclude
 (defvar erc-fill-tests--graphic-dir "fill/snapshots/.graphic")
@@ -162,8 +161,12 @@
         (insert (setq got (read repr))))
       (erc-mode))
     (if erc-fill-tests--save-p
-        (with-temp-file expect-file
-          (insert repr))
+        (let (inhibit-message)
+          (with-temp-file expect-file
+            (insert repr))
+          ;; Limit writing snapshots to one test at a time.
+          (setq erc-fill-tests--save-p nil)
+          (message "erc-fill-tests--compare: wrote %S" expect-file))
       (if (file-exists-p expect-file)
           ;; Ensure string-valued properties, like timestamps, aren't
           ;; recursive (signals `max-lisp-eval-depth' exceeded).
@@ -297,16 +300,20 @@
      ;; Set this here so that the first few messages are from 1970
      (let ((erc-fill-tests--time-vals (lambda () 1680332400)))
        (erc-fill-tests--insert-privmsg "bob" "zero.")
+       (erc-fill-tests--insert-privmsg "bob" "0.5")
 
        (erc-process-ctcp-query
         erc-server-process
         (make-erc-response
-         :unparsed ":bob!~u@fake PRIVMSG #chan :\1ACTION one\1"
-         :sender "bob!~u@fake" :command "PRIVMSG"
-         :command-args '("#chan" "\1ACTION one\1") :contents "\1ACTION one\1")
+         :unparsed ":bob!~u@fake PRIVMSG #chan :\1ACTION one.\1"
+         :sender "bob!~u@fake"
+         :command "PRIVMSG"
+         :command-args '("#chan" "\1ACTION one.\1")
+         :contents "\1ACTION one.\1")
         "bob" "~u" "fake")
 
        (erc-fill-tests--insert-privmsg "bob" "two.")
+       (erc-fill-tests--insert-privmsg "bob" "2.5")
 
        ;; Compat switch to opt out of overhanging speaker.
        (let (erc-fill--wrap-action-dedent-p)
diff --git a/test/lisp/erc/erc-scenarios-stamp.el 
b/test/lisp/erc/erc-scenarios-stamp.el
index d6b5d868ce5..b98300d04be 100644
--- a/test/lisp/erc/erc-scenarios-stamp.el
+++ b/test/lisp/erc/erc-scenarios-stamp.el
@@ -50,7 +50,6 @@
        (erc-stamp--current-time 704591940)
        (erc-stamp--tz t)
        (erc-server-flood-penalty 0.1)
-       (erc-timestamp-only-if-changed-flag nil)
        (erc-insert-timestamp-function #'erc-insert-timestamp-left)
        (erc-modules (cons 'fill-wrap erc-modules))
        (erc-timestamp-only-if-changed-flag nil)
@@ -87,4 +86,31 @@
             (should (looking-back "CEIMRUabefhiklmnoqstuv\n"))
             (should (looking-at (rx "[")))))))))
 
+(ert-deftest erc-scenarios-stamp--legacy-date-stamps ()
+  (with-suppressed-warnings ((obsolete erc-stamp-prepend-date-stamps-p))
+    (erc-scenarios-common-with-cleanup
+        ((erc-scenarios-common-dialog "base/reconnect")
+         (erc-stamp-prepend-date-stamps-p t)
+         (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect))
+         (port (process-contact dumb-server :service))
+         (erc-server-flood-penalty 0.1)
+         (expect (erc-d-t-make-expecter)))
+
+      (ert-info ("Connect")
+        (with-current-buffer (erc :server "127.0.0.1"
+                                  :port port
+                                  :full-name "tester"
+                                  :nick "tester")
+          (funcall expect 5 "Opening connection")
+          (goto-char (1- (match-beginning 0)))
+          (should (eq 'erc-timestamp (field-at-pos (point))))
+          (should (eq 'unknown (erc--get-inserted-msg-prop 'erc-msg)))
+          ;; Force redraw of date stamp.
+          (setq erc-timestamp-last-inserted-left nil)
+
+          (funcall expect 5 "This server is in debug mode")
+          (while (and (zerop (forward-line -1))
+                      (not (eq 'erc-timestamp (field-at-pos (point))))))
+          (should (erc--get-inserted-msg-prop 'erc-cmd)))))))
+
 ;;; erc-scenarios-stamp.el ends here
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 57bf5860ac4..1af087e7e31 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1432,6 +1432,80 @@
 
           (should-not calls))))))
 
+(ert-deftest erc--delete-inserted-message ()
+  (erc-mode)
+  (erc--initialize-markers (point) nil)
+  ;; Put unique invisible properties on the line endings.
+  (erc-display-message nil 'notice nil "one")
+  (put-text-property (1- erc-insert-marker) erc-insert-marker 'invisible 'a)
+  (let ((erc--msg-prop-overrides '((erc-msg . datestamp) (erc-ts . 0))))
+    (erc-display-message nil nil nil
+                         (propertize "\n[date]" 'field 'erc-timestamp)))
+  (put-text-property (1- erc-insert-marker) erc-insert-marker 'invisible 'b)
+  (erc-display-message nil 'notice nil "two")
+
+  (ert-info ("Date stamp deleted cleanly")
+    (goto-char 11)
+    (should (looking-at (rx "\n[date]")))
+    (should (eq 'datestamp (get-text-property (point) 'erc-msg)))
+    (should (eq (point) (field-beginning (1+ (point)))))
+
+    (erc--delete-inserted-message (point))
+
+    ;; Preceding line ending clobbered, replaced by trailing.
+    (should (looking-back (rx "*** one\n")))
+    (should (looking-at (rx "*** two")))
+    (should (eq 'b (get-text-property (1- (point)) 'invisible))))
+
+  (ert-info ("Markers at pos-bol preserved")
+    (erc-display-message nil 'notice nil "three")
+    (should (looking-at (rx "*** two")))
+
+    (let ((m (point-marker))
+          (n (point-marker))
+          (p (point)))
+      (set-marker-insertion-type m t)
+      (goto-char (point-max))
+      (erc--delete-inserted-message p)
+      (should (= (marker-position n) p))
+      (should (= (marker-position m) p))
+      (goto-char p)
+      (set-marker m nil)
+      (set-marker n nil)
+      (should (looking-back (rx "*** one\n")))
+      (should (looking-at (rx "*** three")))))
+
+  (ert-info ("Compat")
+    (erc-display-message nil 'notice nil "four")
+    (should (looking-at (rx "*** three\n")))
+    (with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p))
+      (let ((erc-legacy-invisible-bounds-p t))
+        (erc--delete-inserted-message (point))))
+    (should (looking-at (rx "*** four\n"))))
+
+  (ert-info ("Deleting most recent message preserves markers")
+    (let ((m (point-marker))
+          (n (point-marker))
+          (p (point)))
+      (should (equal "*** four\n" (buffer-substring p erc-insert-marker)))
+      (set-marker-insertion-type m t)
+      (goto-char (point-max))
+      (erc--delete-inserted-message p)
+      (should (= (marker-position m) p))
+      (should (= (marker-position n) p))
+      (goto-char p)
+      (should (looking-back (rx "*** one\n")))
+      (should (looking-at erc-prompt))
+      (erc--assert-input-bounds)
+
+      ;; However, `m' is now forever "trapped" at `erc-insert-marker'.
+      (erc-display-message nil 'notice nil "two")
+      (should (= m erc-insert-marker))
+      (goto-char n)
+      (should (looking-at (rx "*** two\n")))
+      (set-marker m nil)
+      (set-marker n nil))))
+
 (ert-deftest erc--order-text-properties-from-hash ()
   (let ((table (map-into '((a . 1)
                            (erc-ts . 0)
@@ -2617,8 +2691,8 @@
               (obarray (obarray-make))
               (err (should-error (erc--update-modules erc-modules))))
          (should (equal (cadr err) "`foo' is not a known ERC module"))
-         (should (equal (funcall get-calls)
-                        `((req . ,(intern-soft "erc-foo")))))))
+         (should (equal (mapcar #'prin1-to-string (funcall get-calls))
+                        '("(req . erc-foo)")))))
 
      ;; Module's mode command exists but lacks an associated file.
      (ert-info ("Bad autoload flagged as suspect")
@@ -2627,10 +2701,8 @@
               (obarray (obarray-make))
               (erc-modules (list (intern "foo"))))
 
-         ;; Create a mode activation command.
+         ;; Create a mode-activation command and make mode-var global.
          (funcall mk-cmd "foo")
-
-         ;; Make the mode var global.
          (funcall mk-global "foo")
 
          ;; No local modules to return.
@@ -2639,7 +2711,7 @@
                         '("foo")))
          ;; ERC requires the library via prefixed module name.
          (should (equal (mapcar #'prin1-to-string (funcall get-calls))
-                        `("(req . erc-foo)" "(erc-foo-mode . 1)"))))))))
+                        '("(req . erc-foo)" "(erc-foo-mode . 1)"))))))))
 
 ;; A local module (here, `lo2') lacks a mode toggle, so ERC tries to
 ;; load its defining library, first via the symbol property
diff --git a/test/lisp/erc/resources/erc-d/erc-d-t.el 
b/test/lisp/erc/resources/erc-d/erc-d-t.el
index cf869fb3c70..7126165fd91 100644
--- a/test/lisp/erc/resources/erc-d/erc-d-t.el
+++ b/test/lisp/erc/resources/erc-d/erc-d-t.el
@@ -157,6 +157,7 @@ ON-SUCCESS, is nonexistent.  To reset, specify a FROM 
argument."
   (let (positions)
     (lambda (timeout text &optional reset-from)
       (let* ((pos (cdr (assq (current-buffer) positions)))
+             (erc-d-t--wait-message-prefix (and (< timeout 0) "Sustaining: "))
              (cb (lambda ()
                    (unless pos
                      (push (cons (current-buffer) (setq pos (make-marker)))
diff --git a/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld 
b/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld
index 82c6d52cf7c..f966daeed1f 100644
--- a/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld
+++ b/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld
@@ -1 +1 @@
-#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging 
all user I/O. If you do not wish for everything you send to be readable by the 
server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a 
tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause 
to complain of? Come me to what was done to her.\n<bob> alice: Either your 
unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr  1 
2023]\n<bob> zero.[07:00]\n* bob [...]
+#("\n\n\n[Thu Jan  1 1970]\n*** This server is in debug mode and is logging 
all user I/O. If you do not wish for everything you send to be readable by the 
server owner(s), please disconnect.[00:00]\n<alice> bob: come, you are a 
tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause 
to complain of? Come me to what was done to her.\n<bob> alice: Either your 
unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr  1 
2023]\n<bob> zero.[07:00]\n<bob> [...]
\ No newline at end of file
diff --git a/test/lisp/gnus/gnus-group-tests.el 
b/test/lisp/gnus/gnus-group-tests.el
index e12f42711ea..3f5cbefc6ea 100644
--- a/test/lisp/gnus/gnus-group-tests.el
+++ b/test/lisp/gnus/gnus-group-tests.el
@@ -38,7 +38,7 @@
      ;; This is a very aggressive shortening of the left hand side.
      ("nnimap+email@banana.salesman.example.com:234" . "email@banana:234")
      ("nntp+some.where.edu:soc.motss" . "some:s.motss")
-     ("nntp+news.gmane.org:gmane.emacs.gnus.general" . "news:g.e.g.general";)
+     ("nntp+news.gmane.io:gmane.emacs.gnus.general" . "news:g.e.g.general";)
      ("nntp+news.gnus.org:gmane.text.docbook.apps" . "news:g.t.d.apps";)
 
      ;; nnimap groups.
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index 0d409cead26..db327056533 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -348,15 +348,17 @@
 (defalias 'subr-tests--parent-mode
   (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
 
+(define-derived-mode subr-tests--derived-mode-1 prog-mode "test")
+(define-derived-mode subr-tests--derived-mode-2 subr-tests--parent-mode "test")
 (ert-deftest provided-mode-derived-p ()
   ;; base case: `derived-mode' directly derives `prog-mode'
-  (should (progn
-            (define-derived-mode derived-mode prog-mode "test")
-            (provided-mode-derived-p 'derived-mode 'prog-mode)))
-  ;; edge case: `derived-mode' derives an alias of `prog-mode'
-  (should (progn
-            (define-derived-mode derived-mode subr-tests--parent-mode "test")
-            (provided-mode-derived-p 'derived-mode 'prog-mode))))
+  (should (provided-mode-derived-p 'subr-tests--derived-mode-1 'prog-mode))
+  ;; Edge cases: aliases along the derivation.
+  (should (provided-mode-derived-p 'subr-tests--parent-mode
+                                   'subr-tests--parent-mode))
+  (should (provided-mode-derived-p 'subr-tests--derived-mode-2
+                                   'subr-tests--parent-mode))
+  (should (provided-mode-derived-p 'subr-tests--derived-mode-2 'prog-mode)))
 
 (ert-deftest number-sequence-test ()
   (should (= (length



reply via email to

[Prev in Thread] Current Thread [Next in Thread]