emacs-diffs
[Top][All Lists]
Advanced

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

feature/native-comp 6523b84: Merge remote-tracking branch 'savannah/mast


From: Andrea Corallo
Subject: feature/native-comp 6523b84: Merge remote-tracking branch 'savannah/master' into HEAD
Date: Sun, 29 Nov 2020 09:31:52 -0500 (EST)

branch: feature/native-comp
commit 6523b8401519a29ca0aefaf44c3dfa36f681f64e
Merge: 2e0256e 38ed05f
Author: Andrea Corallo <akrl@sdf.org>
Commit: Andrea Corallo <akrl@sdf.org>

    Merge remote-tracking branch 'savannah/master' into HEAD
---
 build-aux/update-subdirs                   |   2 +-
 doc/emacs/killing.texi                     |  11 +-
 doc/emacs/search.texi                      |   9 +-
 doc/lispref/compile.texi                   |   6 +-
 doc/lispref/display.texi                   |  14 ++
 doc/lispref/edebug.texi                    |   4 +-
 doc/lispref/minibuf.texi                   |  16 +++
 doc/lispref/searching.texi                 |   4 +-
 doc/lispref/text.texi                      |   2 +-
 doc/misc/ebrowse.texi                      |   2 +-
 doc/misc/eshell.texi                       |   2 +-
 doc/misc/eww.texi                          |   6 +
 doc/misc/gnus.texi                         |  11 +-
 doc/misc/tramp.texi                        |   2 +-
 etc/NEWS                                   |  53 ++++++++
 etc/NEWS.27                                |   9 +-
 etc/tutorials/TUTORIAL                     |  18 +--
 etc/tutorials/TUTORIAL.de                  |   6 +-
 etc/tutorials/TUTORIAL.es                  |  28 ++--
 etc/tutorials/TUTORIAL.fr                  |  12 +-
 etc/tutorials/TUTORIAL.he                  |  17 ++-
 etc/tutorials/TUTORIAL.it                  |  73 +++++-----
 etc/tutorials/TUTORIAL.sv                  | 205 ++++++++++++++---------------
 lisp/allout.el                             |   3 +-
 lisp/arc-mode.el                           |  56 +++++---
 lisp/cedet/ede/pmake.el                    |   2 +-
 lisp/cedet/semantic/grammar.el             |   3 +-
 lisp/cus-edit.el                           |  13 +-
 lisp/custom.el                             |  10 +-
 lisp/delsel.el                             |   2 +
 lisp/emacs-lisp/bytecomp.el                |  11 +-
 lisp/emacs-lisp/easymenu.el                |   7 +-
 lisp/emacs-lisp/eldoc.el                   |   3 +-
 lisp/emacs-lisp/rx.el                      |   1 +
 lisp/emacs-lisp/tabulated-list.el          |   1 +
 lisp/erc/erc-menu.el                       |  15 +--
 lisp/eshell/esh-io.el                      |   3 +-
 lisp/face-remap.el                         |  51 +++++--
 lisp/files.el                              |   1 +
 lisp/filesets.el                           |  43 +++++-
 lisp/gnus/gnus-art.el                      |   1 -
 lisp/gnus/gnus-fun.el                      |   4 +-
 lisp/gnus/gnus-search.el                   |  48 ++++---
 lisp/gnus/gnus-sum.el                      |  16 ++-
 lisp/gnus/message.el                       |   2 -
 lisp/gnus/mml2015.el                       |   5 +-
 lisp/gnus/smime.el                         |   2 +-
 lisp/help-fns.el                           |  51 +++++--
 lisp/international/mule-cmds.el            |  15 +--
 lisp/isearch.el                            |  17 ++-
 lisp/leim/quail/arabic.el                  |   2 +-
 lisp/leim/quail/croatian.el                |   2 +-
 lisp/leim/quail/cyril-jis.el               |   2 +-
 lisp/leim/quail/cyrillic.el                |   2 +-
 lisp/leim/quail/czech.el                   |   2 +-
 lisp/leim/quail/ethiopic.el                |   2 +-
 lisp/leim/quail/georgian.el                |   2 +-
 lisp/leim/quail/greek.el                   |   2 +-
 lisp/leim/quail/hanja-jis.el               |   2 +-
 lisp/leim/quail/hanja.el                   |   2 +-
 lisp/leim/quail/hanja3.el                  |   2 +-
 lisp/leim/quail/hebrew.el                  |   2 +-
 lisp/leim/quail/ipa-praat.el               |   2 +-
 lisp/leim/quail/latin-alt.el               |   2 +-
 lisp/leim/quail/latin-post.el              |   2 +-
 lisp/leim/quail/latin-pre.el               |   2 +-
 lisp/leim/quail/persian.el                 |   2 +-
 lisp/leim/quail/programmer-dvorak.el       |   2 +-
 lisp/leim/quail/py-punct.el                |   2 +-
 lisp/leim/quail/pypunct-b5.el              |   2 +-
 lisp/leim/quail/rfc1345.el                 |   2 +-
 lisp/leim/quail/sami.el                    |   2 +-
 lisp/leim/quail/sgml-input.el              |   2 +-
 lisp/leim/quail/slovak.el                  |   2 +-
 lisp/leim/quail/symbol-ksc.el              |   2 +-
 lisp/leim/quail/tamil-dvorak.el            |   2 +-
 lisp/leim/quail/vntelex.el                 |   2 +-
 lisp/leim/quail/vnvni.el                   |   2 +-
 lisp/leim/quail/welsh.el                   |   2 +-
 lisp/mh-e/mh-folder.el                     |   7 +-
 lisp/mh-e/mh-identity.el                   |   2 +-
 lisp/mh-e/mh-letter.el                     |   2 +-
 lisp/mh-e/mh-search.el                     |   2 +-
 lisp/mh-e/mh-show.el                       |   7 +-
 lisp/minibuffer.el                         | 129 ++++++++++++------
 lisp/net/eww.el                            |   9 +-
 lisp/net/sieve.el                          |   3 +-
 lisp/net/tramp-sh.el                       |   2 +-
 lisp/net/tramp.el                          |  14 +-
 lisp/obsolete/otodo-mode.el                |   3 +-
 lisp/org/ob-picolisp.el                    |   6 +-
 lisp/org/ob-screen.el                      |   2 +-
 lisp/org/org-agenda.el                     |   1 -
 lisp/org/org-table.el                      |   5 +-
 lisp/org/org.el                            |   1 -
 lisp/outline.el                            |  59 +++++----
 lisp/play/fortune.el                       |   2 +-
 lisp/progmodes/antlr-mode.el               |   3 +-
 lisp/progmodes/cc-mode.el                  |  18 ++-
 lisp/progmodes/compile.el                  |  15 ++-
 lisp/progmodes/cperl-mode.el               |   4 +-
 lisp/progmodes/flymake.el                  |   2 +-
 lisp/progmodes/hideshow.el                 |   1 -
 lisp/progmodes/idlw-help.el                |  14 +-
 lisp/progmodes/idlw-shell.el               |   8 --
 lisp/progmodes/idlwave.el                  |   4 -
 lisp/progmodes/meta-mode.el                |   5 +-
 lisp/progmodes/octave.el                   |   4 +-
 lisp/progmodes/prolog.el                   |  14 +-
 lisp/progmodes/sh-script.el                |   8 +-
 lisp/progmodes/sql.el                      |  10 +-
 lisp/progmodes/tcl.el                      |   4 +-
 lisp/progmodes/vhdl-mode.el                |  12 +-
 lisp/server.el                             |  12 +-
 lisp/shell.el                              |   2 +-
 lisp/simple.el                             | 127 ++++++++++++++----
 lisp/speedbar.el                           |   6 +-
 lisp/subr.el                               |  23 +++-
 lisp/term.el                               |  11 +-
 lisp/textmodes/dns-mode.el                 |   3 +-
 lisp/textmodes/reftex-index.el             |   6 +-
 lisp/textmodes/reftex-toc.el               |   3 +-
 lisp/textmodes/reftex.el                   |   6 +-
 lisp/textmodes/table.el                    | 101 +++++++-------
 lisp/vc/diff-mode.el                       |   6 +-
 lisp/vc/ediff-mult.el                      |   2 +-
 lisp/vc/ediff-ptch.el                      |  14 +-
 lisp/vc/log-view.el                        |  10 +-
 lisp/vc/vc.el                              |   2 +-
 lisp/wdired.el                             |   2 +-
 lisp/wid-browse.el                         |   2 -
 lisp/wid-edit.el                           |  72 ++++++++--
 src/Makefile.in                            |   7 +
 src/alloc.c                                |  13 +-
 src/emacs-module.c                         | 104 ++++++---------
 src/eval.c                                 |  89 +++++++++++++
 src/font.c                                 |  17 +++
 src/frame.c                                |  16 ++-
 src/lisp.h                                 |  11 +-
 src/minibuf.c                              |   5 +-
 src/nsterm.m                               |   2 +-
 src/pdumper.c                              |   2 +-
 src/search.c                               |  18 +++
 src/w32fns.c                               |  14 +-
 src/xdisp.c                                |   2 +
 test/lisp/custom-tests.el                  |  38 ++++++
 test/lisp/emacs-lisp/ert-tests.el          |   5 +
 test/lisp/files-x-tests.el                 |   8 +-
 test/lisp/net/tramp-tests.el               |   4 +-
 test/lisp/progmodes/compile-tests.el       |   2 +
 test/lisp/subr-tests.el                    |  90 +++++++++++++
 test/lisp/wid-edit-tests.el                | 153 +++++++++++++++++++++
 test/src/emacs-module-resources/mod-test.c |  10 ++
 test/src/emacs-module-tests.el             |  50 +++++++
 154 files changed, 1683 insertions(+), 725 deletions(-)

diff --git a/build-aux/update-subdirs b/build-aux/update-subdirs
index 96712f0..c0ecb84 100755
--- a/build-aux/update-subdirs
+++ b/build-aux/update-subdirs
@@ -41,7 +41,7 @@ if [ "x$subdirs" = x ]; then
   rm -f subdirs.el
 else
   rm -f subdirs.el~
-  echo ";; In load-path, after this directory should come
+  echo ";; In load-path, after this directory should come  -*- 
lexical-binding: t -*-
 ;; certain of its subdirectories.  Here we specify them.
 (normal-top-level-add-to-load-path '($subdirs))
 ;; Local" "Variables:
diff --git a/doc/emacs/killing.texi b/doc/emacs/killing.texi
index bd7dbb6..0bd18fd 100644
--- a/doc/emacs/killing.texi
+++ b/doc/emacs/killing.texi
@@ -362,7 +362,7 @@ through the possibilities.
 that was yanked and replaces it with the text from an earlier kill.
 So, to recover the text of the next-to-the-last kill, first use
 @kbd{C-y} to yank the last kill, and then use @kbd{M-y} to replace it
-with the previous kill.  @kbd{M-y} is allowed only after a @kbd{C-y}
+with the previous kill.  This works only after a @kbd{C-y}
 or another @kbd{M-y}.
 
   You can understand @kbd{M-y} in terms of a last-yank pointer which
@@ -394,6 +394,15 @@ pointer remains at the same place in the kill ring, so 
repeating
   When you call @kbd{C-y} with a numeric argument, that also sets the
 last-yank pointer to the entry that it yanks.
 
+  Alternatively, when the previous command was not a yank command,
+@kbd{M-y} activates the minibuffer where you can browse previous kills
+using the minibuffer history commands (@pxref{Minibuffer History}), or
+you can use completion commands (@pxref{Completion}) on a list of
+previously killed blocks of text from the kill ring.  Exiting the
+minibuffer will insert the selected text to the buffer.  With a plain
+prefix argument (@kbd{C-u M-y}), this command leaves the cursor in
+front of the inserted text, and sets the mark at the end.
+
 @node Appending Kills
 @subsection Appending Kills
 
diff --git a/doc/emacs/search.texi b/doc/emacs/search.texi
index 0612c13..5be45ce 100644
--- a/doc/emacs/search.texi
+++ b/doc/emacs/search.texi
@@ -295,9 +295,12 @@ from point to the @var{n}th occurrence of the specified 
character.
 appends the current kill to the search string.  @kbd{M-y}
 (@code{isearch-yank-pop}), if called after @kbd{C-y}, replaces that
 appended text with an earlier kill, similar to the usual @kbd{M-y}
-(@code{yank-pop}) command (@pxref{Yanking}).  Clicking @kbd{mouse-2}
-in the echo area appends the current X selection (@pxref{Primary
-Selection}) to the search string (@code{isearch-yank-x-selection}).
+(@code{yank-pop}) command.  When @kbd{M-y} is called not after
+@kbd{C-y}, then it activates the minibuffer where you can select
+a previous kill to append to the search string (@pxref{Earlier
+Kills}).  Clicking @kbd{mouse-2} in the echo area appends the current
+X selection (@pxref{Primary Selection}) to the search string
+(@code{isearch-yank-x-selection}).
 
 @kindex C-M-d @r{(Incremental search)}
 @kindex C-M-y @r{(Incremental search)}
diff --git a/doc/lispref/compile.texi b/doc/lispref/compile.texi
index ad8afaa..51a4b04 100644
--- a/doc/lispref/compile.texi
+++ b/doc/lispref/compile.texi
@@ -199,7 +199,7 @@ $ ls -l push*
 @end example
 @end deffn
 
-@deffn Command byte-recompile-directory directory &optional flag force
+@deffn Command byte-recompile-directory directory &optional flag force 
follow-symlinks
 @cindex library compilation
 This command recompiles every @samp{.el} file in @var{directory} (or
 its subdirectories) that needs recompilation.  A file needs
@@ -218,6 +218,10 @@ Interactively, @code{byte-recompile-directory} prompts for
 If @var{force} is non-@code{nil}, this command recompiles every
 @samp{.el} file that has a @samp{.elc} file.
 
+This command will normally not compile @samp{.el} files that are
+symlinked.  If the optional @var{follow-symlink} parameter is
+non-@code{nil}, symlinked @samp{.el} will also be compiled.
+
 The returned value is unpredictable.
 @end deffn
 
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 6fc8587..f86baf5 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -3754,6 +3754,20 @@ Additional typographic style information for the font, 
such as
 The charset registry and encoding of the font, such as
 @samp{iso8859-1}.  The value should be a string or a symbol.
 
+@item :dpi
+The resolution in dots per inch for which the font is designed.  The
+value must be a non-negative number.
+
+@item :spacing
+The spacing of the font: proportional, dual, mono, or charcell.  The
+value should be either an integer (0 for proportional, 90 for dual,
+100 for mono, 110 for charcell) or a one-letter symbol (one of
+@code{P}, @code{D}, @code{M}, or @code{C}).
+
+@item :avgwidth
+The average width of the font in 1/10 pixel units.  The value should
+be a non-negative number.
+
 @item :script
 The script that the font must support (a symbol).
 
diff --git a/doc/lispref/edebug.texi b/doc/lispref/edebug.texi
index 820fdb9..b4c631b 100644
--- a/doc/lispref/edebug.texi
+++ b/doc/lispref/edebug.texi
@@ -877,8 +877,8 @@ If non-@code{nil}, Edebug binds @code{print-circle} to this 
value while
 printing results.  The default value is @code{t}.
 @end defopt
 
-  See @xref{Output Functions} for further details about how printing
-can be customized.
+  For further details about how printing can be customized, see
+@pxref{Output Functions}.
 
 @node Trace Buffer
 @subsection Trace Buffer
diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi
index f1cfd29..56bc0b8 100644
--- a/doc/lispref/minibuf.texi
+++ b/doc/lispref/minibuf.texi
@@ -1798,6 +1798,13 @@ buffer.  This function must accept one argument, a 
completion, and
 should either return @code{nil} or a string to be displayed next to
 the completion.
 
+@item :affixation-function
+The value should be a function to add prefixes and suffixes to
+completions.  This function must accept one argument, a list of
+completions, and should return such a list of completions where
+each element contains a list of three elements: a completion,
+a prefix string, and a suffix string.
+
 @item :exit-function
 The value should be a function to run after performing completion.
 The function should accept two arguments, @var{string} and
@@ -1897,6 +1904,15 @@ function should take one argument, @var{string}, which 
is a possible
 completion.  It should return a string, which is displayed after the
 completion @var{string} in the @file{*Completions*} buffer.
 
+@item affixation-function
+The value should be a function for adding prefixes and suffixes to
+completions.  The function should take one argument,
+@var{completions}, which is a list of possible completions.  It should
+return such a list of @var{completions} where each element contains a list
+of three elements: a completion, a prefix which is displayed before
+the completion string in the @file{*Completions*} buffer, and
+a suffix displayed after the completion string.
+
 @item display-sort-function
 The value should be a function for sorting completions.  The function
 should take one argument, a list of completion strings, and return a
diff --git a/doc/lispref/searching.texi b/doc/lispref/searching.texi
index 592b876..35a5188 100644
--- a/doc/lispref/searching.texi
+++ b/doc/lispref/searching.texi
@@ -1484,8 +1484,8 @@ Corresponding string regexp: @samp{\_>}
 @cindex @code{submatch} in rx
 Match the @var{rx}s, making the matched text and position accessible
 in the match data.  The first group in a regexp is numbered 1;
-subsequent groups will be numbered one higher than the previous
-group.@*
+subsequent groups will be numbered one above the previously
+highest-numbered group in the pattern so far.@*
 Corresponding string regexp: @samp{\(@dots{}\)}
 
 @item (group-n @var{n} @var{rx}@dots{})
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 550e7fe..c6ca4ee 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -1100,7 +1100,7 @@ one, it rotates the kill ring to place the yanked string 
at the front.
 This command replaces the just-yanked entry from the kill ring with a
 different entry from the kill ring.
 
-This is allowed only immediately after a @code{yank} or another
+This works only immediately after a @code{yank} or another
 @code{yank-pop}.  At such a time, the region contains text that was just
 inserted by yanking.  @code{yank-pop} deletes that text and inserts in
 its place a different piece of killed text.  It does not add the deleted
diff --git a/doc/misc/ebrowse.texi b/doc/misc/ebrowse.texi
index 1751a77..98c1a79 100644
--- a/doc/misc/ebrowse.texi
+++ b/doc/misc/ebrowse.texi
@@ -735,7 +735,7 @@ context menu.
 
 Classes can be marked for operations similar to the standard Emacs
 commands @kbd{M-x tags-search} and @kbd{M-x tags-query-replace} (see
-also @xref{Tags-like Functions}.)
+also @pxref{Tags-like Functions}.)
 
 @table @kbd
 @cindex toggle mark
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index c33ca0e..0c5501f 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -652,7 +652,7 @@ Eshell solves this problem by running such programs in 
Emacs's
 terminal emulator.
 
 Programs that need a terminal to display output properly are referred
-to in this manual as ``visual commands,'' because they are not simply
+to in this manual as ``visual commands'', because they are not simply
 line-oriented.  You must tell Eshell which commands are visual, by
 adding them to @code{eshell-visual-commands}; for commands that are
 visual for only certain @emph{sub}-commands -- e.g., @samp{git log} but
diff --git a/doc/misc/eww.texi b/doc/misc/eww.texi
index 1bccbd7..a2a21f0 100644
--- a/doc/misc/eww.texi
+++ b/doc/misc/eww.texi
@@ -262,6 +262,12 @@ fetching images that originate from the same source as the
 retrieving these images'' and @code{t} means ``always send cookies
 when retrieving these images''.
 
+@vindex eww-use-browse-url
+  When following links in EWW, @acronym{URL}s that match the
+@code{eww-use-browse-url} regexp will be passed to @code{browse-url}
+instead of EWW handling them itself.  The action can be further
+customized by altering @code{browse-url-handlers}.
+
 @vindex eww-header-line-format
 @cindex Header
   The header line of the EWW buffer can be changed by customizing
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index c6ce129..81ce139 100644
--- a/doc/misc/gnus.texi
+++ b/doc/misc/gnus.texi
@@ -5361,6 +5361,15 @@ articles with the same subject, go to the first unread 
article.
 
 This variable is not particularly useful if you use a threaded display.
 
+@item gnus-paging-select-next
+@vindex gnus-paging-select-next
+Control whether to select the next/previous article when paging (with
+commands like @kbd{SPC} or @kbd{DEL}).  If non-@code{nil}, select the
+next article when reaching the end of the article (or the previous
+article when paging backwards).
+
+If @code{nil}, don't do anything at the end/start of the articles.
+
 @item gnus-summary-check-current
 @vindex gnus-summary-check-current
 If non-@code{nil}, all the ``unread'' movement commands will not proceed
@@ -29151,7 +29160,7 @@ again and again.  @xref{MIME Commands}.
 @item
 The new hooks @code{gnus-gcc-pre-body-encode-hook} and
 @code{gnus-gcc-post-body-encode-hook} are run before/after encoding
-the message body of the Gcc copy of a sent message.  See
+the message body of the Gcc copy of a sent message.
 @xref{Archived Messages}.
 
 @end itemize
diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index 6738ed5..3a6e425 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -2180,7 +2180,7 @@ be recomputed.  To force @value{tramp} to recompute 
afresh, call
 
 Per default, @value{tramp} uses the command @command{/bin/sh} for
 starting a shell on the remote host.  This can be changed by setting
-the connection property @t{"remote-shell"}, see @xref{Predefined
+the connection property @t{"remote-shell"}; see @pxref{Predefined
 connection information}.  If you want, for example, use
 @command{/usr/bin/zsh} on a remote host, you might apply
 
diff --git a/etc/NEWS b/etc/NEWS
index 2cea803..e42a1bc 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -208,6 +208,13 @@ This command would previously not redefine values defined 
by these
 forms, but this command has now been changed to work more like
 'eval-defun', and reset the values as specified.
 
++++
+** Standalone 'M-y' uses the minibuffer to complete previous kills.
+When 'M-y' is typed not after a yank command, it activates the minibuffer
+where you can browse previous kills using the minibuffer history or
+completion.  In Isearch 'C-s M-y' uses the minibuffer with completion
+on previous kills to read a string and append it to the search string.
+
 ---
 ** New user options 'copy-region-blink-delay' and 'delete-pair-blink-delay'.
 'copy-region-blink-delay' specifies a delay to indicate the region
@@ -482,6 +489,12 @@ tags to be considered as well.
 ** Gnus
 
 +++
+*** New user option 'gnus-paging-select-next'.
+This controls what happens when using commands like `SPC' and `DEL' to
+page the current article.  If non-nil (the default), go to the
+next/prev article, but if nil, do nothing at the end/start of the article.
+
++++
 *** New gnus-search library.
 A new unified search syntax which can be used across multiple
 supported search engines.  Set 'gnus-search-use-parsed-queries' to
@@ -1104,6 +1117,10 @@ background colors or transparency, such as xbm, pbm, 
svg, png and gif.
 ** EWW
 
 +++
+*** New user option 'eww-use-browse-url'.
+This is a regexp that can be set to alter how links are followed in eww.
+
++++
 *** New user option 'eww-retrieve-command'.
 This can be used to download data via an external command.  If nil
 (the default), then 'url-retrieve' is used.
@@ -1328,6 +1345,13 @@ This new command (bound to 'C-c C-l') regenerates the 
current hunk.
 
 ** Miscellaneous
 
+*** New user option 'completions-detailed'.
+When non-nil, some commands like 'describe-symbol' show more detailed
+completions with more information in completion prefix and suffix.
+
+---
+*** User option 'completions-format' supports a new value 'one-column'.
+
 ---
 *** New user option 'bibtex-unify-case-convert'.
 This new option allows the user to customize how case is converted
@@ -1503,6 +1527,18 @@ mouse now pops up a TTY menu by default instead of 
running the command
 'tmm-menubar'.  To restore the old behavior, set the user option
 'tty-menu-open-use-tmm' to non-nil.
 
+** text-scale-mode
+
+---
+*** text-scale-mode can now adjust font size of the header line.
+When the new buffer local variable 'text-scale-remap-header-line'
+is non-nil, 'text-scale-adjust' will also scale the text in the header
+line when displaying that buffer.
+
+This is useful for major modes that arrange their display in a tabular
+form below the header line.  It is enabled by default in
+'tabulated-list-mode' and its derived modes.
+
 ** xwidget-webkit mode
 
 *** New xwidget commands.
@@ -1679,6 +1715,7 @@ parameter.
 'previous-system-time-locale' have been removed, as they were created
 by mistake and were not useful to Lisp code.
 
+---
 ** The 'load-dangerous-libraries' variable is now obsolete.
 It was used to allow loading Lisp libraries compiled by XEmacs, a
 modified version of Emacs which is no longer actively maintained.
@@ -1693,6 +1730,7 @@ Use macro 'with-current-buffer-window' with action alist 
entry 'body-function'.
 To load the file after byte-compiling, add a call to 'load' from Lisp
 or use 'M-x emacs-lisp-byte-compile-and-load' interactively.
 
+---
 ** The metamail.el library is now marked obsolete.
 
 ---
@@ -1777,6 +1815,17 @@ ledit.el, lmenu.el, lucid.el and old-whitespace.el.
 
 * Lisp Changes in Emacs 28.1
 
+---
+** `defvar` detects the error of defining a variable currently lexically bound.
+Such mixes are always signs that the outer lexical binding was an
+error and should have used dynamic binding instead.
+
++++
+** New completion function 'affixation-function' to add prefix/suffix.
+It accepts a list of completions and should return a list where
+each element is a list with three elements: a completion,
+a prefix string, and a suffix string.
+
 +++
 ** 'read-char-from-minibuffer' and 'y-or-n-p' support 'help-form'.
 If you bind 'help-form' to a non-nil value while calling these functions,
@@ -1972,6 +2021,10 @@ to lexical binding, where dynamic (special) variables 
bound in one
 file can affect code in another.  For details, see the manual section
 "(Elisp) Converting to Lexical Binding".
 
++++
+** 'byte-recompile-directory' can now compile symlinked .el files.
+This is achieved by giving a non-nil FOLLOW-SYMLINKS parameter.
+
 ---
 ** 'unload-feature' now also tries to undo additions to buffer-local hooks.
 
diff --git a/etc/NEWS.27 b/etc/NEWS.27
index 61f9c0e..4855cd3 100644
--- a/etc/NEWS.27
+++ b/etc/NEWS.27
@@ -48,7 +48,7 @@ skip leading or trailing empty lines of the buffer.
 
 ** Tramp
 
-*** The user option 'tramp-completion-reread-directory-timeout' is made 
obsolete.
+*** The user option 'tramp-completion-reread-directory-timeout' is now 
obsolete.
 
 
 * New Modes and Packages in Emacs 27.2
@@ -62,6 +62,13 @@ skip leading or trailing empty lines of the buffer.
 
 * Changes in Emacs 27.2 on Non-Free Operating Systems
 
+** Emacs now ignores modifier keys when IME input is used.
+By default, pressing Ctrl, Shift, and Alt keys while using IME input
+will no longer apply the modifiers to the produced characters, as
+there are IMEs which use keys with modifiers to input some
+characters.  Customize the variable 'w32-ignore-modifiers-on-IME-input'
+to nil to get back the old behavior.
+
 
 * Installation Changes in Emacs 27.1
 
diff --git a/etc/tutorials/TUTORIAL b/etc/tutorials/TUTORIAL
index 319ba52..a5b4b76 100644
--- a/etc/tutorials/TUTORIAL
+++ b/etc/tutorials/TUTORIAL
@@ -1,13 +1,13 @@
 Emacs tutorial.  See end for copying conditions.
 
-Emacs commands generally involve the CONTROL key (sometimes labeled
-CTRL or CTL) or the META key (sometimes labeled EDIT or ALT).  Rather than
-write that in full each time, we'll use the following abbreviations:
+Emacs commands generally involve the CONTROL key (often labeled CTRL)
+or the META key (usually labeled ALT).  Rather than writing that
+in full each time, we'll use the following abbreviations:
 
  C-<chr>  means hold the CONTROL key while typing the character <chr>
          Thus, C-f would be: hold the CONTROL key and type f.
- M-<chr>  means hold the META or EDIT or ALT key down while typing <chr>.
-         If there is no META, EDIT or ALT key, instead press and release the
+ M-<chr>  means hold the META or ALT key down while typing <chr>.
+         If there is no META or ALT key, instead press and release the
          ESC key and then type <chr>.  We write <ESC> for the ESC key.
 
 Important note: to end the Emacs session, type C-x C-c.  (Two characters.)
@@ -33,7 +33,7 @@ that is called "editing" and that's what Emacs is for.
 The first thing that you need to know is how to move around from place
 to place in the text.  You already know how to move forward one screen,
 with C-v.  To move backwards one screen, type M-v (hold down the META key
-and type v, or type <ESC>v if you do not have a META, EDIT, or ALT key).
+and type v, or type <ESC>v if you do not have a META or ALT key).
 
 >> Try typing M-v and then C-v, a few times.
 
@@ -196,7 +196,7 @@ easily learn to use other advanced cursor motion commands 
as well.
 Most Emacs commands accept a numeric argument; for most commands, this
 serves as a repeat-count.  The way you give a command a repeat count
 is by typing C-u and then the digits before you type the command.  If
-you have a META (or EDIT or ALT) key, there is another, alternative way
+you have a META (or ALT) key, there is another, alternative way
 to enter a numeric argument: type the digits while holding down the
 META key.  We recommend learning the C-u method because it works on
 any terminal.  The numeric argument is also called a "prefix argument",
@@ -676,7 +676,7 @@ another in the buffer.  When you type M-x, Emacs prompts 
you at the
 bottom of the screen with M-x and you should type the name of the
 command; in this case, "replace-string".  Just type "repl s<TAB>" and
 Emacs will complete the name.  (<TAB> is the Tab key, usually found
-above the CapsLock or Shift key near the left edge of the keyboard.)
+above the Caps Lock or Shift key near the left edge of the keyboard.)
 Submit the command name with <Return>.
 
 The replace-string command requires two arguments--the string to be
@@ -1024,7 +1024,7 @@ very brief documentation--sufficient to remind you of 
commands you
 have already learned.
 
 Multi-character commands such as C-x C-s and <ESC>v (instead of M-v,
-if you have no META or EDIT or ALT key) are also allowed after C-h c.
+if you have no META or ALT key) are also allowed after C-h c.
 
 To get more information about a command, use C-h k instead of C-h c.
 
diff --git a/etc/tutorials/TUTORIAL.de b/etc/tutorials/TUTORIAL.de
index ae58fc9..e5ac254 100644
--- a/etc/tutorials/TUTORIAL.de
+++ b/etc/tutorials/TUTORIAL.de
@@ -1,8 +1,8 @@
 Einführung in Emacs.  Siehe Dateiende für Vervielfältigungsbedingungen.
 
 Emacs-Befehle beinhalten im allgemeinen die CONTROL-Taste (manchmal
-auch als CTRL, CTL oder STRG beschriftet) sowie die META-Taste (auch
-EDIT oder ALT genannt).  Folgende Abkürzungen werden verwendet:
+auch als CTRL oder STRG beschriftet) sowie die META-Taste (oft
+ALT genannt).  Folgende Abkürzungen werden verwendet:
 
  C-<Zeichen>  bedeutet, dass die CONTROL-Taste gedrückt sein muss,
               während man das Zeichen <Zeichen> eingibt.  Beispiel:
@@ -264,7 +264,7 @@ Die meisten Emacs-Befehle akzeptieren ein numerisches 
Argument, das in
 der Regel als Wiederholungszähler dient (d.h., wie oft der Befehl
 ausgeführt werden soll).  Eingegeben wird diese Zahl mit C-u, dann die
 Ziffern und dann der Befehl selbst.  Alternativ können Sie die
-META-Taste (bzw. EDIT- oder ALT-Taste) gedrückt halten und dann die
+META-Taste (oder ALT-Taste) gedrückt halten und dann die
 Ziffern des Wiederholungszählers eingeben.  Wir empfehlen allerdings,
 die C-u-Methode zu lernen, da sie mit jedem Terminal funktioniert.
 Das numerische Argument wird auch »Präfix-Argument« genannt, da man es
diff --git a/etc/tutorials/TUTORIAL.es b/etc/tutorials/TUTORIAL.es
index 66ff3c3..faecb26 100644
--- a/etc/tutorials/TUTORIAL.es
+++ b/etc/tutorials/TUTORIAL.es
@@ -2,16 +2,16 @@ Tutorial de Emacs.  Vea al final las condiciones de copiado.
 
 Generalmente los comandos de Emacs involucran la tecla CONTROL
 (algunas veces llamada CTRL O CTL) o la tecla meta (algunas veces
-llamada EDIT o ALT).  En lugar de escribir completamente esto en cada
+llamada ALT).  En lugar de escribir completamente esto en cada
 ocasión, usaremos las siguientes abreviaturas.
 
  C-<car> significa mantener presionada la tecla CONTROL mientras
          teclea el carácter <car>.  Por lo tanto C-f será: Mantenga
          presionada la tecla CONTROL y teclee f.
- M-<car> significa mantener presionada la tecla META o EDIT o ALT
-        mientras teclea <car>.  Si no hay teclas META, EDIT o ALT, en
-        su lugar presione y libere la tecla ESC y luego teclee
-        <car>.  Escribimos <ESC> para referirnos a la tecla ESC.
+ M-<car> significa mantener presionada la tecla META o ALT mientras
+         teclea <car>.  Si no hay teclas META o ALT, en su lugar
+         presione y libere la tecla ESC y luego teclee <car>.
+         Escribimos <ESC> para referirnos a la tecla ESC.
 
 Nota importante: para terminar la sesión de Emacs teclee C-x C-c (dos
 caracteres).  Para cancelar un comando parcialmente introducido,
@@ -32,7 +32,7 @@ texto.
 Lo primero que necesita saber es cómo moverse de un lugar a otro en el
 texto.  Ya sabe cómo avanzar una pantalla, con C-v.  Para retroceder
 una pantalla teclee M-v (mantenga oprimida la tecla META y teclee v, o
-teclee <ESC>v si no tiene las teclas META, EDIT o ALT).
+teclee <ESC>v si no tiene las teclas META o ALT).
 
 >> Intente teclear M-v y luego C-v, varias veces.
 
@@ -203,12 +203,12 @@ La mayoría de comandos de Emacs aceptan un argumento 
numérico; para la
 mayoría de comandos esto sirve como un factor de repetición.  La
 manera de pasarle un factor de repetición a un comando es tecleando
 C-u y luego los dígitos antes de introducir los comandos.  Si tiene
-una tecla META (o EDIT o ALT), hay una manera alternativa para
-ingresar un argumento numérico: teclear los dígitos mientras presiona
-la tecla META.  Recomendamos aprender el método C-u porque éste
-funciona en cualquier terminal.  El argumento numérico es también
-llamado un «argumento prefijo», porque usted teclea el argumento antes
-del comando al que se aplica.
+una tecla META (o ALT), hay una manera alternativa para ingresar un
+argumento numérico: teclear los dígitos mientras presiona la tecla
+META.  Recomendamos aprender el método C-u porque éste funciona en
+cualquier terminal.  El argumento numérico es también llamado un
+«argumento prefijo», porque usted teclea el argumento antes del
+comando al que se aplica.
 
 Por ejemplo, C-u 8 C-f mueve hacia adelante ocho caracteres.
 
@@ -1075,8 +1075,8 @@ como una breve documentación: suficiente para recordarle 
los comandos
 que ha aprendido.
 
 Los comandos de múltiples caracteres tales como C-x C-s y (si no tiene
-las teclas META o EDIT o ALT) <ESC>v también están permitidos después
-de C-h c.
+las teclas META o ALT) <ESC>v también están permitidos después de
+C-h c.
 
 Para conseguir más información sobre un comando use C-h k en vez de
 C-h c.
diff --git a/etc/tutorials/TUTORIAL.fr b/etc/tutorials/TUTORIAL.fr
index 3148c3f..6dc0651 100644
--- a/etc/tutorials/TUTORIAL.fr
+++ b/etc/tutorials/TUTORIAL.fr
@@ -1,13 +1,13 @@
 Didacticiel d'Emacs.  Voir la fin de ce document pour les conditions.
 
 Les commandes Emacs utilisent généralement la touche CONTROLE (souvent
-désignée par CTRL ou CTL) ou la touche META (souvent désignée par EDIT
-ou ALT). Pour ces touches, nous utiliserons les abréviations suivantes :
+désignée par CTRL) ou la touche META (plus souvent désignée par ALT).
+Pour ces touches, nous utiliserons les abréviations suivantes :
 
  C-<car> signifie qu'il faut maintenir la touche CONTROLE appuyée tout
          en tapant le caractère <car>. Ainsi, C-f signifie : presser
          sur la touche CONTROLE tout en pressant la touche f.
- M-<car> signifie qu'il faut maintenir la touche META ou EDIT ou ALT
+ M-<car> signifie qu'il faut maintenir la touche META ou ALT
         appuyée tout en tapant le caractère <car>. Si aucune de ces
         touches n'existe, pressez puis relâchez la touche ESC et
         tapez <car>. Nous écrirons <ESC> pour désigner la touche ESC.
@@ -31,7 +31,7 @@ La première chose que vous devez savoir est comment vous 
déplacer à
 travers le texte. Vous savez déjà comment avancer d'un écran avec
 C-v. Pour revenir un écran en arrière, tapez M-v (pressez la touche
 META tout en appuyant sur v ou faites <ESC>v si vous n'avez pas de
-touche META, EDIT ou ALT).
+touche META ou ALT).
 
 >> Faites M-v, puis C-v plusieurs fois.
 
@@ -208,7 +208,7 @@ La plupart des commandes Emacs acceptent un paramètre 
numérique qui,
 la plupart du temps, indique un nombre de répétitions. Pour indiquer à
 une commande le nombre de fois que l'on souhaite la répéter, on
 utilise C-u suivi du nombre avant de taper la commande. Si vous avez
-une touche META (ou EDIT ou ALT), il existe une autre façon d'entrer
+une touche META (ou ALT), il existe une autre façon d'entrer
 un paramètre numérique : tapez le nombre tout en pressant la touche
 META. Nous vous conseillons d'apprendre à utiliser la méthode C-u car
 elle fonctionne sur tous les types de terminaux. Le paramètre
@@ -1101,7 +1101,7 @@ suffisant pour vous rappeler les commandes que vous avez 
déjà
 apprises.
 
 Les commandes multi-caractères, comme C-x C-s et (si vous n'avez ni
-touche META, ni touche EDIT, ni touche ALT) <ESC>v sont également
+touche META, ni touche ALT) <ESC>v sont également
 possibles après C-h c.
 
 Pour obtenir plus d'informations sur une commande, faites C-h k au
diff --git a/etc/tutorials/TUTORIAL.he b/etc/tutorials/TUTORIAL.he
index 907da24..1478fde 100644
--- a/etc/tutorials/TUTORIAL.he
+++ b/etc/tutorials/TUTORIAL.he
@@ -1,13 +1,13 @@
 שיעור ראשון בשימוש ב־‫Emacs‬. זכויות שימוש ראה בסוף המסמך.
 
-פקודות רבות של Emacs משתמשות במקש CONTROL (לפעמים הוא מסומן ב־CTRL או CTL)
-או במקש META (לפעמים מסומן EDIT או ALT). במקום לציין את כל השמות האפשריים
+פקודות רבות של Emacs משתמשות במקש CONTROL (בדרך־כלל מסומן ב־CTRL)
+או במקש META (בדרך־כלל מסומן ALT). במקום לציין את כל השמות האפשריים
 בכל פעם, נשתמש בקיצורים הבאים:
 
 ‏<תו>-C  משמעותו לחץ והחזק מקש CONTROL ואז הקש על מקש <תו>.
          לדוגמא, C-f משמעותו: לחץ והחזק CONTROL והקש על f.
-‏<תו>-M  משמעותו לחץ והחזק מקש META או EDIT או ALT ואז הקש על מקש <תו>.
-         אם במקלדת אין אף אחד ממקשי META או EDIT או ALT, אפשר להקיש
+‏<תו>-M  משמעותו לחץ והחזק מקש META או ALT ואז הקש על מקש <תו>.
+         אם במקלדת אין אף אחד ממקשי META או ALT, אפשר להקיש
         ולשחרר מקש ESC ואז להקיש <תו>. אנו נכתוב <ESC> עבור מקש ESC.
 
 הערה חשובה: כדי לצאת מ־Emacs יש להקיש C-x C-c (שני תוים, משמאל לימין).
@@ -32,7 +32,7 @@ Emacs.
 דבר ראשון שעליכם ללמוד הוא כיצד לנוע ממקום אחד למשנהו בתוך הטקסט. אתם
 כבר יודעים כיצד להתקדם לעמוד הבא, עם C-v. לחזרה לעמוד הקודם הקישו M-v
 (החזיקו מקש META והקישו v או הקישו ‪<ESC>v‬ אם אין במקלדת מקש META
-או EDIT או ALT).
+או ALT).
 
 >>  נסו עתה כמה פעמים להקיש M-v ואחר־כך C-v.
 
@@ -181,7 +181,7 @@ M-f עובר את המילה הבאה ונעצר בסופה. M-b פועל באו
 רוב הפקודות של Emacs מקבלות ארגומנט נומרי; עבור רוב הפקודות הארגומנט
 משמש כמונה של מספר החזרות על הפקודה. כדי לספק ארגומנט לפקודה, יש להקיש
 C-u ואחר־כך ספרות, וזאת לפני שמקישים את הפקודה עצמה. עם במקלדת קיים
-מקש META (או EDIT או ALT), יש גם אפשרות אחרת לציין ארגומנט נומרי:
+מקש META (או ALT), יש גם אפשרות אחרת לציין ארגומנט נומרי:
 הקישו את הספרות תוך כדי החזקת מקש META. אנו ממליצים על C-u משום שהוא
 יעבוד עם כל מקלדת. הארגומנט הנומרי נקרא גם "ארגומנט קידומת" (prefix
 argument) משום מקישים אותו לפני הפקודה אליה הוא מתייחס.
@@ -605,7 +605,7 @@ replace-string (החלף מחרוזת) אשר מחליפה מחרוזת אחת 
 אחרי שתקישו M-x, ‏Emacs מציג M-x בתחתית התצוגה ומחכה שתקישו את שם
 הפקודה, במקרה זה "replace-string". מספיק שתקישו "repl s<TAB>‎" ו־Emacs
 ישלים את השם המלא. (<TAB> הוא מקש Tab, בדרך כלל תמצאו אותו מעל מקש
-ה־CapsLock או Shift, ליד הקצה השמאלי של המקלדת.) סיימו את הזנת הפקודה
+ה־Caps Lock או Shift, ליד הקצה השמאלי של המקלדת.) סיימו את הזנת הפקודה
 ע״י הקשת <Return>.
 
 הפקודה להחלפת מחרוזת זקוקה לשני ארגומנטים -- המחרוזת שתוחלף וזו שתחליף
@@ -919,8 +919,7 @@ M-x help <Return>‎ כתחליף.)
 לכם את הפקודות שלמדתם בעבר.
 
 ניתן לציין אחרי C-h c גם פקודות שמופעלות ע״י סדרת מקשים באורך גדול
-מאחד, כגון C-x C-s או ‏‎<ESC> v (כתחליף ל־M-v, אם אין מקש META או EDIT
-או ALT).
+מאחד, כגון C-x C-s או ‏‎<ESC> v (כתחליף ל־M-v, אם אין מקש META או ALT).
 
 לקבלת מידע מפורט יותר על פקודה, השתמשו בפקודה C-h k במקום C-h c.
 
diff --git a/etc/tutorials/TUTORIAL.it b/etc/tutorials/TUTORIAL.it
index e19486a..448937b 100644
--- a/etc/tutorials/TUTORIAL.it
+++ b/etc/tutorials/TUTORIAL.it
@@ -1,18 +1,18 @@
 Esercitazione di Emacs.  Condizioni d'uso alla fine del file.
 
-I comandi di Emacs comportano generalmente l'uso del tasto CONTROL (a
-volte indicato con CTRL o CTL) o del tasto META (a volte indicato con
-EDIT o ALT).  Piuttosto che indicarli per esteso ogni volta, useremo
+I comandi di Emacs comportano generalmente l'uso del tasto CONTROL
+(spesso indicato con CTRL) o del tasto META (di solito indicato con
+ALT).  Piuttosto che indicarli per esteso ogni volta, useremo
 le seguenti abbreviazioni:
 
  C-<car>  significa che bisogna tenere abbassato il tasto CONTROL
          mentre si preme il carattere <car>.  Quindi C-f significa:
          tieni premuto CONTROL e batti f.
- M-<car>  significa che bisogna tenere abbassato il tasto META o EDIT
-         o ALT mentre si preme il carattere <car>.  Se non ci sono
-         tasti META, EDIT o ALT, al loro posto si può premere e poi
-         rilasciare il tasto ESC e quindi premere <car>.  Useremo
-         <ESC> per indicare il tasto ESC.
+ M-<car>  significa che bisogna tenere abbassato il tasto META o ALT
+         mentre si preme il carattere <car>.  Se non ci sono tasti
+         META o ALT, al loro posto si può premere e poi rilasciare
+         il tasto ESC e quindi premere <car>.  Useremo <ESC> per
+         indicare il tasto ESC.
 
 Nota importante: per chiudere una sessione di lavoro di Emacs usa C-x
 C-c (due caratteri).
@@ -20,7 +20,7 @@ Per annullare un comando inserito parzialmente usa C-g.
 Per terminare l'esercitazione, usa C-x k quindi <Invio> al prompt.
 I caratteri “>>” posti al margine sinistro indicano le direttive per
 provare a usare un comando.  Per esempio:
-<<Blank lines inserted here by startup of help-with-tutorial>>
+<<Blank lines inserted around following line by help-with-tutorial>>
 [Spaziatura inserita a scopo didattico.  Il testo continua sotto]
 >> Adesso premi C-v (vedi schermata successiva) per spostarti alla
        prossima schermata (vai avanti, tieni premuto il tasto
@@ -33,11 +33,12 @@ alla schermata successiva, favorendo così la continuità di 
lettura.
 La prima cosa che bisogna imparare è come raggiungere un certo punto
 del testo.  Sai già come andare avanti di una schermata, con C-v.  Per
 andare indietro di una schermata, premi M-v (tieni premuto il tasto
-META e poi premi v, oppure usa <ESC>v se non c'è un tasto META, EDIT o
-ALT).
+META e poi premi v, oppure usa <ESC>v se non c'è un tasto META o ALT).
 
 >> Ora prova: premi M-v e quindi C-v alcune volte.
 
+Puoi ovviamente spostarti avanti e indietro in questo testo in altri
+modi, se li conosci.
 
 * SOMMARIO
 ----------
@@ -207,11 +208,11 @@ Molti comandi di Emacs accettano un argomento numerico 
che spesso
 serve a conteggiare per quante volte vanno ripetuti.  Il modo in cui
 si può fornire ad un comando il numero di ripetizioni è il seguente:
 si usa C-u e quindi si indicano le cifre prima di impartire il comando
-stesso.  Se esiste un tasto META (o EDIT o ALT) c'è un modo
-alternativo: si battono le cifre tenendo premuto il tasto META.  Noi
-consigliamo di imparare il metodo con C-u perché funziona su tutti i
-terminali.  L'argomento numerico è anche chiamato “argomento
-prefisso”, perché viene indicato prima del comando a cui si riferisce.
+stesso.  Se esiste un tasto META (o ALT) c'è un modo alternativo: si
+battono le cifre tenendo premuto il tasto META.  Noi consigliamo di
+imparare il metodo con C-u perché funziona su tutti i terminali.
+L'argomento numerico è anche chiamato “argomento prefisso”, perché
+viene indicato prima del comando a cui si riferisce.
 
 Per esempio, C-u 8 C-f sposta il cursore in avanti di otto caratteri.
 
@@ -502,9 +503,10 @@ usare per annullare l'inserimento del testo).
 >> Elimina questa riga con C-k poi usa C-/ e dovrebbe ricomparire.
 
 C-_ è un comando di annullamento alternativo, funziona esattamente
-come C-/.  Su alcuni terminali, la sequenza C-/ invia effettivamente
-C-_ a Emacs.  Alternativamente, anche C-x u ha la stessa funzione di
-C-/, ma è leggermente più scomoda da inserire.
+come C-/.  Su alcune tastiere non è necessario usare il tasto shift
+per inserire C-_. Su alcuni terminali, la sequenza C-/ invia
+effettivamente C-_ a Emacs.  Alternativamente, anche C-x u ha la
+stessa funzione di C-/, ma è leggermente più scomoda da inserire.
 
 Un argomento numerico per C-/, C-_ o C-x u agisce come numero delle
 ripetizioni da effettuare.
@@ -651,11 +653,12 @@ la possibilità di salvare il buffer del primo file: 
sarebbe fastidioso
 dover prima passare a quel buffer per salvarlo con C-x C-s.  Così c'è
 il comando
 
-       C-x s   Salva alcuni buffer
+       C-x s   Salva alcuni buffer nei loro file
 
-C-x s chiede conferma del salvataggio per ogni buffer che contiene
-testo modificato e non ancora salvato.  Chiede, per ognuno di quei
-buffer, se si voglia salvarne il contenuto nel file corrispondente.
+C-x s chiede conferma del salvataggio per ogni buffer associato ad un
+file che contiene testo modificato e non ancora salvato.  Chiede, per
+ognuno di quei buffer, se si voglia salvarne il contenuto nel file
+corrispondente.
 
 >> Inserisci una riga di testo e poi premi C-x s.
    Dovrebbe chiederti se vuoi salvare il file chiamato “...TUTORIAL”.
@@ -701,14 +704,14 @@ gestisce la posta.
 
 Ci sono molti comandi C-x.  Ecco una lista di quelli già conosciuti:
 
-       C-x C-f    Apri un file.
-       C-x C-s    Salva un file.
-       C-x s      Salva alcuni buffer.
-       C-x C-b    Elenca buffer.
-       C-x b      Passa a un altro buffer.
-       C-x C-c    Chiudi Emacs.
-       C-x 1      Elimina tutte le finestre tranne una.
-       C-x u      Annulla.
+       C-x C-f    Apri un file
+       C-x C-s    Salva il buffer sul file
+       C-x s      Salva alcuni buffer sui loro file
+       C-x C-b    Elenca buffer
+       C-x b      Passa a un altro buffer
+       C-x C-c    Chiudi Emacs
+       C-x 1      Elimina tutte le finestre tranne una
+       C-x u      Annulla
 
 I comandi estesi con nome sono usati ancora meno spesso, oppure sono
 usati solo in certe modalità.  Un esempio è il comando replace-string
@@ -749,7 +752,7 @@ salvataggio automatico.
 Se il computer si blocca si può recuperare il file salvato
 automaticamente aprendo il file in modo normale (il file che si stava
 scrivendo, non quello di salvataggio automatico) e usando poi M-x
-recover-file<Invio>.  Quando viene chiesta la conferma si risponda
+recover-this-file<Invio>.  Quando viene chiesta la conferma si risponda
 con yes<Invio> per procedere con il recupero dei dati salvati
 automaticamente.
 
@@ -1091,8 +1094,8 @@ quindi anche come breve descrizione, sufficiente per 
ricordarsi di
 comandi già imparati.
 
 I comandi con più caratteri come ad esempio C-x C-s e (se non c'è il
-tasto META o EDIT o ALT) <ESC>v sono permessi allo stesso modo dopo
-una richiesta di aiuto fatta con C-h c.
+tasto META o ALT) <ESC>v sono permessi allo stesso modo dopo una
+richiesta di aiuto fatta con C-h c.
 
 Per avere ulteriori informazioni su un comando si usa C-h k invece che
 C-h c.
@@ -1156,7 +1159,7 @@ Puoi imparare di più su Emacs leggendo il suo manuale, 
sia nella forma
 stampata piuttosto che da dentro Emacs stesso (usa il menu Help oppure
 C-h r).  Due funzionalità che possono farti comodo sono il
 completamento automatico, che consente di ridurre il numero di
-caratteri da digitare, e dired, che semplifica la gestione dei file.
+caratteri da digitare, e Dired, che semplifica la gestione dei file.
 
 Il completamento è un modo per evitare la pressione di tasti quando
 non sia necessario.  Ad esempio, quando vuoi passare al buffer
diff --git a/etc/tutorials/TUTORIAL.sv b/etc/tutorials/TUTORIAL.sv
index 231d8a0..fcc388f 100644
--- a/etc/tutorials/TUTORIAL.sv
+++ b/etc/tutorials/TUTORIAL.sv
@@ -1,14 +1,16 @@
 Emacs användarhandledning. I slutet finns kopieringsvillkoren.
 
-Emacs-kommandon innebär ofta användning av kontrolltangenten (vanligen
-märkt CTRL eller CTL) eller META-tangenten (på vissa tangentbord märkt
-ALT eller EDIT). Vi använder här följande förkortningar:
-
- C-<chr> håll ner kontrolltangenten samtidigt som du skriver bokstaven
-         <chr>. C-f betyder: håll ner kontrolltangenten och tryck f.
- M-<chr> håll ner META-tangenten samtidigt som du skriver <chr>. Om
-         META-tangent saknas trycker du <ESC>, ESC-tangenten, släpper
-         den och trycker sedan <chr>.
+Emacs-kommandon använder ofta kontrolltangenten (ofta märkt CONTROL
+eller CTRL) eller META-tangenten (vanligen märkt ALT).
+Istället för att skriva ut deras namn varje gång använder vi följande
+förkortningar:
+
+ C-<tkn>  håll ner kontrolltangenten samtidigt som du skriver tecknet
+          <tkn>. C-f betyder: håll ner kontrolltangenten och tryck f.
+ M-<tkn>  håll ner META- eller ALT-tangenten samtidigt som du skriver
+          <tkn>. Om det inte finns någon META- eller ALT-tangent
+          trycker du på ESC-tangenten, släpper den och trycker sedan
+          <tkn>. När vi skriver <ESC> menar vi ESC-tangenten (eller Escape).
 
 Viktigt: För att avsluta Emacs trycker du C-x C-c (två tecken).
 För att avsluta kommandon som inte skrivits in fullt, tryck C-g.
@@ -18,28 +20,28 @@ Tecknen ">>" i vänstermarginalen anger att du kan prova ett
 kommando. Till exempel:
 <<Tomma rader sätts in runt nästa rad när help-with-tutorial aktiveras>>
 [Tomma rader av pedagogiska skäl. Texten fortsätter nedanför.]
->> Tryck C-v (View next screen) för att rulla nedåt i handledningen.
+>> Tryck C-v för att rulla nedåt i handledningen.
         Prova nu. Håll ned kontrolltangenten och tryck v. Gör så i
         fortsättningen när du når slutet av en skärmbild.
 
-Notera att det är ett överlapp på två rader när du rullar en hel sida.
-Detta är för att behålla sammanhanget när du bläddrar framåt i texten.
+Observera att det är ett överlapp på två rader när du rullar en hel sida.
+Detta sker för att behålla sammanhanget när du bläddrar framåt i texten.
 
-Det här är en kopia av Emacs användarhandledning, som anpassats något
-för dig. Längre fram kommer vi att instruera dig att prova olika
-kommandon som ändrar i texten. Var inte orolig om du ändrar texten
-innan vi säger till dig att göra det. Det kallas för att redigera och
-det är det som Emacs är till för.
+Det här är ett exemplar av Emacs användarhandledning som har anpassats
+något för dig. Längre fram kommer vi att be dig att prova olika kommandon
+som ändrar i texten. Var inte orolig om du ändrar texten innan vi säger
+till dig att göra det. Det kallas för att redigera och det är det som
+Emacs är till för.
 
 Det första du behöver veta är hur du manövrerar från plats till plats
 i texten. Du har redan lärt dig hur du flyttar en skärmbild framåt,
 med C-v. För att flytta dig en skärmbild bakåt trycker du M-v. (Håll
 ned META-tangenten och tryck v eller tryck <ESC>v om du inte har
-META-, EDIT- eller ALT-tangent.)
+en META- eller ALT-tangent.)
 
 >> Prova att trycka M-v och C-v några gånger.
 
-Det är OK att rulla texten på andra sätt om du vet hur.
+Det går bra att rulla texten på andra sätt som du kanske känner till.
 
 * SAMMANFATTNING
 ----------------
@@ -53,12 +55,12 @@ Följande kommandon är bra för att se hela skärmbilder:
                 KONTROLL-1.)
 
 >> Leta reda på markören och se vad som står där. Tryck sedan C-l.
-   Hitta markören igen och notera att det är samma text som står kring
-   markören nu, men nu mitt på skärmen. Om du trycker C-l igen så
+   Hitta markören igen och observera att det är samma text som står
+   kring markören nu, men nu mitt på skärmen. Om du trycker C-l igen så
    flyttas texten högst upp på skärmen. Tryck C-l igen och den flyttas
    ner till botten.
 
-Du kan också använda PageUp och PageDn tangenterna, om din terminal
+Du kan också använda tangenterna PageUp och PageDn, om din terminal
 har dem, för att flytta en hel skärmbild åt gången, men du redigerar
 effektivare om du använder C-v och M-v.
 
@@ -86,10 +88,10 @@ fyra piltangenterna. Så här:
    och C-p. Använd sedan C-l för att centrera diagrammet på
    skärmbilden.
 
-Detta är enklare att komma ihåg om du tänker på dessa förkortningar: P
-för föregående (previous), N för nästa (next), B för bakåt (backward)
-och F för framåt (forward). Du kommer att använda dessa grundläggande
-kommandona hela tiden.
+Kommandona är enklare att komma ihåg om man tänker på vad de står för:
+P för föregående (eng. "previous"), N för nästa, B för bakåt
+och F för framåt.
+Du kommer att använda dessa grundläggande kommandon hela tiden.
 
 >> Gör några C-n så att du kommer ned till den här raden.
 
@@ -102,8 +104,8 @@ avslutas också vanligtvis med ett radslut men Emacs kräver 
inte att
 den gör det.
 
 >> Prova med C-b i början av en rad. Detta gör att markören
-   flyttas till slutet av den tidigare raden. Detta är för att den
-   flyttar markören över radslutstecknet.
+   flyttas till slutet av den tidigare raden. Detta beror på att
+   markören flyttas över radslutstecknet.
 
 C-f flyttar också över radslut, precis som C-b.
 
@@ -138,12 +140,13 @@ motsatt riktning.
 Lägg märke till likheten mellan C-f och C-b å ena sidan och M-f och
 M-b å den andra. Ofta används META-kommandon till språkrelaterade
 operationer (ord, stycken, avsnitt), medan kontrollkommandon används
-till grundläggande operationer som inte beror av vad man redigerar
+till grundläggande operationer som inte beror på vad man redigerar
 (bokstäver, rader, etc.).
 
 Denna likhet finns också mellan rader och stycken: C-a och C-e flyttar
-markören till början av en rad eller till slutet av en rad, medan M-a
-och M-e flyttar den till början respektive slutet av ett stycke.
+markören till början och till slutet av en rad, medan M-a och M-e
+flyttar den till början respektive slutet av ett stycke.
+(Minnesregel: A och E för (tyska) Anfang och Ende.)
 
 >> Prova några C-a och sedan några C-e.
    Prova också några M-a och sedan några M-e.
@@ -153,8 +156,8 @@ M-a fortsätter att flytta markören till nästa stycke. Även 
om detta
 inte verkar självklart är det ganska naturligt.
 
 Platsen där markören är i texten kallas också för "arbetspunkt"
-(point). Eller omskrivet: Markören visar på skärmen var arbetspunkten
-är i texten.
+(point). Eller med andra ord: markören visar på skärmen var
+arbetspunkten är i texten.
 
 Här är en kort sammanfattning av de enklaste markörförflyttnings-
 kommandona, inklusive ord- och styckesförflyttningskommandon:
@@ -181,10 +184,10 @@ Två andra viktiga markörrörelsekommandon är M-< (META 
mindre-än), som
 flyttar markören till början av texten, och M-> (META större-än), som
 flyttar den till slutet av texten.
 
-På en del tangentbord är "<" placerad över komma, så att man måste
+På svenska tangentbord är ">" placerad över "<", så att man måste
 använda skift för att få fram den. På dessa tangentbord måste man
-också använda skift för att skriva M-<. Utan skifttangenten skulle det
-bli M-komma.
+också använda skift för att skriva M->. Utan skifttangenten skulle det
+bli M-<.
 
 >> Prova M-< nu för att flytta markören till början av vägledningen.
    Använd sedan C-v för att flytta markören tillbaka hit igen.
@@ -195,7 +198,7 @@ bli M-komma.
 Du kan också flytta markören med hjälp av piltangenterna, om
 terminalen har piltangenter. Vi föreslår att du lär dig C-b, C-f, C-n
 och C-p av tre skäl. För det första kommer de att fungera på alla
-slags terminaler. För det andra kommer du att finna, när du har fått
+slags terminaler. För det andra kommer du att märka, när du har fått
 lite träning i att använda Emacs, att det går mycket snabbare att
 använda kontrollfunktionerna än piltangenterna (för att du undviker
 att ändra fingersättningen). Den tredje anledningen är att när man har
@@ -203,14 +206,13 @@ lärt sig att använda kontrolltangenten blir det lättare 
att lära sig
 de mer avancerade kontrollfunktionerna.
 
 De flesta kommandon i Emacs tar ett numeriskt argument och för de
-flesta kommandon leder detta till att de repeteras. Ett numeriskt
+flesta kommandon leder detta till att de upprepas. Ett numeriskt
 argument anges genom att du skriver C-u och sedan talet, innan du
-skriver kommandot. Om du har en META- (eller EDIT- eller ALT-) tangent
-så finns det ett annat alternativ för att ge numeriska argument: skriv
-talet medan du håller ned META-tangenten. Vi föreslår att du använder
-C-u för det fungerar på alla slags terminaler. Det numeriska
-argumentet kallas också för "prefixargument" eftersom det skrivs före
-kommandot.
+skriver kommandot. Om du har en META- eller ALT-tangent så finns det
+ett annat alternativ för att ge numeriska argument: skriv talet medan
+du håller ned META-tangenten. Vi föreslår att du använder C-u eftersom
+det fungerar på alla slags terminaler. Det numeriska argumentet kallas
+också för "prefixargument" eftersom det skrivs före kommandot.
 
 Till exempel: C-u 8 C-f flyttar markören åtta steg framåt.
 
@@ -218,8 +220,8 @@ Till exempel: C-u 8 C-f flyttar markören åtta steg framåt.
    kommer så nära den här raden som möjligt med ett enda kommando.
 
 De flesta kommandon använder det numeriska argumentet för ett
-repeterat utförande men det finns kommandon som använder det
-annorlunda. Flera kommandon, men inga av dem du lärt dig hittills,
+upprepat utförande men det finns kommandon som använder det på
+andra sätt. Flera kommandon, men inga av dem du lärt dig hittills,
 använder det som en flagga. Med ett prefixargument, och oberoende av
 dess värde, gör kommandot något annat.
 
@@ -231,7 +233,7 @@ uppåt.
 
 >> Prova C-u 8 C-v nu.
 
-Detta borde ha flyttat skärmbilden 8 rader uppåt. Om du önskar flytta
+Detta borde ha flyttat skärmbilden 8 rader uppåt. Om du vill flytta
 tillbaka igen är det bara att ge samma argument till M-v.
 
 Om du använder ett fönstersystem, som X eller MS-Windows, finns det
@@ -239,7 +241,7 @@ troligen ett rektangulärt område på sidan av 
Emacs-fönstret, en så
 kallad rullningslist. Genom att klicka i den med musen kan du rulla
 texten.
 
-Om din mus har ett rullningshjul kan även den användas för att rulla
+Om din mus har ett rullningshjul kan även det användas för att rulla
 texten.
 
 
@@ -263,15 +265,15 @@ Om du av misstag slår <ESC> blir du kvitt detta med ett 
C-g.
 * SPÄRRADE KOMMANDON
 --------------------
 
-En del Emacs-kommandon är "spärrade" så att nybörjare inte skall
+En del Emacs-kommandon är "spärrade" för att inte nybörjare skall
 använda dem av misstag.
 
 Om du provar ett av dessa spärrade kommandon kommer Emacs ge ett
-meddelande som berättar vilket kommando det är och kommer att fråga om
+meddelande som berättar vilket kommando det är och fråga om
 du verkligen vill fortsätta och utföra detta kommando.
 
-Om du verkligen önskar att utföra kommandot skriver du <SPC>,
-(mellanslagstangenten) som svar på frågan. Normalt, om du inte önskar
+Om du verkligen önskar att utföra kommandot trycker du på
+mellanslagstangenten som svar på frågan. Normalt, om du inte önskar
 att utföra detta kommando, svarar du "n" på frågan.
 
 >> Skriv C-x C-l (som är ett spärrat kommando).
@@ -295,7 +297,7 @@ tas bort.
 >> Flytta markören till den här raden och tryck C-u 0 C-l.
 >> Tryck C-h k C-f.
    Se hur det här fönstret krymper samtidigt som ett nytt uppträder
-   för att visa dokumentationen av C-f-kommandot.
+   för att visa dokumentationen av kommandot C-f.
 
 >> Slå C-x 1 och se hur dokumentationsfönstret nu försvinner.
 
@@ -309,13 +311,13 @@ kommandon är två, tre eller fyra tecken långa.
 
 Om du önskar att sätta in text är det bara att skriva in texten.
 Vanliga tecken, som A, 7, *, etc., sätts in direkt när du skriver dem.
-Tryck på <Return> för att sätta in en radbrytning. (Det är den tangent
-på tangentbordet som ibland är märkt med "Enter")
+Tryck på <Return>, returtangenten, för att sätta in en radbrytning.
+(Den är vanligen märkt "Return" eller "Enter" eller med en krokpil bakåt.)
 
 För att radera tecknet omedelbart före aktuell markörposition, tryck
 på <DEL>. Det är tangenten på tangentbordet som vanligtvis är markerad
-med "Backspace" -- det är samma tangent som du normal använder för att
-radera det sist inmatade tecknet utanför Emacs.
+med en lång vänsterpil eller "Backspace" -- det är samma tangent som du
+normalt använder för att radera det sist inmatade tecknet utanför Emacs.
 
 Det kan finnas en annan tangent på ditt tangentbordet som är märkt med
 "Delete", men det är inte den vi menar med <DEL>.
@@ -326,9 +328,9 @@ Det kan finnas en annan tangent på ditt tangentbordet som 
är märkt med
     är bara en lokal kopia.
 
 När en rad blir för lång för att rymmas på en skärmbredd så fortsätter
-den på raden under. Om du använder ett fönstersystem, visas små böjda
+den på raden under. Om du använder ett fönstersystem så visas små böjda
 pilar i det lilla utrymmet på bägge sidor om textmassan (i vänster och
-höger marginal) för att ange var en rad fortsätter, Om du använder
+höger marginal) för att ange var en rad fortsätter. Om du använder
 en textterminal anges med ett bakstreck (”\”) i kolumnen längst till
 höger att raden fortsätter.
 
@@ -339,7 +341,7 @@ höger att raden fortsätter.
 >>  Använd <DEL> för att radera texten tills raden ryms på en
     skärmbredd igen. Fortsättningstecknet kommer då att försvinna.
 
-Du kan radera radbrytning precis som andra tecken. Genom att radera
+Du kan radera radbrytningar precis som andra tecken. Genom att radera
 radbrytningen mellan två rader slås dessa samman till en. Om
 resultatet av denna sammanslagning blir för stor för att passa inom en
 skärmbredd, så kommer den att visas med ett fortsättningstecken.
@@ -363,7 +365,7 @@ Du bör se att efter att den nya raden satts in, sätter 
Emacs in
 blanktecken så att markören flyttas fram under "T" i "Tryck".
 
 Tänk på att de flesta Emacs-kommandon kan ta numeriska argument. Detta
-gäller också texttecken. Genom att repetera ett texttecken kommer det
+gäller också texttecken. Genom att upprepa ett texttecken kommer det
 skrivas flera gånger.
 
 >> Prova det nu: Skriv C-u 8 * för att sätta in ********.
@@ -402,11 +404,11 @@ tryck C-w. Detta tar bort texten mellan de två 
positionerna.
 >> Tryck C-w. Detta tar bort texten från och med D fram till just före
    o.
 
-Skillnaden mellan att "ta bort" (killing) och "radera" (deleting) text
+Skillnaden mellan att "ta bort" (kill) och "radera" (delete) text
 är att "borttagen" text kan sättas tillbaka (var som helst), medan
 raderad text inte kan det på det sättet. (Du kan dock ångra en
 radering--se nedan.) Återinsättning av borttagen text kallas
-"återhämtning" (yanking).  Generellt kan man säga att kommandon som
+"återhämtning" (yank).  Generellt kan man säga att kommandon som
 tar bort fler än ett tecken sparar undan texten (så att den kan
 återhämtas) medan kommandon som bara raderar ett tecken, eller bara
 raderar tomma rader och mellanrum inte sparar någonting (och den
@@ -422,7 +424,7 @@ Lägg märke till att ett enstaka C-k bara raderar texten på 
raden och
 att det andra C-k raderar själva raden och flyttar upp texten på raden
 under ett steg. C-k hanterar numeriska argument lite speciellt. Den
 raderar så många rader OCH innehållet i dem. Detta är alltså inte bara
-en repetition av kommandot. C-u 2 C-k raderar två rader samt de tomma
+en upprepning av kommandot. C-u 2 C-k raderar två rader samt de tomma
 raderna, medan C-k två gånger inte kommer att göra det.
 
 Du kan antingen hämta tillbaka borttagen text till samma plats som där
@@ -438,7 +440,7 @@ tillbaka den sist borttagna texten och placerar den där 
markören är.
 
 >> Prova: Gör C-y för att få tillbaka texten.
 
-Om du gör flera C-k i rad så kommer all bortagen text att sparas
+Om du gör flera C-k i rad så kommer all borttagen text att sparas
 samlat så att ett C-y återhämtar alla raderna på en gång.
 
 >> Prova detta. Tryck C-k ett par gånger.
@@ -450,11 +452,11 @@ Och hämta så tillbaka igen:
 
 Men vad gör du om du har en text du önskar att hämta tillbaka men du
 har redan tagit bort något nytt? C-y skulle hämta tillbaka den senaste
-texten som blev borttagen men tidigare bortagen text är inte
+texten som blev borttagen men tidigare borttagen text är inte
 förlorad. Du kan få tillbaka den med kommandot M-y. Efter att du har
 använt C-y för att hämta tillbaka den sist borttagna texten kommer M-y
 ersätta denna text med tidigare borttagen text. Genom att göra M-y om
-och om igen hämtas allt tidigare borttagen text tillbaka. När du har
+och om igen hämtas all tidigare borttagen text tillbaka. När du har
 nått den önskade texten behöver du inte göra något ytterligare för att
 behålla den. Fortsätt bara med din redigeringen och lämna den
 återtagna texten där den är.
@@ -477,25 +479,25 @@ Om du gör en förändring i texten och sedan ångrar dig, så 
kan du
 upphäva ändringen med ångra-kommandot C-/.
 
 Normalt kommer C-/ upphäva förändringen som gjordes av det sist
-utförda kommandot. Om du repeterar C-/ flera gånger kommer varje
-repetition upphäva ett kommando till.
+utförda kommandot. Om du upprepar C-/ flera gånger kommer varje
+upprepning upphäva ett kommando till.
 
 Det finns två undantag. Kommandon som inte förändrar texten räknas
 inte (detta inkluderar markörförflyttningar och bläddringskommandon),
-och inskrivna enkelbokstäver blir vanligtvis grupperade i grupper om
-upp till 20 tecken. Detta är för att reducera antalet C-/ som behövs
+och inskrivna enkelbokstäver blir vanligtvis samlade i grupper om
+upp till 20 tecken. Detta görs för att reducera antalet C-/ som behövs
 för att ångra inskriven text.
 
 >> Ta bort den här raden med C-k, hämta sedan tillbaka den med C-/.
 
-C-_ är ett alternativt ångra-kommandot. Den fungerar exakt på samma
+C-_ är ett alternativt ångra-kommando. Det fungerar exakt på samma
 sätt som C-/. På vissa textterminaler skickar C-/ faktiskt C-_ till
 Emacs. Även C-x u fungerar precis som C-/, men är inte lika enkelt att
 skriva.
 
 Ett numeriskt argument till C-/, C-_ eller C-x u medför upprepning.
 
-Du kan ångra radering av text precis på samma sätt som du kan ångra
+Du kan ångra radering av text precis på samma sätt som att du kan ångra
 att du tagit bort text. Skillnaden mellan att ta bort och att radera
 någonting påverkar endast om du kan hämta tillbaka det med C-y. För
 ångerfunktionen spelar det ingen roll hur texten försvunnit.
@@ -514,19 +516,18 @@ sätt är det som om du förändrar själva filen men 
förändringen du gör
 kommer inte bli permanent förrän filen sparas (save). Detta är för att
 undvika att halvförändrade filer sparas när du inte vill det. Till och
 med när du sparar filen kommer Emacs att behålla originalet under ett
-nytt namn, som backup, ifall du senare ångrar alltihop.
+nytt namn, som säkerhetskopia, ifall du senare ångrar alltihop.
 
 Om du tittar nästan längst ner på skärmbilden så kommer du se en rad
-som börjar med minustecken, och som startar med "-:--- TUTORIAL.sv"
+som börjar med minustecken, och som startar med "U:--- TUTORIAL.sv"
 eller något snarlikt. Denna del av skärmbilden visar normalt namnet på
-filen du besöker. Just nu besöker du din personlig kopia av
+filen du besöker. Just nu besöker du ditt personliga exemplar av
 vägledningen till Emacs, vilken heter "TUTORIAL.sv". Vilken fil du än
 är inne i så kommer filnamnet stå där.
 
 En annan sak med kommandot för att finna filer är att du måste ange
-vilket filnamn du önskar. Vi säger att kommandot "läser ett
-argument". I detta fall är argumentet namnet på filen. Efter att du
-gett kommandot
+vilket filnamn du önskar. Vi säger att kommandot "läser ett argument".
+I detta fall är argumentet namnet på filen. Efter att du gett kommandot
 
         C-x C-f   Finn en fil
 
@@ -569,7 +570,7 @@ för att titta på den. Du kan också finna en fil som inte 
existerar.
 Det är så man skapar nya filer med Emacs: finn filen, som är tom till
 att börja med, och sätt igång med att skriva texten som skall in i
 filen. Först när du sparar filen kommer Emacs att verkligen skapa
-filen med den text du har skrivit. Från och med detta editerar du en
+filen med den text du har skrivit. Från och med detta redigerar du en
 fil som existerar.
 
 
@@ -599,16 +600,16 @@ När du har flera buffertar så är bara en av dem 
"gällande" åt gången.
 Det är den buffert du redigerar. Om du vill redigera en annan buffert
 så måste du byta till den. Om du vill byta till en buffert som
 motsvarar en fil kan du göra det genom att besöka den igen med C-x
-C-f. Det finns dock ett enklare sätt: använd C-x b kommandot. I det
+C-f. Det finns dock ett enklare sätt: använd kommandot C-x b. I det
 kommandot anger du buffertens namn.
 
->> Skapa en fil med namnet "foo" genom att trycka C-x C-f foo <Return>.
+>> Skapa en fil med namnet "abc" genom att trycka C-x C-f abc <Return>.
    Skriv sedan C-x b TUTORIAL.sv <Return> för att komma tillbaka till
    den här handledningen.
 
-Mestadels är buffertens namn densamma som filens namn (utan
-katalogdel.) Det är dock inte alltid så. Bufferlistan du skapar med
-C-x C-b visar alltid namnen på varje buffert.
+Mestadels är buffertens namn densamma som filens namn (utan katalogdel).
+Det är dock inte alltid så. Bufferlistan som du skapar med C-x C-b
+visar alltid namnen på varje buffert.
 
 En del buffertar är inte knutna till någon fil, till exempel bufferten
 "*Buffer List*". Det är den buffert som innehåller buffertlistan som
@@ -641,7 +642,7 @@ vill spara eller ej.
 * UTVIDGNING AV KOMMANDOMÄNGDEN
 -------------------------------
 
-Det finns mycket fler Emacs-kommandon än antalet KONTROLL- eller
+Det finns många fler Emacs-kommandon än antalet KONTROLL- eller
 META-tangenter. För att komma förbi denna begränsning har Emacs ett
 "X"- (eXtend) kommando. Detta finns i två varianter:
 
@@ -669,8 +670,8 @@ Emacsprocessen förstörs. I de flesta vanliga kommandoskalen 
så kan man
 återgå till Emacs med kommandot 'fg' eller med '%emacs'.
 
 C-x C-c används när du skall avsluta Emacs. Det är klokt att avsluta
-Emacs om den har startats av ett mail-program eller andra
-applikationer.
+Emacs om den har startats av ett e-post-program eller annan
+applikation.
 
 Det finns många C-x kommandon. Här är en lista över de du har lärt dig
 hittills:
@@ -686,14 +687,13 @@ hittills:
 
 Namngivna utvidgade kommandon är kommandon som används mycket sällan
 eller bara i vissa lägen. Ett exempel på ett sådant kommando är
-replace-string, som globalt ersätter en teckensträng med en annan. När
+replace-string, som överallt ersätter en teckensträng med en annan. När
 du skriver M-x kommer Emacs visa en prompt nederst i skärmbilden med
 M-x där du skall skriva in kommandot du önskar att köra, i det här
 fallet "replace-string". Det är bara att skriva "repl s<TAB>" och
 Emacs kommer då att fylla i kommandonamnet. (<TAB> är
-tabulatortangenten, som vanligtvis finns över CapsLock- eller
-skifttangenten nära den vänstra kanten på tangentbordet.) Kör
-kommandot med <Return>.
+tabulatortangenten, som vanligtvis finns över skiftlåstangenten
+nära den vänstra kanten på tangentbordet.) Kör kommandot med <Return>.
 
 Kommandot replace-string kräver två argument, teckensträngen som skall
 ersättas och teckensträngen som den skall ersättas med. Du måste
@@ -715,7 +715,7 @@ När du har gjort förändringar i en fil men inte sparat den, 
så kommer
 detta sparar Emacs periodiskt ändringarna i en autosparfil för varje
 fil du redigerar. Denna fil har ett # i början och slutet av
 filnamnet. Om du till exempel har en fil med namnet "hej.c" så kommer
-namnet på autosparfilen bli "#hej.c#". När du sparar filen på vanlig
+namnet på autosparfilen bli "#hej.c#". När du sparar filen på vanligt
 sätt kommer Emacs radera autosparfilen.
 
 Om maskinen kraschar kan du återfå dina automatiskt sparade ändringar
@@ -828,7 +828,7 @@ genom ett numeriskt argument.
    att använda C-x f en gång till.
 
 Om du gör förändringar mitt i en rad så kommer inte sidoläget Auto
-Fill att kunna omformattera raderna för dig.
+Fill att kunna formatera om raderna för dig.
 För att göra detta kan du trycka M-q med markören inne i det avsnittet
 du önskar att omformatera.
 
@@ -946,7 +946,7 @@ sig och inte en äkta "modifierare".
 
 Om du hade skrivit C-x 1 i det nedre fönstret skulle det övre ha
 försvunnit. Tänk på detta kommando som "Behåll bara ett fönster, det
-som markören står i."
+som markören står i".
 
 Du måste inte ha samma buffert i bägge fönstren. Du kan använda C-x
 C-f för att finna en ny fil i ett av fönstren samtidigt som det andra
@@ -964,7 +964,7 @@ filer:
    1 för att bli kvitt det nedre igen.
 
 
-* MULTIPLA RAMAR
+* FLERA RAMAR
 ----------------
 
 Emacs kan också skapa flera "ramar".  En ram är vad vi kallar en
@@ -1042,8 +1042,7 @@ beskrivande namn kan de också fungera som en enkel 
dokumentation,
 tillräckligt för att påminna dig om kommandon du redan lärt dig.
 
 Flerteckenskommandon, så som C-x C-s och <ESC>v (i stället för M-v, om
-du inte har META, EDIT eller ALT tangenten) är också tillåtna efter
-C-h c.
+du inte har META- eller ALT-tangenten) är också tillåtna efter C-h c.
 
 För att få mer information om ett kommando kan du använda C-h k
 istället för C-h c.
@@ -1052,7 +1051,7 @@ istället för C-h c.
 
 Detta kommer visa funktionens dokumentation och namn i ett eget
 fönster. För att avsluta hjälpfönstret kan du trycka C-x 1. Du behöver
-inte göra det omedelbart. Du kan editera med hjälptexten som stöd för
+inte göra det omedelbart. Du kan redigera med hjälptexten som stöd för
 att först senare ta bort fönstret med C-x 1.
 
 Här är fler varianter på C-h:
@@ -1107,17 +1106,17 @@ Två finesser som du kan komma att gilla speciellt är 
komplettering
 filhantering.
 
 Komplettering är ett sätt att undvika onödiga tangenttryckningar. Till
-exempel, om du vill byta till *Messages* bufferten, kan du du skriva
+exempel, om du vill byta till bufferten *Messages* så kan du du skriva
 C-x b *M<Tab> och Emacs kommer fylla i resten av buffertnamnet så
 långt den kan räkna ut det från det du redan skrivit. Komplettering
 finns beskrivet i Emacs-manualen i noden "Completion".
 
 Dired gör det möjligt att lista filer i en katalog (och även dess
-subkataloger), flytta runt i listan, besöka, byta namn, ta bort och
+underkataloger), flytta runt i listan, besöka, byta namn, ta bort och
 operera på olika sätt på filerna. Dired finns beskrivet i Info i
 Emacs-manualen i noden "Dired".
 
-Manualen beskriver även många andra Emacs funktioner.
+Manualen beskriver även många andra funktioner i Emacs.
 
 
 * SLUTORD
@@ -1135,7 +1134,7 @@ själv, klaga!
 
 Denna vägledning härstammar från en hel rad Emacs-vägledningar och den
 första skrevs av Stuart Cracraft för den ursprungliga Emacs. Mats
-Lidell översatte den till Svenska.
+Lidell översatte den till svenska.
 
 This version of the tutorial, like GNU Emacs, is copyrighted, and
 comes with permission to distribute copies on certain conditions:
diff --git a/lisp/allout.el b/lisp/allout.el
index a4802a1..880098c 100644
--- a/lisp/allout.el
+++ b/lisp/allout.el
@@ -1621,8 +1621,7 @@ So `allout-post-command-business' should not reactivate 
it...")
        cur)
     (while menus
       (setq cur (car menus)
-           menus (cdr menus))
-      (easy-menu-add cur))))
+            menus (cdr menus)))))
 ;;;_  > allout-overlay-preparations
 (defun allout-overlay-preparations ()
   "Set the properties of the allout invisible-text overlay and others."
diff --git a/lisp/arc-mode.el b/lisp/arc-mode.el
index ce0c061..69a159a 100644
--- a/lisp/arc-mode.el
+++ b/lisp/arc-mode.el
@@ -1058,27 +1058,43 @@ return nil.  Otherwise point is returned."
       (archive-goto-file short))
     next))
 
-(defun archive-copy-file (file new-name)
-  "Copy FILE to a location specified by NEW-NAME.
-Interactively, FILE is the file at point, and the function prompts
-for NEW-NAME."
+(defun archive-copy-file (files new-name)
+  "Copy FILES to a location specified by NEW-NAME.
+FILES can be a single file or a list of files.
+
+Interactively, FILES is the list of marked files, or the file at
+point if nothing is marked, and the function prompts for
+NEW-NAME."
   (interactive
-   (let ((name (archive--file-desc-ext-file-name (archive-get-descr))))
-     (list name
-           (read-file-name (format "Copy %s to: " name)))))
-  (when (file-directory-p new-name)
-    (setq new-name (expand-file-name file new-name)))
-  (when (and (file-exists-p new-name)
-             (not (yes-or-no-p (format "%s already exists; overwrite? "
-                                       new-name))))
-    (user-error "Not overwriting %s" new-name))
-  (let* ((descr (archive-get-descr))
-         (archive (buffer-file-name))
-         (extractor (archive-name "extract"))
-         (ename (archive--file-desc-ext-file-name descr)))
-    (with-temp-buffer
-      (archive--extract-file extractor archive ename)
-      (write-region (point-min) (point-max) new-name))))
+   (let ((names
+          (mapcar
+           #'archive--file-desc-ext-file-name
+           (or (archive-get-marked ?*) (list (archive-get-descr))))))
+     (list names
+           (read-file-name (format "Copy %s to: " (string-join names ", "))))))
+  (unless (consp files)
+    (setq files (list files)))
+  (when (and (> (length files) 1)
+             (not (file-directory-p new-name)))
+    (user-error "Can't copy a list of files to a single file"))
+  (save-excursion
+    (dolist (file files)
+      (let ((write-to (if (file-directory-p new-name)
+                          (expand-file-name file new-name)
+                        new-name)))
+        (when (and (file-exists-p write-to)
+                   (not (yes-or-no-p (format "%s already exists; overwrite? "
+                                             write-to))))
+          (user-error "Not overwriting %s" write-to))
+        (archive-goto-file file)
+        (let* ((descr (archive-get-descr))
+               (archive (buffer-file-name))
+               (extractor (archive-name "extract"))
+               (ename (archive--file-desc-ext-file-name descr)))
+          (with-temp-buffer
+            (set-buffer-multibyte nil)
+            (archive--extract-file extractor archive ename)
+            (write-region (point-min) (point-max) write-to)))))))
 
 (defun archive-extract (&optional other-window-p event)
   "In archive mode, extract this entry of the archive into its own buffer."
diff --git a/lisp/cedet/ede/pmake.el b/lisp/cedet/ede/pmake.el
index 9d9a95f..2e3256f 100644
--- a/lisp/cedet/ede/pmake.el
+++ b/lisp/cedet/ede/pmake.el
@@ -180,7 +180,7 @@ MFILENAME is the makefile to generate."
          ;;
          ;; NOTE: This is GNU Make specific.
          (if (and (oref this automatic-dependencies) df)
-             (insert "DEPS_MAGIC := $(shell mkdir .deps > /dev/null "
+             (insert "DEPS_MAGIC := $(shell mkdir .deps > " null-device " "
                      "2>&1 || :)\n"
                      "-include $(DEP_FILES)\n\n"))
          ;;
diff --git a/lisp/cedet/semantic/grammar.el b/lisp/cedet/semantic/grammar.el
index f71ac6c..b7670ef 100644
--- a/lisp/cedet/semantic/grammar.el
+++ b/lisp/cedet/semantic/grammar.el
@@ -1258,7 +1258,8 @@ common grammar menu."
        (unless (boundp ',symbol)
          (easy-menu-define ,symbol nil
            "Grammar Menu" (copy-sequence semantic-grammar-menu)))
-       (easy-menu-add ,symbol)
+       (when (featurep 'xemacs)
+         (easy-menu-add ,symbol))
        (let ((,items (cdr ,mode-menu))
              (,path (list (car ,symbol))))
          (when ,items
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index eceba8f..a00cb17 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2900,14 +2900,20 @@ Modified means that the widget that holds the value has 
been edited by the user
 in a customize buffer.
 To check for other states, call `custom-variable-state'."
   (catch 'get-error
-    (let* ((symbol (widget-get widget :value))
+    (let* ((form (widget-get widget :custom-form))
+           (symbol (widget-get widget :value))
            (get (or (get symbol 'custom-get) 'default-value))
            (value (if (default-boundp symbol)
                       (condition-case nil
                           (funcall get symbol)
                         (error (throw 'get-error t)))
-                    (symbol-value symbol))))
-      (not (equal value (widget-value (car (widget-get widget :children))))))))
+                    (symbol-value symbol)))
+           (orig-value (widget-value (car (widget-get widget :children)))))
+      (not (equal (if (memq form '(lisp mismatch))
+                      ;; Mimic `custom-variable-value-create'.
+                      (custom-quote value)
+                    value)
+                  orig-value)))))
 
 (defun custom-variable-state-set (widget &optional state)
   "Set the state of WIDGET to STATE.
@@ -5126,7 +5132,6 @@ Erase customizations; set options
 Entry to this mode calls the value of `Custom-mode-hook'
 if that value is non-nil."
   (use-local-map custom-mode-map)
-  (easy-menu-add Custom-mode-menu)
   (setq-local tool-bar-map
              (or custom-tool-bar-map
                  ;; Set up `custom-tool-bar-map'.
diff --git a/lisp/custom.el b/lisp/custom.el
index 3f1e8ca..0b2b325 100644
--- a/lisp/custom.el
+++ b/lisp/custom.el
@@ -157,7 +157,9 @@ set to nil, as the value is no longer rogue."
   (if (keywordp doc)
       (error "Doc string is missing"))
   (let ((initialize #'custom-initialize-reset)
-       (requests nil))
+        (requests nil)
+        ;; Whether automatically buffer-local.
+        buffer-local)
     (unless (memq :group args)
       (custom-add-to-group (custom-current-group) symbol 'custom-variable))
     (while args
@@ -183,7 +185,7 @@ set to nil, as the value is no longer rogue."
                 (put symbol 'safe-local-variable value))
                 ((eq keyword :local)
                  (when (memq value '(t permanent))
-                   (make-variable-buffer-local symbol))
+                   (setq buffer-local t))
                  (when (eq value 'permanent)
                    (put symbol 'permanent-local t)))
                ((eq keyword :type)
@@ -205,7 +207,9 @@ set to nil, as the value is no longer rogue."
     (put symbol 'custom-requests requests)
     ;; Do the actual initialization.
     (unless custom-dont-initialize
-      (funcall initialize symbol default)))
+      (funcall initialize symbol default))
+    (when buffer-local
+      (make-variable-buffer-local symbol)))
   (run-hooks 'custom-define-hook)
   symbol)
 
diff --git a/lisp/delsel.el b/lisp/delsel.el
index df2adc7..e1087fb 100644
--- a/lisp/delsel.el
+++ b/lisp/delsel.el
@@ -274,6 +274,8 @@ to `delete-selection-mode'."
 (put 'quoted-insert 'delete-selection t)
 
 (put 'yank 'delete-selection 'yank)
+(put 'yank-pop 'delete-selection 'yank)
+(put 'yank-from-kill-ring 'delete-selection 'yank)
 (put 'clipboard-yank 'delete-selection 'yank)
 (put 'insert-register 'delete-selection t)
 ;; delete-backward-char and delete-forward-char already delete the selection by
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 532f3d1..28c53bf 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -1799,7 +1799,7 @@ Files in subdirectories of DIRECTORY are processed also."
   (byte-recompile-directory directory nil t))
 
 ;;;###autoload
-(defun byte-recompile-directory (directory &optional arg force)
+(defun byte-recompile-directory (directory &optional arg force follow-symlinks)
   "Recompile every `.el' file in DIRECTORY that needs recompilation.
 This happens when a `.elc' file exists but is older than the `.el' file.
 Files in subdirectories of DIRECTORY are processed also.
@@ -1812,7 +1812,11 @@ compile it.  A nonzero ARG also means ask about each 
subdirectory
 before scanning it.
 
 If the third argument FORCE is non-nil, recompile every `.el' file
-that already has a `.elc' file."
+that already has a `.elc' file.
+
+This command will normally not follow symlinks when compiling
+files.  If FOLLOW-SYMLINKS is non-nil, symlinked `.el' files will
+also be compiled."
   (interactive "DByte recompile directory: \nP")
   (if arg (setq arg (prefix-numeric-value arg)))
   (if noninteractive
@@ -1845,7 +1849,8 @@ that already has a `.elc' file."
             (if (file-directory-p source)
                 (and (not (member file '("RCS" "CVS")))
                      (not (eq ?\. (aref file 0)))
-                     (not (file-symlink-p source))
+                      (or follow-symlinks
+                         (not (file-symlink-p source)))
                      ;; This file is a subdirectory.  Handle them differently.
                      (or (null arg) (eq 0 arg)
                          (y-or-n-p (concat "Check " source "? ")))
diff --git a/lisp/emacs-lisp/easymenu.el b/lisp/emacs-lisp/easymenu.el
index b0198db..7a24af7 100644
--- a/lisp/emacs-lisp/easymenu.el
+++ b/lisp/emacs-lisp/easymenu.el
@@ -488,17 +488,14 @@ To implement dynamic menus, either call this from
 `menu-bar-update-hook' or use a menu filter."
   (easy-menu-add-item map path (easy-menu-create-menu name items) before))
 
-;; XEmacs needs the following two functions to add and remove menus.
-;; In Emacs this is done automatically when switching keymaps, so
-;; here easy-menu-remove and easy-menu-add are a noops.
-(defalias 'easy-menu-remove 'ignore
+(define-obsolete-function-alias 'easy-menu-remove #'ignore "28.1"
   "Remove MENU from the current menu bar.
 Contrary to XEmacs, this is a nop on Emacs since menus are automatically
 \(de)activated when the corresponding keymap is (de)activated.
 
 \(fn MENU)")
 
-(defalias 'easy-menu-add #'ignore
+(define-obsolete-function-alias 'easy-menu-add #'ignore "28.1"
   "Add the menu to the menubar.
 On Emacs this is a nop, because menus are already automatically
 activated when the corresponding keymap is activated.  On XEmacs
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index 78cb8f0..d81060e 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -591,7 +591,8 @@ Honor `eldoc-echo-area-use-multiline-p' and
                ;; format the *eldoc* buffer, using as most of its
                ;; contents as we know will fit.
                (with-current-buffer (eldoc--format-doc-buffer docs)
-                 (eldoc--echo-area-substring available)))
+                 (save-excursion
+                   (eldoc--echo-area-substring available))))
               (t ;; this is the "truncate brutally" situation
                (let ((string
                       (with-current-buffer (eldoc--format-doc-buffer docs)
diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el
index 76c3ac3..6d33299 100644
--- a/lisp/emacs-lisp/rx.el
+++ b/lisp/emacs-lisp/rx.el
@@ -1418,6 +1418,7 @@ into a plain rx-expression, collecting names into 
`rx--pcase-vars'."
      (cons head (mapcar #'rx--pcase-transform rest)))
     (_ rx)))
 
+;;;###autoload
 (pcase-defmacro rx (&rest regexps)
   "A pattern that matches strings against `rx' REGEXPS in sexp form.
 REGEXPS are interpreted as in `rx'.  The pattern matches any
diff --git a/lisp/emacs-lisp/tabulated-list.el 
b/lisp/emacs-lisp/tabulated-list.el
index 3057767..ae3ed05 100644
--- a/lisp/emacs-lisp/tabulated-list.el
+++ b/lisp/emacs-lisp/tabulated-list.el
@@ -767,6 +767,7 @@ as the ewoc pretty-printer."
   (setq-local revert-buffer-function #'tabulated-list-revert)
   (setq-local glyphless-char-display
               (tabulated-list-make-glyphless-char-display-table))
+  (setq-local text-scale-remap-header-line t)
   ;; Avoid messing up the entries' display just because the first
   ;; column of the first entry happens to begin with a R2L letter.
   (setq bidi-paragraph-direction 'left-to-right)
diff --git a/lisp/erc/erc-menu.el b/lisp/erc/erc-menu.el
index 9c02899..b36a1cb 100644
--- a/lisp/erc/erc-menu.el
+++ b/lisp/erc/erc-menu.el
@@ -114,22 +114,19 @@ ERC menu yet.")
      ;; make sure the menu only gets defined once, since Emacs 22
      ;; activates it immediately
      (easy-menu-define erc-menu erc-mode-map "ERC menu" erc-menu-definition)
-     (setq erc-menu-defined t))
-   (erc-menu-add))
-  ((erc-menu-remove)
-   ;; `easy-menu-remove' is a no-op in Emacs 22
+     (setq erc-menu-defined t)))
+  (;; `easy-menu-remove' is a no-op in Emacs 22
    (message "You might have to restart Emacs to remove the ERC menu")))
 
-;; silence byte-compiler warning
-(defvar erc-menu)
-
 (defun erc-menu-add ()
   "Add the ERC menu to the current buffer."
-  (easy-menu-add erc-menu erc-mode-map))
+  (declare (obsolete nil "28.1"))
+  nil)
 
 (defun erc-menu-remove ()
   "Remove the ERC menu from the current buffer."
-  (easy-menu-remove erc-menu))
+  (declare (obsolete nil "28.1"))
+  nil)
 
 (provide 'erc-menu)
 
diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el
index b415486..b0443a4 100644
--- a/lisp/eshell/esh-io.el
+++ b/lisp/eshell/esh-io.el
@@ -382,8 +382,7 @@ it defaults to `insert'."
   "Set handle INDEX, using MODE, to point to TARGET."
   (when target
     (if (and (stringp target)
-            (or (string= target null-device)
-                (string= target "/dev/null")))
+             (string= target (null-device)))
        (aset eshell-current-handles index nil)
       (let ((where (eshell-get-target target mode))
            (current (car (aref eshell-current-handles index))))
diff --git a/lisp/face-remap.el b/lisp/face-remap.el
index 028269a..4ccd463 100644
--- a/lisp/face-remap.el
+++ b/lisp/face-remap.el
@@ -229,6 +229,28 @@ Each positive or negative step scales the default face 
height by this amount."
 (defvar text-scale-mode-amount 0)
 (make-variable-buffer-local 'text-scale-mode-amount)
 
+(defvar text-scale-remap-header-line nil
+  "If non-nil, text scaling may change font size of header lines too.")
+(make-variable-buffer-local 'text-scale-header-line)
+
+(defun face-remap--clear-remappings ()
+  (dolist (remapping
+           ;; This is a bit messy to stay backwards compatible.
+           ;; In the future, this can be simplified to just use
+           ;; `text-scale-mode-remapping'.
+           (if (consp (car-safe text-scale-mode-remapping))
+               text-scale-mode-remapping
+             (list text-scale-mode-remapping)))
+    (face-remap-remove-relative remapping))
+  (setq text-scale-mode-remapping nil))
+
+(defun face-remap--remap-face (sym)
+  (push (face-remap-add-relative sym
+                       :height
+                       (expt text-scale-mode-step
+                             text-scale-mode-amount))
+        text-scale-mode-remapping))
+
 (define-minor-mode text-scale-mode
   "Minor mode for displaying buffer text in a larger/smaller font.
 
@@ -240,21 +262,32 @@ face size by the value of the variable 
`text-scale-mode-step'
 The `text-scale-increase', `text-scale-decrease', and
 `text-scale-set' functions may be used to interactively modify
 the variable `text-scale-mode-amount' (they also enable or
-disable `text-scale-mode' as necessary)."
+disable `text-scale-mode' as necessary).
+
+If `text-scale-remap-header-line' is non-nil, also change
+the font size of the header line."
   :lighter (" " text-scale-mode-lighter)
-  (when text-scale-mode-remapping
-    (face-remap-remove-relative text-scale-mode-remapping))
+  (face-remap--clear-remappings)
   (setq text-scale-mode-lighter
        (format (if (>= text-scale-mode-amount 0) "+%d" "%d")
                text-scale-mode-amount))
-  (setq text-scale-mode-remapping
-       (and text-scale-mode
-            (face-remap-add-relative 'default
-                                         :height
-                                         (expt text-scale-mode-step
-                                               text-scale-mode-amount))))
+  (when text-scale-mode
+    (face-remap--remap-face 'default)
+    (when text-scale-remap-header-line
+      (face-remap--remap-face 'header-line)))
   (force-window-update (current-buffer)))
 
+(defun text-scale--refresh (symbol newval operation where)
+  "Watcher for `text-scale-remap-header-line'.
+See `add-variable-watcher'."
+  (when (and (eq symbol 'text-scale-remap-header-line)
+             (eq operation 'set)
+             text-scale-mode)
+    (with-current-buffer where
+      (let ((text-scale-remap-header-line newval))
+        (text-scale-mode 1)))))
+(add-variable-watcher 'text-scale-remap-header-line #'text-scale--refresh)
+
 (defun text-scale-min-amount ()
   "Return the minimum amount of text-scaling we allow."
   ;; When the resulting pixel-height of characters will become smaller
diff --git a/lisp/files.el b/lisp/files.el
index 7777259..a201827 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2683,6 +2683,7 @@ since only a single case-insensitive search through the 
alist is made."
      ("\\.dir-locals\\(?:-2\\)?\\.el\\'" . lisp-data-mode)
      ("eww-bookmarks\\'" . lisp-data-mode)
      ("tramp\\'" . lisp-data-mode)
+     ("/archive-contents\\'" . lisp-data-mode)
      ("places\\'" . lisp-data-mode)
      ("\\.emacs-places\\'" . lisp-data-mode)
      ("\\.el\\'" . emacs-lisp-mode)
diff --git a/lisp/filesets.el b/lisp/filesets.el
index c7ec3f7..883871c 100644
--- a/lisp/filesets.el
+++ b/lisp/filesets.el
@@ -203,8 +203,8 @@ key is supported."
 (defun filesets-select-command (cmd-list)
   "Select one command from CMD-LIST -- a string with space separated names."
   (let ((this (shell-command-to-string
-              (format "which --skip-alias %s 2> /dev/null | head -n 1"
-                      cmd-list))))
+              (format "which --skip-alias %s 2> %s | head -n 1"
+                      cmd-list null-device))))
     (if (equal this "")
        nil
       (file-name-nondirectory (substring this 0 (- (length this) 1))))))
@@ -1718,9 +1718,12 @@ Assume MODE (see `filesets-entry-mode'), if provided."
                                (filesets-entry-get-master entry)))))
                  (cons entry (filesets-ingroup-cache-get entry))))
               (:tree
-               (let ((dir  (nth 0 entry))
-                     (patt (nth 1 entry)))
-                 (filesets-directory-files dir patt ':files t)))
+                (let* ((dirpatt (filesets-entry-get-tree entry))
+                       (dir (nth 0 dirpatt))
+                       (patt (nth 1 dirpatt))
+                       (depth (or (filesets-entry-get-tree-max-level entry)
+                                  filesets-tree-max-level)))
+                  (filesets-files-under 0 depth entry dir patt)))
               (:pattern
                (let ((dirpatt (filesets-entry-get-pattern entry)))
                  (if dirpatt
@@ -1734,6 +1737,34 @@ Assume MODE (see `filesets-entry-mode'), if provided."
                          (lambda (file)
                            (not (filesets-filetype-property file event))))))
 
+(defun filesets-files-under (level depth entry dir patt &optional relativep)
+  "Files under DIR that match PATT.
+LEVEL is the current level under DIR.
+DEPTH is the maximal tree scanning depth for ENTRY.
+ENTRY is a fileset.
+DIR is a directory.
+PATT is a regexp that included file names must match.
+RELATIVEP non-nil means use relative file names."
+  (and (or (= depth 0) (< level depth))
+       (let* ((dir         (file-name-as-directory dir))
+              (files-here  (filesets-directory-files
+                            dir patt nil (not relativep)
+                            (filesets-entry-get-filter-dirs-flag entry)))
+              (subdirs     (filesets-filter-dir-names files-here))
+              (files
+               (filesets-filter-dir-names
+                (apply #'append
+                       files-here
+                       (mapcar
+                        (lambda (subdir)
+                          (let* ((subdir (file-name-as-directory subdir))
+                                 (full-subdir  (concat dir subdir)))
+                            (filesets-files-under (+ level 1) depth entry
+                                                  full-subdir patt)))
+                        subdirs))
+                t)))
+         files)))
+
 (defun filesets-open (&optional mode name lookup-name)
   "Open the fileset called NAME.
 Use LOOKUP-NAME for searching additional data if provided."
@@ -1878,7 +1909,7 @@ User will be queried, if no fileset name is provided."
                       (substring (elt submenu 0) 2))))
     (if (listp submenu)
        (cons name (cdr submenu))
-      (apply 'vector (list name (cdr (append submenu nil)))))))
+      (apply 'vector (list name (cadr (append submenu nil)))))))
 ;      (vconcat `[,name] (subseq submenu 1)))))
 
 (defun filesets-wrap-submenu (submenu-body)
diff --git a/lisp/gnus/gnus-art.el b/lisp/gnus/gnus-art.el
index 8f4ca7e..7f594c9 100644
--- a/lisp/gnus/gnus-art.el
+++ b/lisp/gnus/gnus-art.el
@@ -7251,7 +7251,6 @@ This is an extended text-mode.
        '(message-font-lock-keywords t))
   (set (make-local-variable 'mail-header-separator) "")
   (set (make-local-variable 'gnus-article-edit-mode) t)
-  (easy-menu-add message-mode-field-menu message-mode-map)
   (mml-mode)
   (setq buffer-read-only nil)
   (buffer-enable-undo)
diff --git a/lisp/gnus/gnus-fun.el b/lisp/gnus/gnus-fun.el
index 3218649..b6d6aa4 100644
--- a/lisp/gnus/gnus-fun.el
+++ b/lisp/gnus/gnus-fun.el
@@ -286,8 +286,8 @@ colors of the displayed X-Faces."
     (setq file (car file))
     (with-temp-buffer
       (shell-command
-       (format "pnmcut -left 110 -top 30 -width 144 -height 144 '%s' | ppmnorm 
2>/dev/null | pnmscale -width 48 | ppmtopgm | pgmtopbm -threshold -value 0.92 | 
pbmtoxbm | compface"
-              file)
+       (format "pnmcut -left 110 -top 30 -width 144 -height 144 '%s' | ppmnorm 
2>%s | pnmscale -width 48 | ppmtopgm | pgmtopbm -threshold -value 0.92 | 
pbmtoxbm | compface"
+              file null-device)
        (current-buffer))
       ;;(sleep-for 3)
       (delete-file file)
diff --git a/lisp/gnus/gnus-search.el b/lisp/gnus/gnus-search.el
index 498da20..89d8cff 100644
--- a/lisp/gnus/gnus-search.el
+++ b/lisp/gnus/gnus-search.el
@@ -1030,6 +1030,13 @@ Responsible for handling and, or, and parenthetical 
expressions.")
 (declare-function nnimap-buffer "nnimap" ())
 (declare-function nnimap-command "nnimap" (&rest args))
 
+(defvar gnus-search-imap-search-keys
+  '(body cc bcc from header keyword larger smaller subject text to uid x-gm-raw
+        answered before deleted draft flagged on since recent seen sentbefore
+        senton sentsince unanswered undeleted undraft unflagged unkeyword
+        unseen all)
+  "Known IMAP search keys.")
+
 ;; imap interface
 (cl-defmethod gnus-search-run-search ((engine gnus-search-imap)
                                      srv query groups)
@@ -1059,6 +1066,15 @@ Responsible for handling and, or, and parenthetical 
expressions.")
       (setq q-string
            (gnus-search-make-query-string engine query))
 
+      ;; A bit of backward-compatibility slash convenience: if the
+      ;; query string doesn't start with any known IMAP search
+      ;; keyword, assume it is a "TEXT" search.
+      (unless (and (string-match "\\`[[:word:]]+" q-string)
+                  (memql (intern-soft (downcase
+                                       (match-string 0 q-string)))
+                         gnus-search-imap-search-keys))
+       (setq q-string (concat "TEXT " q-string)))
+
       ;; If it's a thread query, make sure that all message-id
       ;; searches are also references searches.
       (when (alist-get 'thread query)
@@ -1115,12 +1131,6 @@ Other capabilities could be tested here."
        (nnimap-get-response call)))
      (t (nnimap-command "UID SEARCH %s" query)))))
 
-;; TODO: Don't exclude booleans and date keys, just check for them
-;; before checking for general keywords.
-(defvar gnus-search-imap-search-keys
-  '(body cc bcc from header keyword larger smaller subject text to uid 
x-gm-raw)
-  "Known IMAP search keys, excluding booleans and date keys.")
-
 (cl-defmethod gnus-search-transform ((_ gnus-search-imap)
                                     (_query null))
   "ALL")
@@ -1303,6 +1313,7 @@ of whichever date elements are present."
        (pcase flag
          ("flag" "flagged")
          ("read" "seen")
+         ("replied" "answered")
          (_ flag)))
   (if (member flag '("seen" "answered" "deleted" "draft" "flagged"))
       (upcase flag)
@@ -1365,10 +1376,13 @@ Returns a list of [group article score] vectors."
                                                server query &optional groups)
   (let ((prefix (slot-value engine 'remove-prefix))
        (group-regexp (when groups
-                       (regexp-opt
-                        (mapcar
-                         (lambda (x) (gnus-group-real-name x))
-                         groups))))
+                       (mapconcat
+                        (lambda (x)
+                          (replace-regexp-in-string
+                           ;; Accept any of [.\/] as path separators.
+                           "[.\\/]" "[.\\\\/]"
+                           (gnus-group-real-name x)))
+                        groups "\\|")))
        artlist vectors article group)
     (goto-char (point-min))
     (while (not (eobp))
@@ -1383,16 +1397,16 @@ Returns a list of [group article score] vectors."
     ;; Are we running an additional grep query?
     (when-let ((grep-reg (alist-get 'grep query)))
       (setq artlist (gnus-search-grep-search engine artlist grep-reg)))
+    ;; Prep prefix.
+    (when (and prefix (null (string-empty-p prefix)))
+      (setq prefix (file-name-as-directory (expand-file-name prefix))))
     ;; Turn (file-name score) into [group article score].
     (pcase-dolist (`(,f-name ,score) artlist)
-      (setq article (file-name-nondirectory f-name))
+      (setq article (file-name-nondirectory f-name)
+           group (file-name-directory f-name))
       ;; Remove prefix.
-      (when (and prefix
-                (file-name-absolute-p prefix)
-                (string-match (concat "^"
-                                      (file-name-as-directory prefix))
-                              f-name))
-       (setq group (replace-match "" t t (file-name-directory f-name))))
+      (when prefix
+       (setq group (string-remove-prefix prefix group)))
       ;; Break the directory name down until it's something that
       ;; (probably) can be used as a group name.
       (setq group
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index 561f199..469fa36 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -442,6 +442,16 @@ will go to the next group without confirmation."
                 (const slightly-quietly)
                 (sexp :menu-tag "on" t)))
 
+(defcustom gnus-paging-select-next t
+  "Control whether to select the next/prev article when paging.
+If non-nil, select the next article when reaching the end of the
+article (or the previous article when paging backwards).
+
+If nil, don't do anything at the end/start of the articles."
+  :version "28.1"
+  :group 'gnus-summary-maneuvering
+  :type 'boolean)
+
 (defcustom gnus-auto-select-same nil
   "If non-nil, select the next article with the same subject.
 If there are no more articles with the same subject, go to
@@ -7898,7 +7908,8 @@ Also see the variable `gnus-article-skip-boring'."
                   (gnus-message 3 "End of message"))
                  (circular
                   (gnus-summary-beginning-of-article))
-                 (lines
+                 ((or lines
+                      (not gnus-paging-select-next))
                   (gnus-message 3 "End of message"))
                  ((null lines)
                   (if (and (eq gnus-summary-goto-unread 'never)
@@ -7929,7 +7940,8 @@ the beginning of the buffer."
        (gnus-eval-in-buffer-window gnus-article-buffer
          (setq endp (gnus-article-prev-page lines)))
        (when (and move endp)
-         (cond (lines
+         (cond ((or lines
+                    (not gnus-paging-select-next))
                 (gnus-message 3 "Beginning of message"))
                ((null lines)
                 (if (and (eq gnus-summary-goto-unread 'never)
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 5bdf537..288ccc4 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -3109,8 +3109,6 @@ Like `text-mode', but with these additional commands:
        '(message-font-lock-keywords t))
   (if (boundp 'tool-bar-map)
       (set (make-local-variable 'tool-bar-map) (message-make-tool-bar)))
-  (easy-menu-add message-mode-menu message-mode-map)
-  (easy-menu-add message-mode-field-menu message-mode-map)
   ;; Mmmm... Forbidden properties...
   (add-hook 'after-change-functions #'message-strip-forbidden-properties
            nil 'local)
diff --git a/lisp/gnus/mml2015.el b/lisp/gnus/mml2015.el
index 45c9bbf..0765d7b 100644
--- a/lisp/gnus/mml2015.el
+++ b/lisp/gnus/mml2015.el
@@ -735,8 +735,9 @@ If set, it overrides the setting of 
`mml2015-sign-with-sender'."
     (let* ((coding-system-for-write 'binary)
            (coding-system-for-read 'binary)
            (data (shell-command-to-string
-                  (format "%s --list-options no-show-photos --attribute-fd 3 
--list-keys %s 3>&1 >/dev/null 2>&1"
-                          (shell-quote-argument epg-gpg-program) key-id))))
+                  (format "%s --list-options no-show-photos --attribute-fd 3 
--list-keys %s 3>&1 >%s 2>&1"
+                          (shell-quote-argument epg-gpg-program)
+                         key-id null-device))))
       (when (> (length data) 0)
         (insert (substring data 16))
        (condition-case nil
diff --git a/lisp/gnus/smime.el b/lisp/gnus/smime.el
index eb27fee..08d45e5 100644
--- a/lisp/gnus/smime.el
+++ b/lisp/gnus/smime.el
@@ -398,7 +398,7 @@ Any details (stdout and stderr) are left in the buffer 
specified by
 `smime-details-buffer'."
   (smime-new-details-buffer)
   (if (apply 'smime-call-openssl-region b e (list smime-details-buffer t)
-            "smime" "-verify" "-noverify" "-out" '("/dev/null"))
+            "smime" "-verify" "-noverify" "-out" `(,null-device))
       t
     (insert-buffer-substring smime-details-buffer)
     nil))
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index a4c0431..d5b8df9 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -126,17 +126,48 @@ with the current prefix.  The files are chosen according 
to
   :group 'help
   :version "26.3")
 
+(defun help--symbol-completion-table-affixation (completions)
+  (mapcar (lambda (c)
+            (let* ((s (intern c))
+                   (doc (condition-case nil (documentation s) (error nil)))
+                   (doc (and doc (substring doc 0 (string-match "\n" doc)))))
+              (list c (propertize
+                       (concat (cond ((commandp s)
+                                      "c") ; command
+                                     ((eq (car-safe (symbol-function s)) 
'macro)
+                                      "m") ; macro
+                                     ((fboundp s)
+                                      "f") ; function
+                                     ((custom-variable-p s)
+                                      "u") ; user option
+                                     ((boundp s)
+                                      "v") ; variable
+                                     ((facep s)
+                                      "a") ; fAce
+                                     ((and (fboundp 'cl-find-class)
+                                           (cl-find-class s))
+                                      "t")  ; CL type
+                                     (" ")) ; something else
+                               " ")         ; prefix separator
+                       'face 'completions-annotations)
+                    (if doc (propertize (format " -- %s" doc)
+                                        'face 'completions-annotations)
+                      ""))))
+          completions))
+
 (defun help--symbol-completion-table (string pred action)
-  (when help-enable-completion-autoload
-    (let ((prefixes (radix-tree-prefixes (help-definition-prefixes) string)))
-      (help--load-prefixes prefixes)))
-  (let ((prefix-completions
-         (and help-enable-completion-autoload
-              (mapcar #'intern (all-completions string definition-prefixes)))))
-    (complete-with-action action obarray string
-                          (if pred (lambda (sym)
-                                     (or (funcall pred sym)
-                                         (memq sym prefix-completions)))))))
+  (if (and completions-detailed (eq action 'metadata))
+      '(metadata (affixation-function . 
help--symbol-completion-table-affixation))
+    (when help-enable-completion-autoload
+      (let ((prefixes (radix-tree-prefixes (help-definition-prefixes) string)))
+        (help--load-prefixes prefixes)))
+    (let ((prefix-completions
+           (and help-enable-completion-autoload
+                (mapcar #'intern (all-completions string 
definition-prefixes)))))
+      (complete-with-action action obarray string
+                            (if pred (lambda (sym)
+                                       (or (funcall pred sym)
+                                           (memq sym prefix-completions))))))))
 
 (defvar describe-function-orig-buffer nil
   "Buffer that was current when `describe-function' was invoked.
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index d361971..d59f2c0 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -3084,13 +3084,11 @@ on encoding."
         (puthash "BELL (BEL)" ?\a names)
         (setq ucs-names names))))
 
-(defun mule--ucs-names-annotation (name)
-  ;; FIXME: It would be much better to add this annotation before rather than
-  ;; after the char name, so the annotations are aligned.
-  ;; FIXME: The default behavior of displaying annotations in italics
-  ;; doesn't work well here.
-  (let ((char (gethash name ucs-names)))
-    (when char (format " (%c)" char))))
+(defun mule--ucs-names-affixation (names)
+  (mapcar (lambda (name)
+            (let ((char (gethash name ucs-names)))
+              (list name (concat (if char (format "%c" char) " ") "\t") "")))
+          names))
 
 (defun char-from-name (string &optional ignore-case)
   "Return a character as a number from its Unicode name STRING.
@@ -3133,13 +3131,14 @@ octal).  Treat otherwise-ambiguous strings like \"BED\" 
(U+1F6CF)
 as names, not numbers."
   (let* ((enable-recursive-minibuffers t)
         (completion-ignore-case t)
+        (completion-tab-width 4)
         (input
          (completing-read
           prompt
           (lambda (string pred action)
             (if (eq action 'metadata)
                 '(metadata
-                  (annotation-function . mule--ucs-names-annotation)
+                  (affixation-function . mule--ucs-names-affixation)
                   (category . unicode-name))
               (complete-with-action action (ucs-names) string pred)))))
         (char
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 4fba437..a0aa250 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -2500,11 +2500,18 @@ If search string is empty, just beep."
   "Replace just-yanked search string with previously killed string."
   (interactive)
   (if (not (memq last-command '(isearch-yank-kill isearch-yank-pop)))
-      ;; Fall back on `isearch-yank-kill' for the benefits of people
-      ;; who are used to the old behavior of `M-y' in isearch mode. In
-      ;; future, this fallback may be changed if we ever change
-      ;; `yank-pop' to do something like the kill-ring-browser.
-      (isearch-yank-kill)
+      ;; Yank string from kill-ring-browser.
+      (with-isearch-suspended
+       (let ((string (read-from-kill-ring)))
+         (if (and isearch-case-fold-search
+                  (eq 'not-yanks search-upper-case))
+             (setq string (downcase string)))
+         (if isearch-regexp (setq string (regexp-quote string)))
+         (setq isearch-yank-flag t)
+         (setq isearch-new-string (concat isearch-string string)
+               isearch-new-message (concat isearch-message
+                                           (mapconcat 
'isearch-text-char-description
+                                                      string "")))))
     (isearch-pop-state)
     (isearch-yank-string (current-kill 1))))
 
diff --git a/lisp/leim/quail/arabic.el b/lisp/leim/quail/arabic.el
index a3424c8..dd840c3 100644
--- a/lisp/leim/quail/arabic.el
+++ b/lisp/leim/quail/arabic.el
@@ -1,4 +1,4 @@
-;;; arabic.el --- Quail package for inputting Arabic   -*- coding: utf-8;-*-
+;;; arabic.el --- Quail package for inputting Arabic   -*- coding: utf-8; 
lexical-binding:t -*-
 
 ;; Copyright (C) 2007-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/croatian.el b/lisp/leim/quail/croatian.el
index 97bfb85..2efd7ce 100644
--- a/lisp/leim/quail/croatian.el
+++ b/lisp/leim/quail/croatian.el
@@ -1,4 +1,4 @@
-;;; croatian.el -- Quail package for inputting Croatian  -*-coding: utf-8;-*-
+;;; croatian.el -- Quail package for inputting Croatian  -*-coding: utf-8; 
lexical-binding:t -*-
 
 ;; Copyright (C) 2003-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/cyril-jis.el b/lisp/leim/quail/cyril-jis.el
index 6109dfc..730042b 100644
--- a/lisp/leim/quail/cyril-jis.el
+++ b/lisp/leim/quail/cyril-jis.el
@@ -1,4 +1,4 @@
-;;; cyril-jis.el --- Quail package for inputting JISX0208 Cyrillic letters
+;;; cyril-jis.el --- Quail package for inputting JISX0208 Cyrillic letters  
-*- lexical-binding: t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
diff --git a/lisp/leim/quail/cyrillic.el b/lisp/leim/quail/cyrillic.el
index 8845223..fa209f0 100644
--- a/lisp/leim/quail/cyrillic.el
+++ b/lisp/leim/quail/cyrillic.el
@@ -1,4 +1,4 @@
-;;; cyrillic.el --- Quail package for inputting Cyrillic characters
+;;; cyrillic.el --- Quail package for inputting Cyrillic characters  -*- 
lexical-binding: t -*-
 
 ;; Copyright (C) 1997-1998, 2001-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
diff --git a/lisp/leim/quail/czech.el b/lisp/leim/quail/czech.el
index 7c208ef..34416c2 100644
--- a/lisp/leim/quail/czech.el
+++ b/lisp/leim/quail/czech.el
@@ -1,4 +1,4 @@
-;;; czech.el --- Quail package for inputting Czech -*-coding: utf-8;-*-
+;;; czech.el --- Quail package for inputting Czech -*-coding: utf-8; 
lexical-binding:t -*-
 
 ;; Copyright (C) 1998, 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/ethiopic.el b/lisp/leim/quail/ethiopic.el
index 8d19a23..c8753ef 100644
--- a/lisp/leim/quail/ethiopic.el
+++ b/lisp/leim/quail/ethiopic.el
@@ -1,4 +1,4 @@
-;;; ethiopic.el --- Quail package for inputting Ethiopic characters  
-*-coding: utf-8-emacs;-*-
+;;; ethiopic.el --- Quail package for inputting Ethiopic characters  
-*-coding: utf-8-emacs; lexical-binding:t -*-
 
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
 ;;   2006, 2007, 2008, 2009, 2010, 2011
diff --git a/lisp/leim/quail/georgian.el b/lisp/leim/quail/georgian.el
index b6da6be..e66477a 100644
--- a/lisp/leim/quail/georgian.el
+++ b/lisp/leim/quail/georgian.el
@@ -1,4 +1,4 @@
-;;; georgian.el --- Quail package for inputting Georgian characters  
-*-coding: utf-8;-*-
+;;; georgian.el --- Quail package for inputting Georgian characters  -*- 
coding: utf-8; lexical-binding:t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/greek.el b/lisp/leim/quail/greek.el
index 14bbacc..eedfb9d 100644
--- a/lisp/leim/quail/greek.el
+++ b/lisp/leim/quail/greek.el
@@ -1,4 +1,4 @@
-;;; greek.el --- Quail package for inputting Greek -*-coding: utf-8-*-
+;;; greek.el --- Quail package for inputting Greek -*- coding: utf-8; 
lexical-binding:t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
diff --git a/lisp/leim/quail/hanja-jis.el b/lisp/leim/quail/hanja-jis.el
index 6f75325..c4eb4b5 100644
--- a/lisp/leim/quail/hanja-jis.el
+++ b/lisp/leim/quail/hanja-jis.el
@@ -1,4 +1,4 @@
-;;; hanja-jis.el --- Quail package for inputting Korean Hanja (JISX0208)
+;;; hanja-jis.el --- Quail package for inputting Korean Hanja (JISX0208)  -*- 
lexical-binding: t -*-
 
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
 ;;   2006, 2007, 2008, 2009, 2010, 2011
diff --git a/lisp/leim/quail/hanja.el b/lisp/leim/quail/hanja.el
index 455af12..3ca17c6 100644
--- a/lisp/leim/quail/hanja.el
+++ b/lisp/leim/quail/hanja.el
@@ -1,4 +1,4 @@
-;;; hanja.el --- Quail-package for Korean Hanja (KSC5601)  -*-coding: utf-8;-*-
+;;; hanja.el --- Quail-package for Korean Hanja (KSC5601)  -*-coding: utf-8; 
lexical-binding: t -*-
 
 ;; Copyright (C) 1997, 2001-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
diff --git a/lisp/leim/quail/hanja3.el b/lisp/leim/quail/hanja3.el
index f318c01..b380d40 100644
--- a/lisp/leim/quail/hanja3.el
+++ b/lisp/leim/quail/hanja3.el
@@ -1,4 +1,4 @@
-;;; hanja3.el --- Quail-package for Korean Hanja (KSC5601)  -*-coding: 
utf-8;-*-
+;;; hanja3.el --- Quail-package for Korean Hanja (KSC5601)  -*-coding: utf-8; 
lexical-binding: t -*-
 
 ;; Copyright (C) 1997, 1999, 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/hebrew.el b/lisp/leim/quail/hebrew.el
index 772da70..fc6bb80 100644
--- a/lisp/leim/quail/hebrew.el
+++ b/lisp/leim/quail/hebrew.el
@@ -1,4 +1,4 @@
-;; hebrew.el --- Quail package for inputting Hebrew characters  -*-coding: 
utf-8;-*-
+;; hebrew.el --- Quail package for inputting Hebrew characters  -*- coding: 
utf-8; lexical-binding: t -*-
 
 ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
 ;;   2008, 2009, 2010, 2011
diff --git a/lisp/leim/quail/ipa-praat.el b/lisp/leim/quail/ipa-praat.el
index 4c241cc..cc577c6 100644
--- a/lisp/leim/quail/ipa-praat.el
+++ b/lisp/leim/quail/ipa-praat.el
@@ -1,4 +1,4 @@
-;;; ipa-praat.el --- Inputting IPA characters with the conventions of Praat
+;;; ipa-praat.el --- Inputting IPA characters with the conventions of Praat  
-*- lexical-binding: t -*-
 
 ;; Copyright (C) 2011-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/latin-alt.el b/lisp/leim/quail/latin-alt.el
index 06ab5e3..4661ba8 100644
--- a/lisp/leim/quail/latin-alt.el
+++ b/lisp/leim/quail/latin-alt.el
@@ -1,4 +1,4 @@
-;;; latin-alt.el --- Quail package for inputting various European characters 
-*-coding: utf-8;-*-
+;;; latin-alt.el --- Quail package for inputting various European characters 
-*-coding: utf-8; lexical-binding: t -*-
 
 ;; Copyright (C) 1997-1998, 2001-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
diff --git a/lisp/leim/quail/latin-post.el b/lisp/leim/quail/latin-post.el
index 1d6aedd..293c0c0 100644
--- a/lisp/leim/quail/latin-post.el
+++ b/lisp/leim/quail/latin-post.el
@@ -1,4 +1,4 @@
-;;; latin-post.el --- Quail packages for inputting various European characters 
 -*-coding: utf-8;-*-
+;;; latin-post.el --- Quail packages for inputting various European characters 
 -*-coding: utf-8; lexical-binding: t -*-
 
 ;; Copyright (C) 1997-1998, 2001-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
diff --git a/lisp/leim/quail/latin-pre.el b/lisp/leim/quail/latin-pre.el
index f421a7e..802b70e 100644
--- a/lisp/leim/quail/latin-pre.el
+++ b/lisp/leim/quail/latin-pre.el
@@ -1,4 +1,4 @@
-;;; latin-pre.el --- Quail packages for inputting various European characters  
-*-coding: utf-8;-*-
+;;; latin-pre.el --- Quail packages for inputting various European characters  
-*-coding: utf-8; lexical-binding: t -*-
 
 ;; Copyright (C) 1997-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
diff --git a/lisp/leim/quail/persian.el b/lisp/leim/quail/persian.el
index f12abc1..fcbf727 100644
--- a/lisp/leim/quail/persian.el
+++ b/lisp/leim/quail/persian.el
@@ -1,4 +1,4 @@
-;;; persian.el  --- Quail package for inputting Persian/Farsi keyboard -*- 
coding: utf-8;-*-
+;;; persian.el  --- Quail package for inputting Persian/Farsi keyboard -*- 
coding: utf-8; lexical-binding: t -*-
 
 ;; Copyright (C) 2011-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/programmer-dvorak.el 
b/lisp/leim/quail/programmer-dvorak.el
index 0429df0..c544c62 100644
--- a/lisp/leim/quail/programmer-dvorak.el
+++ b/lisp/leim/quail/programmer-dvorak.el
@@ -1,4 +1,4 @@
-;;; programmer-dvorak.el --- Quail package for the programmer Dvorak layout
+;;; programmer-dvorak.el --- Quail package for the programmer Dvorak layout  
-*- lexical-binding: t -*-
 
 ;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/py-punct.el b/lisp/leim/quail/py-punct.el
index 90fa474..76bf612 100644
--- a/lisp/leim/quail/py-punct.el
+++ b/lisp/leim/quail/py-punct.el
@@ -1,4 +1,4 @@
-;;; py-punct.el --- Quail packages for Chinese (pinyin + extra symbols)
+;;; py-punct.el --- Quail packages for Chinese (pinyin + extra symbols)  -*- 
lexical-binding: t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
diff --git a/lisp/leim/quail/pypunct-b5.el b/lisp/leim/quail/pypunct-b5.el
index 9f4e73c..f9330bd 100644
--- a/lisp/leim/quail/pypunct-b5.el
+++ b/lisp/leim/quail/pypunct-b5.el
@@ -1,4 +1,4 @@
-;;; pypunct-b5.el --- Quail packages for Chinese (pinyin + extra symbols)
+;;; pypunct-b5.el --- Quail packages for Chinese (pinyin + extra symbols)  -*- 
lexical-binding: t -*-
 
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
 ;;   2006, 2007, 2008, 2009, 2010, 2011
diff --git a/lisp/leim/quail/rfc1345.el b/lisp/leim/quail/rfc1345.el
index c2e7cd9..7f98021 100644
--- a/lisp/leim/quail/rfc1345.el
+++ b/lisp/leim/quail/rfc1345.el
@@ -1,4 +1,4 @@
-;;; rfc1345.el --- Quail method for RFC 1345 mnemonics -*- coding: utf-8 -*-
+;;; rfc1345.el --- Quail method for RFC 1345 mnemonics -*- coding: utf-8; 
lexical-binding: t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/sami.el b/lisp/leim/quail/sami.el
index 66750c3..33fe287 100644
--- a/lisp/leim/quail/sami.el
+++ b/lisp/leim/quail/sami.el
@@ -1,4 +1,4 @@
-;;; sami.el --- Quail package for inputting Sámi  -*-coding: utf-8;-*-
+;;; sami.el --- Quail package for inputting Sámi  -*-coding: utf-8; 
lexical-binding: t -*-
 
 ;; Copyright (C) 2019-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/sgml-input.el b/lisp/leim/quail/sgml-input.el
index 5cc9f5e..df7ac98 100644
--- a/lisp/leim/quail/sgml-input.el
+++ b/lisp/leim/quail/sgml-input.el
@@ -1,4 +1,4 @@
-;;; sgml-input.el --- Quail method for Unicode entered as SGML entities -*- 
coding: utf-8 -*-
+;;; sgml-input.el --- Quail method for Unicode entered as SGML entities -*- 
coding: utf-8; lexical-binding: t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/slovak.el b/lisp/leim/quail/slovak.el
index 828d25a..c306be1 100644
--- a/lisp/leim/quail/slovak.el
+++ b/lisp/leim/quail/slovak.el
@@ -1,4 +1,4 @@
-;;; slovak.el --- Quail package for inputting Slovak  -*-coding: utf-8;-*-
+;;; slovak.el --- Quail package for inputting Slovak  -*-coding: utf-8; 
lexical-binding: t -*-
 
 ;; Copyright (C) 1998, 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/symbol-ksc.el b/lisp/leim/quail/symbol-ksc.el
index ae33786..e346f7c 100644
--- a/lisp/leim/quail/symbol-ksc.el
+++ b/lisp/leim/quail/symbol-ksc.el
@@ -1,4 +1,4 @@
-;;; symbol-ksc.el --- Quail-package for Korean Symbol (KSC5601) -*-coding: 
utf-8;-*-
+;;; symbol-ksc.el --- Quail-package for Korean Symbol (KSC5601) -*-coding: 
utf-8; lexical-binding: t -*-
 
 ;; Copyright (C) 1997, 2001-2020 Free Software Foundation, Inc.
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
diff --git a/lisp/leim/quail/tamil-dvorak.el b/lisp/leim/quail/tamil-dvorak.el
index 93ff0cc..0a70c3b 100644
--- a/lisp/leim/quail/tamil-dvorak.el
+++ b/lisp/leim/quail/tamil-dvorak.el
@@ -1,4 +1,4 @@
-;;; tamil-dvorak.el --- Quail package for Tamil input with Dvorak keyboard
+;;; tamil-dvorak.el --- Quail package for Tamil input with Dvorak keyboard  
-*- lexical-binding: t -*-
 
 ;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/vntelex.el b/lisp/leim/quail/vntelex.el
index 385bc91..8d53ab0 100644
--- a/lisp/leim/quail/vntelex.el
+++ b/lisp/leim/quail/vntelex.el
@@ -1,4 +1,4 @@
-;;; vntelex.el --- Quail package for Vietnamese by Telex method
+;;; vntelex.el --- Quail package for Vietnamese by Telex method  -*- 
lexical-binding: t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/vnvni.el b/lisp/leim/quail/vnvni.el
index 36cad8e..e797304 100644
--- a/lisp/leim/quail/vnvni.el
+++ b/lisp/leim/quail/vnvni.el
@@ -1,4 +1,4 @@
-;;; vnvni.el --- Quail package for Vietnamese by VNI method
+;;; vnvni.el --- Quail package for Vietnamese by VNI method  -*- 
lexical-binding: t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/leim/quail/welsh.el b/lisp/leim/quail/welsh.el
index bc6e8ec..a62d2f5 100644
--- a/lisp/leim/quail/welsh.el
+++ b/lisp/leim/quail/welsh.el
@@ -1,4 +1,4 @@
-;;; welsh.el --- Quail package for inputting Welsh characters  -*-coding: 
utf-8;-*-
+;;; welsh.el --- Quail package for inputting Welsh characters  -*- coding: 
utf-8; lexical-binding: t -*-
 
 ;; Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
diff --git a/lisp/mh-e/mh-folder.el b/lisp/mh-e/mh-folder.el
index 0a73ff4..0fb5ebf 100644
--- a/lisp/mh-e/mh-folder.el
+++ b/lisp/mh-e/mh-folder.el
@@ -656,9 +656,10 @@ perform the operation on all messages in that region.
   (mh-funcall-if-exists hl-line-mode 1)
   (setq revert-buffer-function 'mh-undo-folder)
   (add-to-list 'minor-mode-alist '(mh-showing-mode " Show"))
-  (easy-menu-add mh-folder-sequence-menu)
-  (easy-menu-add mh-folder-message-menu)
-  (easy-menu-add mh-folder-folder-menu)
+  (mh-do-in-xemacs
+    (easy-menu-add mh-folder-sequence-menu)
+    (easy-menu-add mh-folder-message-menu)
+    (easy-menu-add mh-folder-folder-menu))
   (mh-inc-spool-make)
   (mh-set-help mh-folder-mode-help-messages)
   (if (and (featurep 'xemacs)
diff --git a/lisp/mh-e/mh-identity.el b/lisp/mh-e/mh-identity.el
index ed23996..23fa87d 100644
--- a/lisp/mh-e/mh-identity.el
+++ b/lisp/mh-e/mh-identity.el
@@ -91,7 +91,7 @@ See `mh-identity-add-menu'."
   "Add the current Identity menu.
 See `mh-identity-make-menu'."
   (if mh-identity-menu
-      (easy-menu-add mh-identity-menu)))
+      (mh-do-in-xemacs (easy-menu-add mh-identity-menu))))
 
 (defvar mh-identity-local nil
   "Buffer-local variable that holds the identity currently in use.")
diff --git a/lisp/mh-e/mh-letter.el b/lisp/mh-e/mh-letter.el
index 5e1ce40..b8aca77 100644
--- a/lisp/mh-e/mh-letter.el
+++ b/lisp/mh-e/mh-letter.el
@@ -330,7 +330,7 @@ order).
    (t
     ;; ...or the header only
     (setq font-lock-defaults '((mh-show-font-lock-keywords) t))))
-  (easy-menu-add mh-letter-menu)
+  (mh-do-in-xemacs (easy-menu-add mh-letter-menu))
   ;; Maybe we want to use the existing Mail menu from mail-mode in
   ;; 9.0; in the mean time, let's remove it since the redundancy will
   ;; only produce confusion.
diff --git a/lisp/mh-e/mh-search.el b/lisp/mh-e/mh-search.el
index 6fb76be..5cfe678 100644
--- a/lisp/mh-e/mh-search.el
+++ b/lisp/mh-e/mh-search.el
@@ -618,7 +618,7 @@ The hook `mh-search-mode-hook' is called upon entry to this 
mode.
 
 \\{mh-search-mode-map}"
 
-  (easy-menu-add mh-pick-menu)
+  (mh-do-in-xemacs (easy-menu-add mh-pick-menu))
   (mh-set-help mh-search-mode-help-messages))
 
 
diff --git a/lisp/mh-e/mh-show.el b/lisp/mh-e/mh-show.el
index 7536f94..48ff741 100644
--- a/lisp/mh-e/mh-show.el
+++ b/lisp/mh-e/mh-show.el
@@ -863,9 +863,10 @@ See also `mh-folder-mode'.
   (when mh-decode-mime-flag
     (mh-make-local-hook 'kill-buffer-hook)
     (add-hook 'kill-buffer-hook 'mh-mime-cleanup nil t))
-  (easy-menu-add mh-show-sequence-menu)
-  (easy-menu-add mh-show-message-menu)
-  (easy-menu-add mh-show-folder-menu)
+  (mh-do-in-xemacs
+    (easy-menu-add mh-show-sequence-menu)
+    (easy-menu-add mh-show-message-menu)
+    (easy-menu-add mh-show-folder-menu))
   (make-local-variable 'mh-show-folder-buffer)
   (buffer-disable-undo)
   (use-local-map mh-show-mode-map))
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 9d57a81..d44d896 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -83,7 +83,6 @@
 
 ;; - add support for ** to pcm.
 ;; - Add vc-file-name-completion-table to read-file-name-internal.
-;; - A feature like completing-help.el.
 
 ;;; Code:
 
@@ -121,6 +120,10 @@ This metadata is an alist.  Currently understood keys are:
 - `annotation-function': function to add annotations in *Completions*.
    Takes one argument (STRING), which is a possible completion and
    returns a string to append to STRING.
+- `affixation-function': function to prepend/append a prefix/suffix to
+   entries.  Takes one argument (COMPLETIONS) and should return a list
+   of completions with a list of three elements: completion, its prefix
+   and suffix.
 - `display-sort-function': function to sort entries in *Completions*.
    Takes one argument (COMPLETIONS) and should return a new list
    of completions.  Can operate destructively.
@@ -1131,6 +1134,7 @@ completion candidates than this number."
 (defvar-local completion-all-sorted-completions nil)
 (defvar-local completion--all-sorted-completions-location nil)
 (defvar completion-cycling nil)      ;Function that takes down the cycling map.
+(defvar completion-tab-width nil)
 
 (defvar completion-fail-discreetly nil
   "If non-nil, stay quiet when there  is no match.")
@@ -1669,18 +1673,27 @@ Return nil if there is no valid completion, else t."
     (#b000 nil)
       (_     t))))
 
-(defface completions-annotations '((t :inherit italic))
+(defface completions-annotations '((t :inherit (italic shadow)))
   "Face to use for annotations in the *Completions* buffer.")
 
 (defcustom completions-format 'horizontal
   "Define the appearance and sorting of completions.
 If the value is `vertical', display completions sorted vertically
 in columns in the *Completions* buffer.
-If the value is `horizontal', display completions sorted
-horizontally in alphabetical order, rather than down the screen."
-  :type '(choice (const horizontal) (const vertical))
+If the value is `horizontal', display completions sorted in columns
+horizontally in alphabetical order, rather than down the screen.
+If the value is `one-column', display completions down the screen
+in one column."
+  :type '(choice (const horizontal) (const vertical) (const one-column))
   :version "23.2")
 
+(defcustom completions-detailed nil
+  "When non-nil, display completions with details added as prefix/suffix.
+Some commands might provide a detailed view with more information prepended
+or appended to completions."
+  :type 'boolean
+  :version "28.1")
+
 (defun completion--insert-strings (strings)
   "Insert a list of STRINGS into the current buffer.
 Uses columns to keep the listing readable but compact.
@@ -1689,8 +1702,7 @@ It also eliminates runs of equal strings."
     (let* ((length (apply #'max
                          (mapcar (lambda (s)
                                    (if (consp s)
-                                       (+ (string-width (car s))
-                                           (string-width (cadr s)))
+                                       (apply #'+ (mapcar #'string-width s))
                                      (string-width s)))
                                  strings)))
           (window (get-buffer-window (current-buffer) 0))
@@ -1707,6 +1719,11 @@ It also eliminates runs of equal strings."
           (row 0)
            (first t)
           (laststring nil))
+      (unless (or tab-stop-list (null completion-tab-width)
+                  (zerop (mod colwidth completion-tab-width)))
+        ;; Align to tab positions for the case
+        ;; when the caller uses tabs inside prefix.
+        (setq colwidth (- colwidth (mod colwidth completion-tab-width))))
       ;; The insertion should be "sensible" no matter what choices were made
       ;; for the parameters above.
       (dolist (str strings)
@@ -1715,10 +1732,12 @@ It also eliminates runs of equal strings."
           ;; FIXME: `string-width' doesn't pay attention to
           ;; `display' properties.
           (let ((length (if (consp str)
-                            (+ (string-width (car str))
-                               (string-width (cadr str)))
+                            (apply #'+ (mapcar #'string-width str))
                           (string-width str))))
             (cond
+             ((eq completions-format 'one-column)
+              ;; Nothing special
+              )
             ((eq completions-format 'vertical)
              ;; Vertical format
              (when (> row rows)
@@ -1745,23 +1764,46 @@ It also eliminates runs of equal strings."
                  ;; already past the goal column, there is still
                  ;; a space displayed.
                  (set-text-properties (1- (point)) (point)
-                                      ;; We can't just set tab-width, because
-                                      ;; completion-setup-function will kill
-                                      ;; all local variables :-(
+                                      ;; We can set tab-width using
+                                      ;; completion-tab-width, but
+                                      ;; the caller can prefer using
+                                      ;; \t to align prefixes.
                                       `(display (space :align-to ,column)))
                  nil))))
             (setq first nil)
             (if (not (consp str))
                 (put-text-property (point) (progn (insert str) (point))
                                    'mouse-face 'highlight)
-              (put-text-property (point) (progn (insert (car str)) (point))
-                                 'mouse-face 'highlight)
-              (let ((beg (point))
-                    (end (progn (insert (cadr str)) (point))))
-                (put-text-property beg end 'mouse-face nil)
-                (font-lock-prepend-text-property beg end 'face
-                                                 'completions-annotations)))
+              ;; If `str' is a list that has 2 elements,
+              ;; then the second element is a suffix annotation.
+              ;; If `str' has 3 elements, then the second element
+              ;; is a prefix, and the third element is a suffix.
+              (let* ((prefix (when (nth 2 str) (nth 1 str)))
+                     (suffix (or (nth 2 str) (nth 1 str))))
+                (when prefix
+                  (let ((beg (point))
+                        (end (progn (insert prefix) (point))))
+                    (put-text-property beg end 'mouse-face nil)
+                    ;; When both prefix and suffix are added
+                    ;; by the caller via affixation-function,
+                    ;; then allow the caller to decide
+                    ;; what faces to put on prefix and suffix.
+                    (unless prefix
+                      (font-lock-prepend-text-property
+                       beg end 'face 'completions-annotations))))
+                (put-text-property (point) (progn (insert (car str)) (point))
+                                   'mouse-face 'highlight)
+                (let ((beg (point))
+                      (end (progn (insert suffix) (point))))
+                  (put-text-property beg end 'mouse-face nil)
+                  ;; Put the predefined face only when suffix
+                  ;; is added via annotation-function.
+                  (unless prefix
+                    (font-lock-prepend-text-property
+                     beg end 'face 'completions-annotations)))))
            (cond
+             ((eq completions-format 'one-column)
+              (insert "\n"))
             ((eq completions-format 'vertical)
              ;; Vertical format
              (if (> column 0)
@@ -1880,6 +1922,11 @@ These include:
    completion).  The function can access the completion data via
    `minibuffer-completion-table' and related variables.
 
+`:affixation-function': Function to prepend/append a prefix/suffix to
+   completions.  The function must accept one argument, a list of
+   completions, and return a list where each element is a list of
+   three elements: a completion, a prefix and a suffix.
+
 `:exit-function': Function to run after completion is performed.
 
    The function must accept two arguments, STRING and STATUS.
@@ -1962,10 +2009,13 @@ variables.")
                                            base-size md
                                            minibuffer-completion-table
                                            minibuffer-completion-predicate))
-             (afun (or (completion-metadata-get all-md 'annotation-function)
-                       (plist-get completion-extra-properties
-                                  :annotation-function)
-                       completion-annotate-function))
+             (ann-fun (or (completion-metadata-get all-md 'annotation-function)
+                          (plist-get completion-extra-properties
+                                     :annotation-function)
+                          completion-annotate-function))
+             (aff-fun (or (completion-metadata-get all-md 'affixation-function)
+                          (plist-get completion-extra-properties
+                                     :affixation-function)))
              (mainbuf (current-buffer))
              ;; If the *Completions* buffer is shown in a new
              ;; window, mark it as softly-dedicated, so bury-buffer in
@@ -2006,12 +2056,15 @@ variables.")
                               (if sort-fun
                                   (funcall sort-fun completions)
                                 (sort completions 'string-lessp))))
-                      (when afun
+                      (when ann-fun
                         (setq completions
                               (mapcar (lambda (s)
-                                        (let ((ann (funcall afun s)))
+                                        (let ((ann (funcall ann-fun s)))
                                           (if ann (list s ann) s)))
                                       completions)))
+                      (when aff-fun
+                        (setq completions
+                              (funcall aff-fun completions)))
 
                       (with-current-buffer standard-output
                         (set (make-local-variable 'completion-base-position)
@@ -3034,19 +3087,6 @@ the commands start with a \"-\" or a SPC."
   :version "24.1"
   :type 'boolean)
 
-(defcustom minibuffer-default-prompt-format " (default %s)"
-  "Format string used to output \"default\" values.
-When prompting for input, there will often be a default value,
-leading to prompts like \"Number of articles (default 50): \".
-The \"default\" part of that prompt is controlled by this
-variable, and can be set to, for instance, \" [%s]\" if you want
-a shorter displayed prompt, or \"\", if you don't want to display
-the default at all.
-
-This variable is used by the `format-prompt' function."
-  :version "28.1"
-  :type 'string)
-
 (defun completion-pcm--pattern-trivial-p (pattern)
   (and (stringp (car pattern))
        ;; It can be followed by `point' and "" and still be trivial.
@@ -3864,6 +3904,19 @@ the minibuffer was activated, and execute the forms."
   (with-minibuffer-selected-window
     (scroll-other-window-down arg)))
 
+(defcustom minibuffer-default-prompt-format " (default %s)"
+  "Format string used to output \"default\" values.
+When prompting for input, there will often be a default value,
+leading to prompts like \"Number of articles (default 50): \".
+The \"default\" part of that prompt is controlled by this
+variable, and can be set to, for instance, \" [%s]\" if you want
+a shorter displayed prompt, or \"\", if you don't want to display
+the default at all.
+
+This variable is used by the `format-prompt' function."
+  :version "28.1"
+  :type 'string)
+
 (defun format-prompt (prompt default &rest format-args)
   "Format PROMPT with DEFAULT according to `minibuffer-default-prompt-format'.
 If FORMAT-ARGS is nil, PROMPT is used as a plain string.  If
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 9ed01ec..0c43fc6 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -56,6 +56,13 @@
   :group 'eww
   :type 'string)
 
+(defcustom eww-use-browse-url "\\`mailto:";
+  "eww will use `browse-url' when following links that match this regexp.
+The action to be taken can be further customized via
+`browse-url-handlers'."
+  :version "28.1"
+  :type 'regexp)
+
 (defun erc--download-directory ()
   "Return the name of the download directory.
 If ~/Downloads/ exists, that will be used, and if not, the
@@ -1731,7 +1738,7 @@ If EXTERNAL is double prefix, browse in new buffer."
     (cond
      ((not url)
       (message "No link under point"))
-     ((string-match-p "\\`mailto:"; url)
+     ((string-match-p eww-use-browse-url url)
       ;; This respects the user options `browse-url-handlers'
       ;; and `browse-url-mailto-function'.
       (browse-url url))
diff --git a/lisp/net/sieve.el b/lisp/net/sieve.el
index 08367c6..75bb4e2 100644
--- a/lisp/net/sieve.el
+++ b/lisp/net/sieve.el
@@ -143,8 +143,7 @@ require \"fileinto\";
 (define-derived-mode sieve-manage-mode special-mode "Sieve-manage"
   "Mode used for sieve script management."
   (buffer-disable-undo (current-buffer))
-  (setq truncate-lines t)
-  (easy-menu-add sieve-manage-mode-menu sieve-manage-mode-map))
+  (setq truncate-lines t))
 
 ;; Commands used in sieve-manage mode:
 
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index d2265ed..2851110 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -4979,7 +4979,7 @@ connection if a previous connection has died for some 
reason."
              (setenv "PS1" tramp-initial-end-of-output)
               (unless (stringp tramp-encoding-shell)
                 (tramp-error vec 'file-error "`tramp-encoding-shell' not set"))
-             (let* ((current-host (system-name))
+             (let* ((current-host tramp-system-name)
                     (target-alist (tramp-compute-multi-hops vec))
                     ;; We will apply `tramp-ssh-controlmaster-options'
                     ;; only for the first hop.
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index d40f9a5..6ae79be 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -175,6 +175,12 @@ See the variable `tramp-encoding-shell' for more 
information."
   :version "24.1"
   :type '(choice (const nil) string))
 
+;; Since Emacs 26.1, `system-name' can return `nil' at build time if
+;; Emacs is compiled with "--no-build-details".  We do expect it to be
+;; a string.  (Bug#44481)
+(defconst tramp-system-name (or (system-name) "")
+  "The system name Tramp is running locally.")
+
 (defvar tramp-methods nil
   "Alist of methods for remote files.
 This is a list of entries of the form (NAME PARAM1 PARAM2 ...).
@@ -417,7 +423,7 @@ empty string for the method name."
                       (choice :tag "  Host regexp" regexp sexp)
                       (choice :tag "    User name" string (const nil)))))
 
-(defcustom tramp-default-host (system-name)
+(defcustom tramp-default-host tramp-system-name
   "Default host to use for transferring files.
 Useful for su and sudo methods mostly."
   :type 'string)
@@ -472,8 +478,8 @@ interpreted as a regular expression which always matches."
 (defcustom tramp-restricted-shell-hosts-alist
   (when (memq system-type '(windows-nt))
     (list (format "\\`\\(%s\\|%s\\)\\'"
-                 (regexp-quote (downcase (system-name)))
-                 (regexp-quote (upcase (system-name))))))
+                 (regexp-quote (downcase tramp-system-name))
+                 (regexp-quote (upcase tramp-system-name)))))
   "List of hosts, which run a restricted shell.
 This is a list of regular expressions, which denote hosts running
 a restricted shell like \"rbash\".  Those hosts can be used as
@@ -486,7 +492,7 @@ host runs a restricted shell, it shall be added to this 
list, too."
   (concat
    "\\`"
    (regexp-opt
-    (list "localhost" "localhost6" (system-name) "127.0.0.1" "::1") t)
+    (list "localhost" "localhost6" tramp-system-name "127.0.0.1" "::1") t)
    "\\'")
   "Host names which are regarded as local host.
 If the local host runs a chrooted environment, set this to nil."
diff --git a/lisp/obsolete/otodo-mode.el b/lisp/obsolete/otodo-mode.el
index 2a4af29..6aada07 100644
--- a/lisp/obsolete/otodo-mode.el
+++ b/lisp/obsolete/otodo-mode.el
@@ -923,7 +923,8 @@ If INCLUDE-SEP is non-nil, return point after the 
separator."
 ;;;###autoload
 (define-derived-mode todo-mode nil "TODO"
   "Major mode for editing TODO lists."
-  (easy-menu-add todo-menu))
+  (when (featurep 'xemacs)
+    (easy-menu-add todo-menu)))
 
 (with-suppressed-warnings ((lexical date entry))
   (defvar date)
diff --git a/lisp/org/ob-picolisp.el b/lisp/org/ob-picolisp.el
index ec2a228..bf31197 100644
--- a/lisp/org/ob-picolisp.el
+++ b/lisp/org/ob-picolisp.el
@@ -111,11 +111,11 @@ This function is called by `org-babel-execute-src-block'."
           (cond
            ((or (member "code" result-params)
                 (member "pp" result-params))
-            (format "(pretty (out \"/dev/null\" %s))" full-body))
+            (format "(pretty (out \"%s\" %s))" null-device full-body))
            ((and (member "value" result-params) (not session))
-            (format "(print (out \"/dev/null\" %s))" full-body))
+            (format "(print (out \"%s\" %s))" null-device full-body))
            ((member "value" result-params)
-            (format "(out \"/dev/null\" %s)" full-body))
+            (format "(out \"%s\" %s)" null-device full-body))
            (t full-body)))
          (result
           (if (not (string= session-name "none"))
diff --git a/lisp/org/ob-screen.el b/lisp/org/ob-screen.el
index 837c18f..f663c9d 100644
--- a/lisp/org/ob-screen.el
+++ b/lisp/org/ob-screen.el
@@ -62,7 +62,7 @@ In case you want to use a different screen than one selected 
by your $PATH")
          (process-name (concat "org-babel: terminal (" session ")")))
     (apply 'start-process process-name "*Messages*"
            terminal `("-T" ,(concat "org-babel: " session) "-e" 
,org-babel-screen-location
-                     "-c" "/dev/null" "-mS" ,(concat "org-babel-session-" 
session)
+                     "-c" ,null-device "-mS" ,(concat "org-babel-session-" 
session)
                      ,cmd))
     ;; XXX: Is there a better way than the following?
     (while (not (org-babel-screen-session-socketname session))
diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el
index 689d134..82bf1b2 100644
--- a/lisp/org/org-agenda.el
+++ b/lisp/org/org-agenda.el
@@ -2236,7 +2236,6 @@ The following commands are available:
   (setq mode-name "Org-Agenda")
   (setq indent-tabs-mode nil)
   (use-local-map org-agenda-mode-map)
-  (easy-menu-add org-agenda-menu)
   (when org-startup-truncated (setq truncate-lines t))
   (setq-local line-move-visual nil)
   (add-hook 'post-command-hook 'org-agenda-update-agenda-type nil 'local)
diff --git a/lisp/org/org-table.el b/lisp/org/org-table.el
index 5c37cb1..4636baa 100644
--- a/lisp/org/org-table.el
+++ b/lisp/org/org-table.el
@@ -3287,7 +3287,6 @@ Parameters get priority."
       (setq-local org-selected-window sel-win)
       (use-local-map org-table-fedit-map)
       (add-hook 'post-command-hook #'org-table-fedit-post-command t t)
-      (easy-menu-add org-table-fedit-menu)
       (setq startline (org-current-line))
       (dolist (entry eql)
        (let* ((type (cond
@@ -5129,15 +5128,13 @@ When LOCAL is non-nil, show references for the table at 
point."
                  orgtbl-line-start-regexp))
     (when (fboundp 'font-lock-add-keywords)
       (font-lock-add-keywords nil orgtbl-extra-font-lock-keywords)
-      (org-restart-font-lock))
-    (easy-menu-add orgtbl-mode-menu))
+      (org-restart-font-lock)))
    (t
     (setq auto-fill-inhibit-regexp org-old-auto-fill-inhibit-regexp)
     (remove-hook 'before-change-functions 'org-before-change-function t)
     (when (fboundp 'font-lock-remove-keywords)
       (font-lock-remove-keywords nil orgtbl-extra-font-lock-keywords)
       (org-restart-font-lock))
-    (easy-menu-remove orgtbl-mode-menu)
     (force-mode-line-update 'all))))
 
 (defun orgtbl-make-binding (fun n &rest keys)
diff --git a/lisp/org/org.el b/lisp/org/org.el
index d2a36dd..de7dded 100644
--- a/lisp/org/org.el
+++ b/lisp/org/org.el
@@ -4791,7 +4791,6 @@ This is for getting out of special buffers like capture.")
 (require 'time-date)
 (unless (fboundp 'time-subtract) (defalias 'time-subtract 'subtract-time))
 (require 'easymenu)
-(autoload 'easy-menu-add "easymenu")
 (require 'overlay)
 
 ;; (require 'org-macs) moved higher up in the file before it is first used
diff --git a/lisp/outline.el b/lisp/outline.el
index 47e6528..9b11b86 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -1121,14 +1121,19 @@ Return either 'hide-all, 'headings-only, or 'show-all."
       (setq heading-end (point))
       (outline-end-of-subtree)
       (setq end (point))
-      (setq ov-list (cl-remove-if-not
-                     (lambda (o) (eq (overlay-get o 'invisible) 'outline))
-                     (overlays-in start end)))
-      (cond ((eq ov-list nil) 'show-all)
-            ;; (eq (length ov-list) 1) wouldn’t work: what if there is
-            ;; one folded subheading?
-            ((and (eq (overlay-end (car ov-list)) end)
-                  (eq (overlay-start (car ov-list)) heading-end))
+      (setq ov-list
+            (seq-filter
+             (lambda (o)
+               (and (eq (overlay-get o 'invisible) 'outline)
+                    (save-excursion
+                      (goto-char (overlay-start o))
+                      (outline-on-heading-p t))))
+             (overlays-in start end)))
+      (cond ((null ov-list) 'show-all)
+            ((and (or (= end (point-max)
+                         (1+ (overlay-end (car ov-list))))
+                      (= (overlay-end (car ov-list)) end))
+                  (= (overlay-start (car ov-list)) heading-end))
              'hide-all)
             (t 'headings-only)))))
 
@@ -1168,20 +1173,30 @@ Return either 'hide-all, 'headings-only, or 'show-all."
 (defun outline-cycle-buffer ()
   "Cycle the whole buffer like in `outline-cycle'."
   (interactive)
-  (pcase outline--cycle-buffer-state
-    ('show-all
-     (outline-hide-sublevels 1)
-     (setq outline--cycle-buffer-state 'top-level)
-     (message "Top level headings"))
-    ('top-level
-     (outline-show-all)
-     (outline-hide-region-body (point-min) (point-max))
-     (setq outline--cycle-buffer-state 'all-heading)
-     (message "All headings"))
-    ('all-heading
-     (outline-show-all)
-     (setq outline--cycle-buffer-state 'show-all)
-     (message "Show all"))))
+  (let (has-top-level)
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (or has-top-level (eobp)))
+        (when (outline-on-heading-p t)
+          (when (= (funcall outline-level) 1)
+            (setq has-top-level t)))
+        (outline-next-heading)))
+    (cond
+     ((and (eq outline--cycle-buffer-state 'show-all)
+           has-top-level)
+      (outline-hide-sublevels 1)
+      (setq outline--cycle-buffer-state 'top-level)
+      (message "Top level headings"))
+     ((or (eq outline--cycle-buffer-state 'show-all)
+          (eq outline--cycle-buffer-state 'top-level))
+      (outline-show-all)
+      (outline-hide-region-body (point-min) (point-max))
+      (setq outline--cycle-buffer-state 'all-heading)
+      (message "All headings"))
+     (t
+      (outline-show-all)
+      (setq outline--cycle-buffer-state 'show-all)
+      (message "Show all")))))
 
 (provide 'outline)
 (provide 'noutline)
diff --git a/lisp/play/fortune.el b/lisp/play/fortune.el
index c180fd0..ce40aba 100644
--- a/lisp/play/fortune.el
+++ b/lisp/play/fortune.el
@@ -96,7 +96,7 @@ Normally you won't have a reason to change it."
   "Options to pass to the strfile program (a string)."
   :type 'string)
 
-(defcustom fortune-quiet-strfile-options "> /dev/null"
+(defcustom fortune-quiet-strfile-options (concat "> " null-device)
   "Text added to the command for running `strfile'.
 By default it discards the output produced by `strfile'.
 Set this to \"\" if you would like to see the output."
diff --git a/lisp/progmodes/antlr-mode.el b/lisp/progmodes/antlr-mode.el
index 9dacd58..dc727aa 100644
--- a/lisp/progmodes/antlr-mode.el
+++ b/lisp/progmodes/antlr-mode.el
@@ -2592,7 +2592,8 @@ the default language."
        comment-start-skip "/\\*+ *\\|// *")
   ;; various -----------------------------------------------------------------
   (set (make-local-variable 'font-lock-defaults) antlr-font-lock-defaults)
-  (easy-menu-add antlr-mode-menu)
+  (when (featurep 'xemacs)
+    (easy-menu-add antlr-mode-menu))
   (set (make-local-variable 'imenu-create-index-function)
        'antlr-imenu-create-index-function)
   (set (make-local-variable 'imenu-generic-expression) t) ; fool stupid test
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index c5201d1..5e8cf61 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -2597,7 +2597,8 @@ Key bindings:
   (setq abbrev-mode t)
   (c-init-language-vars-for 'c-mode)
   (c-common-init 'c-mode)
-  (easy-menu-add c-c-menu)
+  (when (featurep 'xemacs)
+    (easy-menu-add c-c-menu))
   (cc-imenu-init cc-imenu-c-generic-expression)
   (add-hook 'flymake-diagnostic-functions 'flymake-cc nil t)
   (c-run-mode-hooks 'c-mode-common-hook))
@@ -2688,7 +2689,8 @@ Key bindings:
   (setq abbrev-mode t)
   (c-init-language-vars-for 'c++-mode)
   (c-common-init 'c++-mode)
-  (easy-menu-add c-c++-menu)
+  (when (featurep 'xemacs)
+    (easy-menu-add c-c++-menu))
   (cc-imenu-init cc-imenu-c++-generic-expression)
   (add-hook 'flymake-diagnostic-functions 'flymake-cc nil t)
   (c-run-mode-hooks 'c-mode-common-hook))
@@ -2735,7 +2737,8 @@ Key bindings:
   (setq abbrev-mode t)
   (c-init-language-vars-for 'objc-mode)
   (c-common-init 'objc-mode)
-  (easy-menu-add c-objc-menu)
+  (when (featurep 'xemacs)
+    (easy-menu-add c-objc-menu))
   (cc-imenu-init nil 'cc-imenu-objc-function)
   (c-run-mode-hooks 'c-mode-common-hook))
 
@@ -2785,7 +2788,8 @@ Key bindings:
   (setq abbrev-mode t)
   (c-init-language-vars-for 'java-mode)
   (c-common-init 'java-mode)
-  (easy-menu-add c-java-menu)
+  (when (featurep 'xemacs)
+    (easy-menu-add c-java-menu))
   (cc-imenu-init cc-imenu-java-generic-expression)
   (c-run-mode-hooks 'c-mode-common-hook))
 
@@ -2827,7 +2831,8 @@ Key bindings:
   (c-initialize-cc-mode t)
   (c-init-language-vars-for 'idl-mode)
   (c-common-init 'idl-mode)
-  (easy-menu-add c-idl-menu)
+  (when (featurep 'xemacs)
+    (easy-menu-add c-idl-menu))
   ;;(cc-imenu-init cc-imenu-idl-generic-expression) ;TODO
   (c-run-mode-hooks 'c-mode-common-hook))
 
@@ -2872,7 +2877,8 @@ Key bindings:
   (setq        abbrev-mode t)
   (c-init-language-vars-for 'pike-mode)
   (c-common-init 'pike-mode)
-  (easy-menu-add c-pike-menu)
+  (when (featurep 'xemacs)
+    (easy-menu-add c-pike-menu))
   ;;(cc-imenu-init cc-imenu-pike-generic-expression) ;TODO
   (c-run-mode-hooks 'c-mode-common-hook))
 
diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el
index de9c9a2..787f5d5 100644
--- a/lisp/progmodes/compile.el
+++ b/lisp/progmodes/compile.el
@@ -214,10 +214,6 @@ of[ \t]+\"?\\([a-zA-Z]?:?[^\":\n]+\\)\"?:" 3 2 nil (1))
      "^\"\\([^,\" \n\t]+\\)\", line \\([0-9]+\\)\
 \\(?:[(. pos]+\\([0-9]+\\))?\\)?[:.,; (-]\\( warning:\\|[-0-9 ]*(W)\\)?" 1 2 3 
(4))
 
-    (cucumber
-     "\\(?:^cucumber\\(?: -p [^[:space:]]+\\)?\\|#\\)\
-\\(?: \\)\\([^(].*\\):\\([1-9][0-9]*\\)" 1 2)
-
     (msft
      ;; Must be before edg-1, so that MSVC's longer messages are
      ;; considered before EDG.
@@ -399,6 +395,17 @@ of[ \t]+\"?\\([a-zA-Z]?:?[^\":\n]+\\)\"?:" 3 2 nil (1))
           (regexp "[0-9][0-9][0-9]")))
      1 (2 . 4) (3 . 5) (6 . 7))
 
+    (cucumber
+     ,(rx (| (: bol
+                (| (: "cucumber" (? " -p " (+ (not space))))
+                   "     "))
+             "#")
+          " "
+          (group (not "(") (* nonl))          ; file
+          ":"
+          (group (in "1-9") (* (in "0-9"))))  ; line
+     1 2)
+
     (lcc
      "^\\(?:E\\|\\(W\\)\\), \\([^(\n]+\\)(\\([0-9]+\\),[ \t]*\\([0-9]+\\)"
      2 3 4 (1))
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index 30a80ea..dd36c68 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -1718,8 +1718,6 @@ or as help on variables `cperl-tips', `cperl-problems',
   (and (boundp 'msb-menu-cond)
        (not cperl-msb-fixed)
        (cperl-msb-fix))
-  (if (fboundp 'easy-menu-add)
-      (easy-menu-add cperl-menu))      ; A NOP in Emacs.
   (if cperl-hook-after-change
       (add-hook 'after-change-functions #'cperl-after-change-function nil t))
   ;; After hooks since fontification will break this
@@ -8158,7 +8156,7 @@ the appropriate statement modifier."
 
 (defun cperl-pod2man-build-command ()
   "Builds the entire background manpage and cleaning command."
-  (let ((command (concat pod2man-program " %s 2>/dev/null"))
+  (let ((command (concat pod2man-program " %s 2>" null-device))
         (flist (and (boundp 'Man-filter-list) Man-filter-list)))
     (while (and flist (car flist))
       (let ((pcom (car (car flist)))
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index b286208..efa7b2f 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -1224,7 +1224,7 @@ default) no filter is applied."
                       map))
       ,@(pcase-let ((`(,ind ,face ,explain)
                      (cond ((null known)
-                            '("?" mode-line "No known backends"))
+                            '("?" nil "No known backends"))
                            (some-waiting
                             `("Wait" compilation-mode-line-run
                               ,(format "Waiting for %s running backend(s)"
diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el
index 2ad66cc..4dee72c 100644
--- a/lisp/progmodes/hideshow.el
+++ b/lisp/progmodes/hideshow.el
@@ -948,7 +948,6 @@ Key bindings:
         (add-hook 'change-major-mode-hook
                   #'turn-off-hideshow
                   nil t)
-        (easy-menu-add hs-minor-mode-menu)
         (set (make-local-variable 'line-move-ignore-invisible) t)
         (add-to-invisibility-spec '(hs . t)))
     (remove-from-invisibility-spec '(hs . t))
diff --git a/lisp/progmodes/idlw-help.el b/lisp/progmodes/idlw-help.el
index 89296ff..af1a1b1 100644
--- a/lisp/progmodes/idlw-help.el
+++ b/lisp/progmodes/idlw-help.el
@@ -306,7 +306,6 @@ Jump:               [h] to function doclib header
 Here are all keybindings.
 \\{idlwave-help-mode-map}"
   (buffer-disable-undo)
-  (easy-menu-add idlwave-help-menu idlwave-help-mode-map)
   (setq truncate-lines t)
   (setq case-fold-search t)
   (setq mode-line-format
@@ -1201,16 +1200,9 @@ Useful when source code is displayed as help.  See the 
option
     (setq idlwave-help-frame
          (make-frame idlwave-help-frame-parameters))
     ;; Strip menubar (?) and toolbar from the Help frame.
-    (if (fboundp 'set-specifier)
-       (progn
-         ;; XEmacs
-         (let ((sval (cons idlwave-help-frame nil)))
-           ;; (set-specifier menubar-visible-p sval)
-           (set-specifier default-toolbar-visible-p sval)))
-      ;; Emacs
-      (modify-frame-parameters idlwave-help-frame
-                              '(;;(menu-bar-lines . 0)
-                                (tool-bar-lines . 0)))))
+    (modify-frame-parameters idlwave-help-frame
+                             '(;;(menu-bar-lines . 0)
+                               (tool-bar-lines . 0))))
   (select-frame idlwave-help-frame))
 
 (defun idlwave-help-get-help-buffer ()
diff --git a/lisp/progmodes/idlw-shell.el b/lisp/progmodes/idlw-shell.el
index 70b9459..31f1845 100644
--- a/lisp/progmodes/idlw-shell.el
+++ b/lisp/progmodes/idlw-shell.el
@@ -954,7 +954,6 @@ IDL has currently stepped.")
            nil 'local)
   (add-hook 'kill-buffer-hook 'idlwave-shell-delete-temp-files nil 'local)
   (add-hook 'kill-emacs-hook 'idlwave-shell-delete-temp-files)
-  (easy-menu-add idlwave-shell-mode-menu idlwave-shell-mode-map)
 
   ;; Set the optional comint variables
   (when idlwave-shell-comint-settings
@@ -4334,13 +4333,6 @@ Shell debugging commands are available as single key 
sequences."
 (easy-menu-define
   idlwave-shell-mode-menu idlwave-shell-mode-map "IDL shell menus"
   idlwave-shell-menu-def)
-(save-current-buffer
-  (dolist (buf (buffer-list))
-    (set-buffer buf)
-    (if (derived-mode-p 'idlwave-mode)
-        (progn
-          (easy-menu-remove idlwave-mode-debug-menu)
-          (easy-menu-add idlwave-mode-debug-menu)))))
 
 ;; The Breakpoint Glyph -------------------------------------------------------
 
diff --git a/lisp/progmodes/idlwave.el b/lisp/progmodes/idlwave.el
index 876c38d..9107086 100644
--- a/lisp/progmodes/idlwave.el
+++ b/lisp/progmodes/idlwave.el
@@ -1873,10 +1873,6 @@ The main features of this mode are
   (set (make-local-variable 'indent-tabs-mode) nil)
   (set (make-local-variable 'completion-ignore-case) t)
 
-  (when (featurep 'easymenu)
-    (easy-menu-add idlwave-mode-menu idlwave-mode-map)
-    (easy-menu-add idlwave-mode-debug-menu idlwave-mode-map))
-
   (setq abbrev-mode t)
 
   (set (make-local-variable idlwave-fill-function) 'idlwave-auto-fill)
diff --git a/lisp/progmodes/meta-mode.el b/lisp/progmodes/meta-mode.el
index 4a5d872..87c20a2 100644
--- a/lisp/progmodes/meta-mode.el
+++ b/lisp/progmodes/meta-mode.el
@@ -946,10 +946,7 @@ The environment marked is the one that contains point or 
follows point."
   (set (make-local-variable 'font-lock-defaults)
        '(meta-font-lock-keywords
          nil nil ((?_ . "w")) nil
-         (font-lock-comment-start-regexp . "%")))
-
-  ;; Activate syntax table, keymap and menu.
-  (easy-menu-add meta-mode-menu))
+         (font-lock-comment-start-regexp . "%"))))
 
 
 ;;;###autoload
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index 55a78c6..c313ad1 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -619,9 +619,7 @@ Key bindings:
   (add-hook 'before-save-hook 'octave-sync-function-file-names nil t)
   (setq-local beginning-of-defun-function 'octave-beginning-of-defun)
   (and octave-font-lock-texinfo-comment (octave-font-lock-texinfo-comment))
-  (add-hook 'eldoc-documentation-functions 'octave-eldoc-function nil t)
-
-  (easy-menu-add octave-mode-menu))
+  (add-hook 'eldoc-documentation-functions 'octave-eldoc-function nil t))
 
 
 (defcustom inferior-octave-program "octave"
diff --git a/lisp/progmodes/prolog.el b/lisp/progmodes/prolog.el
index 75e95d9..a4e677b 100644
--- a/lisp/progmodes/prolog.el
+++ b/lisp/progmodes/prolog.el
@@ -1292,8 +1292,7 @@ To find out what version of Prolog mode you are running, 
enter
   (setq-local shell-dirstack-query "pwd.")
   (setq-local compilation-error-regexp-alist
               prolog-inferior-error-regexp-alist)
-  (compilation-shell-minor-mode)
-  (prolog-inferior-menu))
+  (compilation-shell-minor-mode))
 
 (defun prolog-input-filter (str)
   (cond ((string-match "\\`\\s *\\'" str) nil) ;whitespace
@@ -3378,9 +3377,6 @@ PREFIX is the prefix of the search regexp."
 (defun prolog-menu ()
   "Add the menus for the Prolog editing buffers."
 
-  (easy-menu-add prolog-edit-menu-insert-move)
-  (easy-menu-add prolog-edit-menu-runtime)
-
   ;; Add predicate index menu
   (setq-local imenu-create-index-function
               'imenu-default-create-index-function)
@@ -3391,9 +3387,7 @@ PREFIX is the prefix of the search regexp."
 
   (if (and prolog-imenu-flag
            (< (count-lines (point-min) (point-max)) prolog-imenu-max-lines))
-      (imenu-add-to-menubar "Predicates"))
-
-  (easy-menu-add prolog-menu-help))
+      (imenu-add-to-menubar "Predicates")))
 
 (easy-menu-define
   prolog-inferior-menu-all prolog-inferior-mode-map
@@ -3436,8 +3430,8 @@ PREFIX is the prefix of the search regexp."
   "Create the menus for the Prolog inferior buffer.
 This menu is dynamically created because one may change systems during
 the life of an Emacs session."
-  (easy-menu-add prolog-inferior-menu-all)
-  (easy-menu-add prolog-menu-help))
+  (declare (obsolete nil "28.1"))
+  nil)
 
 (defun prolog-mode-version ()
   "Echo the current version of Prolog mode in the minibuffer."
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 3b24cab..b7327e7 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -2801,12 +2801,12 @@ t means to return a list of all possible completions of 
STRING.
            (not (bolp))
            ?\n)
        "exit:\n"
-       "rm $tmp* >&/dev/null" > \n)
+       "rm $tmp* >&" null-device > \n)
   (es (file-name-nondirectory (buffer-file-name))
       > "local( signals = $signals sighup sigint;" \n
       > "tmp = `{ mktemp -t " str ".XXXXXX } ) {" \n
       > "catch @ e {" \n
-      > "rm $tmp^* >[2]/dev/null" \n
+      > "rm $tmp^* >[2]" null-device \n
       "throw $e" \n
       "} {" > \n
       _ \n
@@ -2816,10 +2816,10 @@ t means to return a list of all possible completions of 
STRING.
         7 "EXIT")
   (rc (file-name-nondirectory (buffer-file-name))
       > "tmp = `{ mktemp -t " str ".XXXXXX }" \n
-      "fn sigexit { rm $tmp^* >[2]/dev/null }" \n)
+      "fn sigexit { rm $tmp^* >[2]" null-device " }" \n)
   (sh (file-name-nondirectory (buffer-file-name))
       > "TMP=`mktemp -t " str ".XXXXXX`" \n
-      "trap \"rm $TMP* 2>/dev/null\" " ?0 \n))
+      "trap \"rm $TMP* 2>" null-device "\" " ?0 \n))
 
 
 
diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el
index 6b0df2d..2209939 100644
--- a/lisp/progmodes/sql.el
+++ b/lisp/progmodes/sql.el
@@ -4186,8 +4186,9 @@ must tell Emacs.  Here's how to do that in your init file:
            (modify-syntax-entry ?\\\\ \"\\\\\" sql-mode-syntax-table)))"
   :abbrev-table sql-mode-abbrev-table
 
-  (if sql-mode-menu
-      (easy-menu-add sql-mode-menu)); XEmacs
+  (when (and (featurep 'xemacs)
+             sql-mode-menu)
+      (easy-menu-add sql-mode-menu))
 
   ;; (smie-setup sql-smie-grammar #'sql-smie-rules)
   (set (make-local-variable 'comment-start) "--")
@@ -4312,8 +4313,9 @@ you entered, right above the output it created.
   (setq mode-name
         (concat "SQLi[" (or (sql-get-product-feature sql-product :name)
                             (symbol-name sql-product)) "]"))
-  (if sql-interactive-mode-menu
-      (easy-menu-add sql-interactive-mode-menu)) ; XEmacs
+  (when (and (featurep 'xemacs)
+             sql-interactive-mode-menu)
+    (easy-menu-add sql-interactive-mode-menu))
 
   ;; Note that making KEYWORDS-ONLY nil will cause havoc if you try
   ;; SELECT 'x' FROM DUAL with SQL*Plus, because the title of the column
diff --git a/lisp/progmodes/tcl.el b/lisp/progmodes/tcl.el
index f0dd9af..ff3fb96 100644
--- a/lisp/progmodes/tcl.el
+++ b/lisp/progmodes/tcl.el
@@ -654,9 +654,7 @@ already exist."
        #'tcl-add-log-defun)
 
   (setq-local beginning-of-defun-function #'tcl-beginning-of-defun-function)
-  (setq-local end-of-defun-function #'tcl-end-of-defun-function)
-
-  (easy-menu-add tcl-mode-menu))
+  (setq-local end-of-defun-function #'tcl-end-of-defun-function))
 
 
 
diff --git a/lisp/progmodes/vhdl-mode.el b/lisp/progmodes/vhdl-mode.el
index f288fac..489092f 100644
--- a/lisp/progmodes/vhdl-mode.el
+++ b/lisp/progmodes/vhdl-mode.el
@@ -4205,9 +4205,11 @@ STRING are replaced by `-' and substrings are converted 
to lower case."
 (defun vhdl-update-mode-menu ()
   "Update VHDL Mode menu."
   (interactive)
-  (easy-menu-remove vhdl-mode-menu-list) ; for XEmacs
+  (when (featurep 'xemacs)
+    (easy-menu-remove vhdl-mode-menu-list))
   (setq vhdl-mode-menu-list (vhdl-create-mode-menu))
-  (easy-menu-add vhdl-mode-menu-list)  ; for XEmacs
+  (when (featurep 'xemacs)
+    (easy-menu-add vhdl-mode-menu-list))
   (easy-menu-define vhdl-mode-menu vhdl-mode-map
                    "Menu keymap for VHDL Mode." vhdl-mode-menu-list))
 
@@ -4313,7 +4315,8 @@ The directory of the current source file is scanned."
     (push ["*Rescan*" vhdl-add-source-files-menu t] menu-list)
     (push "Sources" menu-list)
     ;; Create menu
-    (easy-menu-add menu-list)
+    (when (featurep 'xemacs)
+      (easy-menu-add menu-list))
     (easy-menu-define vhdl-sources-menu newmap
                      "VHDL source files menu" menu-list))
   (message ""))
@@ -4926,7 +4929,8 @@ Key bindings:
   ;; add source file menu
   (if vhdl-source-file-menu (vhdl-add-source-files-menu))
   ;; add VHDL menu
-  (easy-menu-add vhdl-mode-menu-list)  ; for XEmacs
+  (when (featurep 'xemacs)
+    (easy-menu-add vhdl-mode-menu-list))
   (easy-menu-define vhdl-mode-menu vhdl-mode-map
                    "Menu keymap for VHDL Mode." vhdl-mode-menu-list)
   ;; initialize hideshow and add menu
diff --git a/lisp/server.el b/lisp/server.el
index 763f651..2fd9455 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -648,7 +648,17 @@ the `server-process' variable."
            ;; Remove any leftover socket or authentication file.
            (ignore-errors
              (let (delete-by-moving-to-trash)
-               (delete-file server-file)))
+               (delete-file server-file)
+               ;; Also delete the directory that the server file was
+               ;; created in -- but only in /tmp (see bug#44644).
+               ;; There may be other servers running, too, so this may
+               ;; fail.
+               (when (equal (file-name-directory
+                             (directory-file-name
+                              (file-name-directory server-file)))
+                            "/tmp/")
+                 (ignore-errors
+                   (delete-directory (file-name-directory server-file))))))
          (setq server-mode nil) ;; already set by the minor mode code
          (display-warning
           'server
diff --git a/lisp/shell.el b/lisp/shell.el
index 43ad587..51937cd 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -605,7 +605,7 @@ buffer."
                       (t "~/.history")))))
       (if (or (equal comint-input-ring-file-name "")
              (equal (file-truename comint-input-ring-file-name)
-                    (file-truename "/dev/null")))
+                    (file-truename null-device)))
          (setq comint-input-ring-file-name nil))
       ;; Arrange to write out the input ring on exit, if the shell doesn't
       ;; do this itself.
diff --git a/lisp/simple.el b/lisp/simple.el
index bb28145..93fda7d 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -5344,7 +5344,7 @@ Normally set from the UNDO element of a yank-handler; see 
`insert-for-yank'.")
 
 (defun yank-pop (&optional arg)
   "Replace just-yanked stretch of killed text with a different stretch.
-This command is allowed only immediately after a `yank' or a
+The main use of this command is immediately after a `yank' or a
 `yank-pop'.  At such a time, the region contains a stretch of
 reinserted previously-killed text.  `yank-pop' deletes that text
 and inserts in its place a different stretch of killed text by
@@ -5359,30 +5359,36 @@ comes the newest one.
 
 This command honors the `yank-handled-properties' and
 `yank-excluded-properties' variables, and the `yank-handler' text
-property, in the way that `yank' does."
-  (interactive "*p")
+property, in the way that `yank' does.
+
+When this command is called not immediately after a `yank' or a
+`yank-pop', then it activates the minibuffer with its completion
+and history filled with previously-killed items from the
+`kill-ring' variable, and reads a string to yank at point.
+See `yank-from-kill-ring' for more details."
+  (interactive "p")
   (if (not (eq last-command 'yank))
-      (user-error "Previous command was not a yank"))
-  (setq this-command 'yank)
-  (unless arg (setq arg 1))
-  (let ((inhibit-read-only t)
-       (before (< (point) (mark t))))
-    (if before
-       (funcall (or yank-undo-function 'delete-region) (point) (mark t))
-      (funcall (or yank-undo-function 'delete-region) (mark t) (point)))
-    (setq yank-undo-function nil)
-    (set-marker (mark-marker) (point) (current-buffer))
-    (insert-for-yank (current-kill arg))
-    ;; Set the window start back where it was in the yank command,
-    ;; if possible.
-    (set-window-start (selected-window) yank-window-start t)
-    (if before
-       ;; This is like exchange-point-and-mark, but doesn't activate the mark.
-       ;; It is cleaner to avoid activation, even though the command
-       ;; loop would deactivate the mark because we inserted text.
-       (goto-char (prog1 (mark t)
-                    (set-marker (mark-marker) (point) (current-buffer))))))
-  nil)
+      (yank-from-kill-ring (read-from-kill-ring) current-prefix-arg)
+    (setq this-command 'yank)
+    (unless arg (setq arg 1))
+    (let ((inhibit-read-only t)
+          (before (< (point) (mark t))))
+      (if before
+          (funcall (or yank-undo-function 'delete-region) (point) (mark t))
+        (funcall (or yank-undo-function 'delete-region) (mark t) (point)))
+      (setq yank-undo-function nil)
+      (set-marker (mark-marker) (point) (current-buffer))
+      (insert-for-yank (current-kill arg))
+      ;; Set the window start back where it was in the yank command,
+      ;; if possible.
+      (set-window-start (selected-window) yank-window-start t)
+      (if before
+          ;; This is like exchange-point-and-mark, but doesn't activate the 
mark.
+          ;; It is cleaner to avoid activation, even though the command
+          ;; loop would deactivate the mark because we inserted text.
+          (goto-char (prog1 (mark t)
+                       (set-marker (mark-marker) (point) (current-buffer))))))
+    nil))
 
 (defun yank (&optional arg)
   "Reinsert (\"paste\") the last stretch of killed text.
@@ -5449,6 +5455,77 @@ See also the command `yank-pop' (\\[yank-pop])."
 With ARG, rotate that many kills forward (or backward, if negative)."
   (interactive "p")
   (current-kill arg))
+
+(defvar read-from-kill-ring-history)
+(defun read-from-kill-ring ()
+  "Read a string from `kill-ring' using completion and minibuffer history."
+  (let* ((history-add-new-input nil)
+         (ellipsis (if (char-displayable-p ?…) "…" "..."))
+         ;; Remove keymaps from text properties of copied string,
+         ;; because typing RET in the minibuffer might call
+         ;; an irrelevant command from the map of copied string.
+         (read-from-kill-ring-history
+          (mapcar (lambda (s)
+                    (remove-list-of-text-properties
+                     0 (length s)
+                     '(
+                       keymap local-map action mouse-action
+                       button category help-args)
+                     s)
+                    s)
+                  kill-ring))
+         (completions
+          (mapcar (lambda (s)
+                    (let* ((s (query-replace-descr s))
+                           (b 0)
+                           (limit (frame-text-cols)))
+                      ;; Add ellipsis on leading whitespace
+                      (when (string-match "\\`[[:space:]]+" s)
+                        (setq b (match-end 0))
+                        (add-text-properties 0 b `(display ,ellipsis) s))
+                      ;; Add ellipsis at the end of a long string
+                      (when (> (length s) (+ limit b))
+                        (add-text-properties
+                         (min (+ limit b) (length s)) (length s)
+                         `(display ,ellipsis) s))
+                      s))
+                  read-from-kill-ring-history)))
+    (minibuffer-with-setup-hook
+        (lambda ()
+          ;; Allow ‘SPC’ to be self-inserting
+          (use-local-map
+           (let ((map (make-sparse-keymap)))
+             (set-keymap-parent map (current-local-map))
+             (define-key map " " nil)
+             (define-key map "?" nil)
+             map)))
+      (completing-read
+       "Yank from kill-ring: "
+       (lambda (string pred action)
+         (if (eq action 'metadata)
+             ;; Keep sorted by recency
+             '(metadata (display-sort-function . identity))
+           (complete-with-action action completions string pred)))
+       nil nil nil
+       'read-from-kill-ring-history))))
+
+(defun yank-from-kill-ring (string &optional arg)
+  "Insert the `kill-ring' item selected from the minibuffer history.
+Use minibuffer navigation and search commands to browse the
+previously-killed items from the `kill-ring' variable in the
+minibuffer history before typing RET to insert the selected item,
+or use completion on the elements of `kill-ring'.  You can edit
+the item in the minibuffer before inserting it.
+
+With \\[universal-argument] as argument, put point at beginning,
+and mark at end, like `yank' does."
+  (interactive (list (read-from-kill-ring) current-prefix-arg))
+  (push-mark)
+  (insert-for-yank string)
+  (if (consp arg)
+      ;; Swap point and mark like in `yank'.
+      (goto-char (prog1 (mark t)
+                   (set-marker (mark-marker) (point) (current-buffer))))))
 
 ;; Some kill commands.
 
@@ -8745,6 +8822,8 @@ Called from `temp-buffer-show-hook'."
             insert-fun))
       (set (make-local-variable 'completion-reference-buffer) mainbuf)
       (if base-dir (setq default-directory base-dir))
+      (when completion-tab-width
+        (setq tab-width completion-tab-width))
       ;; Maybe insert help string.
       (when completion-show-help
        (goto-char (point-min))
diff --git a/lisp/speedbar.el b/lisp/speedbar.el
index 3619b23..3468780 100644
--- a/lisp/speedbar.el
+++ b/lisp/speedbar.el
@@ -1144,6 +1144,7 @@ frame and window to be the currently active frame and 
window."
 
 (defvar speedbar-previous-menu nil
   "The menu before the last `speedbar-reconfigure-keymaps' was called.")
+(make-obsolete-variable 'speedbar-previous-menu "no longer used." "28.1")
 
 (defun speedbar-reconfigure-keymaps ()
   "Reconfigure the menu-bar in a speedbar frame.
@@ -1195,10 +1196,7 @@ and the existence of packages."
                         (speedbar-initial-keymap)
                         ;; This creates a small keymap we can glom the
                         ;; menu adjustments into.
-                        (speedbar-make-specialized-keymap)))
-      ;; Delete the old menu if applicable.
-      (if speedbar-previous-menu (easy-menu-remove speedbar-previous-menu))
-      (setq speedbar-previous-menu md)
+                         (speedbar-make-specialized-keymap)))
       ;; Now add the new menu
       (easy-menu-define speedbar-menu-map (current-local-map)
         "Speedbar menu" md))
diff --git a/lisp/subr.el b/lisp/subr.el
index f9ca50f..c07d615 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3041,7 +3041,21 @@ to `accept-change-group' or `cancel-change-group'."
   (dolist (elt handle)
     (with-current-buffer (car elt)
       (if (eq buffer-undo-list t)
-         (setq buffer-undo-list nil)))))
+         (setq buffer-undo-list nil)
+       ;; Add a boundary to make sure the upcoming changes won't be
+       ;; merged/combined with any previous changes (bug#33341).
+       ;; We're not supposed to introduce a real (visible)
+        ;; `undo-boundary', tho, so we have to push something else
+        ;; that acts like a boundary w.r.t preventing merges while
+       ;; being harmless.
+        ;; We use for that an "empty insertion", but in order to be harmless,
+        ;; it has to be at a harmless position.  Currently only
+        ;; insertions are ever merged/combined, so we use such a "boundary"
+        ;; only when the last change was an insertion and we use the position
+        ;; of the last insertion.
+        (when (numberp (caar buffer-undo-list))
+          (push (cons (caar buffer-undo-list) (caar buffer-undo-list))
+                buffer-undo-list))))))
 
 (defun accept-change-group (handle)
   "Finish a change group made with `prepare-change-group' (which see).
@@ -4551,10 +4565,9 @@ and replace a sub-expression, e.g.
        (when (= me mb) (setq me (min l (1+ mb))))
        ;; Generate a replacement for the matched substring.
        ;; Operate on only the substring to minimize string consing.
-       ;; Set up match data for the substring for replacement;
-       ;; presumably this is likely to be faster than munging the
-       ;; match data directly in Lisp.
-       (string-match regexp (setq str (substring string mb me)))
+        ;; Translate the match data so that it applies to the matched 
substring.
+        (match-data--translate (- mb))
+        (setq str (substring string mb me))
        (setq matches
              (cons (replace-match (if (stringp rep)
                                       rep
diff --git a/lisp/term.el b/lisp/term.el
index 585232b..148d7a7 100644
--- a/lisp/term.el
+++ b/lisp/term.el
@@ -1107,8 +1107,6 @@ Entry to this mode runs the hooks on `term-mode-hook'."
 
   (term--reset-scroll-region)
 
-  (easy-menu-add term-terminal-menu)
-  (easy-menu-add term-signals-menu)
   (or term-input-ring
       (setq term-input-ring (make-ring term-input-ring-size)))
   (term-update-mode-line))
@@ -1293,8 +1291,6 @@ intervention from Emacs, except for the escape character 
(usually C-c)."
   (when (term-in-line-mode)
     (setq term-old-mode-map (current-local-map))
     (use-local-map term-raw-map)
-    (easy-menu-add term-terminal-menu)
-    (easy-menu-add term-signals-menu)
 
     ;; Don't allow changes to the buffer or to point which are not
     ;; caused by the process filter.
@@ -1576,9 +1572,9 @@ Nil if unknown.")
             process-environment))
     (apply #'start-process name buffer
           "/bin/sh" "-c"
-          (format "stty -nl echo rows %d columns %d sane 2>/dev/null;\
+          (format "stty -nl echo rows %d columns %d sane 2>%s;\
 if [ $1 = .. ]; then shift; fi; exec \"$@\""
-                  term-height term-width)
+                  term-height term-width null-device)
           ".."
           command switches)))
 
@@ -3550,9 +3546,6 @@ The top-most line is line 0."
   ;;   (stop-process process))
   (setq term-pager-old-local-map (current-local-map))
   (use-local-map term-pager-break-map)
-  (easy-menu-add term-terminal-menu)
-  (easy-menu-add term-signals-menu)
-  (easy-menu-add term-pager-menu)
   (make-local-variable 'term-old-mode-line-format)
   (setq term-old-mode-line-format mode-line-format)
   (setq mode-line-format
diff --git a/lisp/textmodes/dns-mode.el b/lisp/textmodes/dns-mode.el
index 6dfea8f..712955e 100644
--- a/lisp/textmodes/dns-mode.el
+++ b/lisp/textmodes/dns-mode.el
@@ -184,8 +184,7 @@ Turning on DNS mode runs `dns-mode-hook'."
   (set (make-local-variable 'font-lock-defaults)
        '(dns-mode-font-lock-keywords nil nil ((?_ . "w"))))
   (add-hook 'before-save-hook 'dns-mode-soa-maybe-increment-serial
-           nil t)
-  (easy-menu-add dns-mode-menu dns-mode-map))
+            nil t))
 
 ;;;###autoload (defalias 'zone-mode 'dns-mode)
 
diff --git a/lisp/textmodes/reftex-index.el b/lisp/textmodes/reftex-index.el
index a1e3026..bebda48 100644
--- a/lisp/textmodes/reftex-index.el
+++ b/lisp/textmodes/reftex-index.el
@@ -408,7 +408,8 @@ Here are all local bindings.
     (make-local-hook 'post-command-hook)
     (make-local-hook 'pre-command-hook))
   (make-local-variable 'reftex-last-follow-point)
-  (easy-menu-add reftex-index-menu reftex-index-mode-map)
+  (when (featurep 'xemacs)
+    (easy-menu-add reftex-index-menu reftex-index-mode-map))
   (add-hook 'post-command-hook 'reftex-index-post-command-hook nil t)
   (add-hook 'pre-command-hook  'reftex-index-pre-command-hook nil t))
 
@@ -1386,7 +1387,8 @@ Here are all local bindings.
   :syntax-table reftex-index-phrases-syntax-table
   (set (make-local-variable 'font-lock-defaults)
        reftex-index-phrases-font-lock-defaults)
-  (easy-menu-add reftex-index-phrases-menu reftex-index-phrases-mode-map)
+  (when (featurep 'xemacs)
+    (easy-menu-add reftex-index-phrases-menu reftex-index-phrases-mode-map))
   (set (make-local-variable 'reftex-index-phrases-marker) (make-marker)))
 ;; (add-hook 'reftex-index-phrases-mode-hook 'turn-on-font-lock)
 
diff --git a/lisp/textmodes/reftex-toc.el b/lisp/textmodes/reftex-toc.el
index 02e7e3a..eb6ed2f 100644
--- a/lisp/textmodes/reftex-toc.el
+++ b/lisp/textmodes/reftex-toc.el
@@ -153,7 +153,8 @@ Here are all local bindings.
   (make-local-variable 'reftex-last-follow-point)
   (add-hook 'post-command-hook 'reftex-toc-post-command-hook nil t)
   (add-hook 'pre-command-hook  'reftex-toc-pre-command-hook nil t)
-  (easy-menu-add reftex-toc-menu reftex-toc-mode-map))
+  (when (featurep 'xemacs)
+    (easy-menu-add reftex-toc-menu reftex-toc-mode-map)))
 
 (defvar reftex-last-toc-file nil
   "Stores the file name from which `reftex-toc' was called.  For redo 
command.")
diff --git a/lisp/textmodes/reftex.el b/lisp/textmodes/reftex.el
index 4071c0d..29ebab5 100644
--- a/lisp/textmodes/reftex.el
+++ b/lisp/textmodes/reftex.el
@@ -207,7 +207,8 @@ on the menu bar.
   (if reftex-mode
       (progn
         ;; Mode was turned on
-        (easy-menu-add reftex-mode-menu)
+        (when (featurep 'xemacs)
+          (easy-menu-add reftex-mode-menu))
         (and reftex-plug-into-AUCTeX
              (reftex-plug-into-AUCTeX))
         (unless (get 'reftex-auto-view-crossref 'initialized)
@@ -224,7 +225,8 @@ on the menu bar.
 
         (run-hooks 'reftex-mode-hook))
     ;; Mode was turned off
-    (easy-menu-remove reftex-mode-menu)))
+    (when (featurep 'xemacs)
+      (easy-menu-remove reftex-mode-menu))))
 
 (defvar reftex-docstruct-symbol)
 (defun reftex-kill-buffer-hook ()
diff --git a/lisp/textmodes/table.el b/lisp/textmodes/table.el
index 065fdd0..984cc08 100644
--- a/lisp/textmodes/table.el
+++ b/lisp/textmodes/table.el
@@ -620,13 +620,6 @@
 (defvar flyspell-mode)
 (defvar real-last-command)
 (defvar delete-selection-mode)
-;; This is evil!!
-;; (eval-when-compile
-;;   (unless (fboundp 'set-face-property)
-;;     (defun set-face-property (face prop value)))
-;;   (unless (fboundp 'unibyte-char-to-multibyte)
-;;     (defun unibyte-char-to-multibyte (char)))
-;;   (defun table--point-in-cell-p (&optional location)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
@@ -811,13 +804,6 @@ simply by any key input."
 
 (setplist 'table-disable-incompatibility-warning nil)
 
-(defvar table-disable-menu (null (and (locate-library "easymenu")
-                                     (require 'easymenu)
-                                     (fboundp 'easy-menu-add-item)))
-  "When non-nil, use of menu by table package is disabled.
-It must be set before loading this package `table.el' for the first
-time.")
-
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
@@ -1202,12 +1188,11 @@ This is always set to nil at the entry to 
`table-with-cache-buffer' before execu
     ))
 
 ;; register table menu under global tools menu
-(unless table-disable-menu
-  (easy-menu-define table-global-menu-map nil
-    "Table global menu" table-global-menu)
-  (easy-menu-add-item (current-global-map) '("menu-bar" "tools") "--")
-  (easy-menu-add-item (current-global-map)
-                      '("menu-bar" "tools") table-global-menu-map))
+(easy-menu-define table-global-menu-map nil
+  "Table global menu" table-global-menu)
+(easy-menu-add-item (current-global-map) '("menu-bar" "tools") "--")
+(easy-menu-add-item (current-global-map)
+                    '("menu-bar" "tools") table-global-menu-map)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
@@ -1287,12 +1272,8 @@ the last cache point coordinate."
        ;; set up the update timer unless it is explicitly inhibited.
        (unless table-inhibit-update
         (table--update-cell)))))
-(if (null (fboundp 'font-lock-add-keywords))
-    nil
-  ;; Color it as a keyword.
-  (font-lock-add-keywords
-   'emacs-lisp-mode
-   '("\\<table-with-cache-buffer\\>")))
+;; Color it as a keyword.
+(font-lock-add-keywords 'emacs-lisp-mode '("\\<table-with-cache-buffer\\>"))
 
 (defmacro table-put-source-info (prop value)
   "Register source generation information."
@@ -1975,7 +1956,7 @@ is negative the cell becomes inactive, meaning that the 
cell becomes
 plain text and loses all the table specific features."
   (interactive "i\ni\np")
   (table--make-cell-map)
-  (if (or force (not (memq (table--get-last-command) table-command-list)))
+  (if (or force (not (memq real-last-command table-command-list)))
       (let* ((cell (table--probe-cell (called-interactively-p 'interactive)))
             (cache-buffer (get-buffer-create table-cache-buffer-name))
             (modified-flag (buffer-modified-p))
@@ -3863,9 +3844,8 @@ converts a table into plain text without frames.  It is a 
companion to
       (setq table-cell-map map)
       (fset 'table-cell-map map)))
   ;; Add menu for table cells.
-  (unless table-disable-menu
-    (easy-menu-define table-cell-menu-map table-cell-map
-      "Table cell menu" table-cell-menu))
+  (easy-menu-define table-cell-menu-map table-cell-map
+    "Table cell menu" table-cell-menu)
   (run-hooks 'table-cell-map-hook))
 
 ;; Create the keymap after running the user init file so that the user
@@ -4056,16 +4036,15 @@ key             binding
 (defun *table--present-cell-popup-menu (event)
   "Present and handle cell popup menu."
   (interactive "e")
-  (unless table-disable-menu
-    (select-window (posn-window (event-start event)))
-    (goto-char (posn-point (event-start event)))
-    (let ((item-list (x-popup-menu event table-cell-menu-map))
-         (func table-cell-menu-map))
-      (while item-list
-       (setq func (nth 3 (assoc (car item-list) func)))
-       (setq item-list (cdr item-list)))
-      (if (and (symbolp func) (fboundp func))
-         (call-interactively func)))))
+  (select-window (posn-window (event-start event)))
+  (goto-char (posn-point (event-start event)))
+  (let ((item-list (x-popup-menu event table-cell-menu-map))
+        (func table-cell-menu-map))
+    (while item-list
+      (setq func (nth 3 (assoc (car item-list) func)))
+      (setq item-list (cdr item-list)))
+    (if (and (symbolp func) (fboundp func))
+        (call-interactively func))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
@@ -4086,9 +4065,10 @@ cache buffer into the designated cell in the table 
buffer."
          (and (boundp 'quail-translating)
               quail-translating))
       (setq table-update-timer
-           (table--set-timer table-time-before-update
-                             (function table--update-cell)
-                             'now))
+            (run-with-idle-timer table-time-before-update
+                                 nil
+                                 (function table--update-cell)
+                                 'now))
     (save-current-buffer
       (set-buffer table-cell-buffer)
       (let ((cache-buffer (get-buffer-create table-cache-buffer-name))
@@ -4125,9 +4105,10 @@ cache buffer into the designated cell in the table 
buffer."
     (setq table-widen-timer nil))
   (if (not now)
       (setq table-widen-timer
-           (table--set-timer (+ table-time-before-update 
table-time-before-reformat)
-                             (function table--update-cell-widened)
-                             'now))
+            (run-with-idle-timer (+ table-time-before-update 
table-time-before-reformat)
+                                 nil
+                                 (function table--update-cell-widened)
+                                 'now))
     (save-current-buffer
       (if table-update-timer
          (table--update-cell 'now))
@@ -4164,9 +4145,10 @@ cache buffer into the designated cell in the table 
buffer."
     (setq table-heighten-timer nil))
   (if (not now)
       (setq table-heighten-timer
-           (table--set-timer (+ table-time-before-update 
table-time-before-reformat)
-                             (function table--update-cell-heightened)
-                             'now))
+            (run-with-idle-timer (+ table-time-before-update 
table-time-before-reformat)
+                                 nil
+                                 (function table--update-cell-heightened)
+                                 'now))
     (save-current-buffer
       (if table-update-timer
          (table--update-cell 'now))
@@ -4710,8 +4692,7 @@ in the list."
 
 (defun table--cell-insert-char (char &optional overwrite)
   "Insert CHAR inside a table cell."
-  (let ((delete-selection-p (and (boundp 'delete-selection-mode)
-                                delete-selection-mode
+  (let ((delete-selection-p (and delete-selection-mode
                                 transient-mark-mode mark-active
                                 (not buffer-read-only)))
        (mark-coordinate (table--transcoord-table-to-cache 
(table--get-coordinate (mark t)))))
@@ -5239,8 +5220,7 @@ This feature is disabled when 
`table-disable-incompatibility-warning'
 is non-nil.  The warning is done only once per session for each item."
   (unless (and table-disable-incompatibility-warning
               (not (called-interactively-p 'interactive)))
-    (when (and (boundp 'flyspell-mode)
-              flyspell-mode
+    (when (and flyspell-mode
               (not (get 'table-disable-incompatibility-warning 'flyspell)))
       (put 'table-disable-incompatibility-warning 'flyspell t)
       (display-warning 'table
@@ -5443,15 +5423,24 @@ It returns COLUMN unless STR contains some wide 
characters."
        idx
       nil)))
 
+
+;;;; Obsolete.
+
+(defvar table-disable-menu nil
+  "When non-nil, use of menu by table package is disabled.
+It must be set before loading this package `table.el' for the first
+time.")
+(make-obsolete-variable 'table-disable-menu "no longer used." "28.1")
+
 (defun table--set-timer (seconds func args)
   "Generic wrapper for setting up a timer."
+  (declare (obsolete run-with-idle-timer "28.1"))
   (run-with-idle-timer seconds nil func args))
 
 (defun table--get-last-command ()
   "Generic wrapper for getting the real last command."
-  (if (boundp 'real-last-command)
-      real-last-command
-    last-command))
+  (declare (obsolete real-last-command "28.1"))
+  real-last-command)
 
 (run-hooks 'table-load-hook)
 
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index 0a90613..51f4180 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -2326,7 +2326,7 @@ where DEFUN... is a list of function names found in FILE."
                       ;; would look for non-existent files like
                       ;; /dev/null.
                       (diff-find-source-location
-                       (not (equal "/dev/null"
+                       (not (equal null-device
                                    (car (diff-hunk-file-names t))))))
                      (other-buf nil)
                      (goto-otherbuf
@@ -2567,8 +2567,8 @@ fixed, visit it in a buffer."
                  (concat "diff.*\n"
                          "\\(?:\\(?:new file\\|deleted\\).*\n\\)?"
                          "\\(?:index.*\n\\)?"
-                         "--- \\(?:/dev/null\\|a/\\(.*\\)\\)\n"
-                         "\\+\\+\\+ \\(?:/dev/null\\|b/\\(.*\\)\\)\n"))))
+                         "--- \\(?:" null-device "\\|a/\\(.*\\)\\)\n"
+                         "\\+\\+\\+ \\(?:" null-device "\\|b/\\(.*\\)\\)\n"))))
         (put-text-property (match-beginning 0)
                            (or (match-beginning 2) (match-beginning 1))
                            'display (propertize
diff --git a/lisp/vc/ediff-mult.el b/lisp/vc/ediff-mult.el
index c977291..b483778 100644
--- a/lisp/vc/ediff-mult.el
+++ b/lisp/vc/ediff-mult.el
@@ -2317,7 +2317,7 @@ If this is a session registry buffer then just bury it."
         (meta-patchbuf ediff-meta-patchbufer)
         session-buf beg-marker end-marker)
 
-    (if (or (file-directory-p file) (string-match "/dev/null" file))
+    (if (or (file-directory-p file) (string-match null-device file))
        (user-error "`%s' is not an ordinary file" (file-name-as-directory 
file)))
     (setq session-buf (ediff-get-session-buffer info)
          beg-marker (ediff-get-session-objB-name info)
diff --git a/lisp/vc/ediff-ptch.el b/lisp/vc/ediff-ptch.el
index f6af5a4..08640fc 100644
--- a/lisp/vc/ediff-ptch.el
+++ b/lisp/vc/ediff-ptch.el
@@ -193,7 +193,7 @@ program."
     (let ((count 0)
          (mark1 (point-min-marker))
          (mark1-end (point-min))
-         (possible-file-names '("/dev/null" . "/dev/null"))
+         (possible-file-names `(,null-device . ,null-device))
          mark2-end mark2 filenames
          beg1 beg2 end1 end2
          patch-map opoint)
@@ -217,10 +217,10 @@ program."
            (setq possible-file-names
                  (cons (if (and beg1 end1)
                            (buffer-substring beg1 end1)
-                         "/dev/null")
+                         null-device)
                        (if (and beg2 end2)
                            (buffer-substring beg2 end2)
-                         "/dev/null")))
+                         null-device)))
             ;; Remove file junk (Bug#26084).
             (while (re-search-backward
                     (concat "^\\(?:" diff-file-junk-re "\\)") mark1-end t)
@@ -309,12 +309,12 @@ program."
                                   (file-exists-p (cdr m2)))
                          (setq base-dir1 (car m1)
                                base-dir2 (car m2))))))))
-             (or (string= (car proposed-file-names) "/dev/null")
+             (or (string= (car proposed-file-names) null-device)
                  (setcar proposed-file-names
                          (ediff-file-name-sans-prefix
                           (car proposed-file-names) base-dir1)))
              (or (string=
-                  (cdr proposed-file-names) "/dev/null")
+                  (cdr proposed-file-names) null-device)
                  (setcdr proposed-file-names
                          (ediff-file-name-sans-prefix
                           (cdr proposed-file-names) base-dir2)))
@@ -323,7 +323,7 @@ program."
 
     ;; take the given file name into account
     (or (file-directory-p filename)
-       (string= "/dev/null" filename)
+       (string= null-device filename)
        (setcar (ediff-get-session-objA (car ediff-patch-map))
                (cons (file-name-nondirectory filename)
                      (file-name-nondirectory filename))))
@@ -582,7 +582,7 @@ optional argument, then use it."
         patch-buf
         (if (and ediff-patch-map
                  (not (string-match-p
-                       "^/dev/null"
+                       (concat "^" null-device)
                        ;; this is the file to patch
                        (ediff-get-session-objA-name (car ediff-patch-map))))
                  (> (length
diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el
index 56ecc64..e7b6eea 100644
--- a/lisp/vc/log-view.el
+++ b/lisp/vc/log-view.el
@@ -208,15 +208,7 @@ If it is nil, `log-view-toggle-entry-display' does 
nothing.")
   "Face for the message header line in `log-view-mode'."
   :group 'log-view)
 
-(defface log-view-commit-body
-  '((((class color) (min-colors 88) (background light))
-     :background "gray95" :foreground "black" :extend t)
-    (((class color) (min-colors 88) (background dark))
-     :background "gray5" :foreground "white" :extend t)
-    (((class color) (min-colors 8) (background light))
-     :foreground "black")
-    (((class color) (min-colors 8) (background dark))
-     :foreground "white"))
+(defface log-view-commit-body '((t :inherit font-lock-comment-face))
   "Face for the commit body in `log-view-mode'."
   :version "28.1")
 
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 83f2596..1a24611 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -1774,7 +1774,7 @@ Return t if the buffer had changes, nil otherwise."
               ;; Diff it against /dev/null.
               (apply #'vc-do-command buffer
                      (if async 'async 1) "diff" file
-                     (append (vc-switches nil 'diff) '("/dev/null"))))))
+                     (append (vc-switches nil 'diff) `(,(null-device)))))))
         (setq files (nreverse filtered))))
     (vc-call-backend (car vc-fileset) 'diff files rev1 rev2 buffer async)
     (set-buffer buffer)
diff --git a/lisp/wdired.el b/lisp/wdired.el
index bb32da3..ebe1961 100644
--- a/lisp/wdired.el
+++ b/lisp/wdired.el
@@ -763,7 +763,7 @@ If OLD, return the old target.  If MOVE, move point before 
it."
       (unless (equal link-to-new link-to-ori)
         (setq changes t)
         (if (equal link-to-new "") ;empty filename!
-            (setq link-to-new "/dev/null"))
+            (setq link-to-new (null-device)))
         (condition-case err
             (progn
               (delete-file link-from)
diff --git a/lisp/wid-browse.el b/lisp/wid-browse.el
index 53f918c..f5c3d48 100644
--- a/lisp/wid-browse.el
+++ b/lisp/wid-browse.el
@@ -77,8 +77,6 @@ if that value is non-nil."
   (setq major-mode 'widget-browse-mode
        mode-name "Widget")
   (use-local-map widget-browse-mode-map)
-  (easy-menu-add widget-browse-mode-customize-menu)
-  (easy-menu-add widget-browse-mode-menu)
   (run-mode-hooks 'widget-browse-mode-hook))
 
 (put 'widget-browse-mode 'mode-class 'special)
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 4e2cf74..8250316 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -591,9 +591,25 @@ Otherwise, just return the value."
                          (widget-put widget :args args)))
                      (widget-apply widget :default-get)))))
 
+(defun widget-inline-p (widget &optional bubblep)
+  "Non-nil if the widget WIDGET is inline.
+
+With BUBBLEP non-nil, check also if WIDGET has a member that bubbles its inline
+property (if any), up to WIDGET, so that WIDGET can act as an inline widget."
+  (or (widget-get widget :inline)
+      (and bubblep
+           (widget-get widget :inline-bubbles-p)
+           (widget-apply widget :inline-bubbles-p))))
+
 (defun widget-match-inline (widget vals)
-  "In WIDGET, match the start of VALS."
-  (cond ((widget-get widget :inline)
+  "In WIDGET, match the start of VALS.
+
+For an inline widget or for a widget that acts like one (see 
`widget-inline-p'),
+try to match elements in VALS as far as possible.  Otherwise, match the first
+element of the list VALS.
+
+Return a list whose car contains all members of VALS that matched WIDGET."
+  (cond ((widget-inline-p widget t)
         (widget-apply widget :match-inline vals))
        ((and (listp vals)
              (widget-apply widget :match (car vals)))
@@ -2198,7 +2214,7 @@ But if NO-TRUNCATE is non-nil, include them."
   (let ((value (widget-get widget :value))
        (args (widget-get widget :args))
        (explicit (widget-get widget :explicit-choice))
-       current)
+        current val inline-p fun)
     (if explicit
        (progn
          ;; If the user specified the choice for this value,
@@ -2207,15 +2223,24 @@ But if NO-TRUNCATE is non-nil, include them."
                                              widget explicit value)))
          (widget-put widget :choice explicit)
          (widget-put widget :explicit-choice nil))
+      (setq inline-p (widget-inline-p widget t))
       (while args
        (setq current (car args)
              args (cdr args))
-       (when (widget-apply current :match value)
-         (widget-put widget :children (list (widget-create-child-value
-                                             widget current value)))
-         (widget-put widget :choice current)
-         (setq args nil
-               current nil)))
+        (if inline-p
+            (if (widget-get current :inline)
+                (setq val value
+                      fun :match-inline)
+              (setq val (car value)
+                    fun :match))
+          (setq val value
+                fun :match))
+        (when (widget-apply current fun val)
+          (widget-put widget :children (list (widget-create-child-value
+                                              widget current val)))
+          (widget-put widget :choice current)
+          (setq args nil
+                current nil)))
       (when current
        (let ((void (widget-get widget :void)))
          (widget-put widget :children (list (widget-create-child-and-convert
@@ -2438,7 +2463,7 @@ If the item is checked, CHOSEN is a cons whose cdr is the 
value."
                             (let ((child (widget-create-child widget type)))
                               (widget-apply child :deactivate)
                               child))
-                           ((widget-get type :inline)
+                            ((widget-inline-p type t)
                             (widget-create-child-value
                              widget type (cdr chosen)))
                            (t
@@ -2795,7 +2820,7 @@ Return an alist of (TYPE MATCH)."
        (if answer
            (setq children (cons (widget-editable-list-entry-create
                                  widget
-                                 (if (widget-get type :inline)
+                                  (if (widget-inline-p type t)
                                      (car answer)
                                    (car (car answer)))
                                  t)
@@ -2979,7 +3004,7 @@ Save CHILD into the :last-deleted list, so it can be 
inserted later."
           (insert-char ?\s (widget-get widget :indent)))
       (push (cond ((null answer)
                   (widget-create-child widget arg))
-                 ((widget-get arg :inline)
+                  ((widget-inline-p arg t)
                   (widget-create-child-value widget arg (car answer)))
                  (t
                   (widget-create-child-value widget arg (car (car answer)))))
@@ -3900,12 +3925,17 @@ example:
     `(cons :format "Key: %v" ,key-type ,value-type)))
 
 (define-widget 'choice 'menu-choice
-  "A union of several sexp types."
+  "A union of several sexp types.
+
+If one of the choices of a choice widget has an :inline t property,
+then the choice widget can act as an inline widget on its own if the
+current choice is inline."
   :tag "Choice"
   :format "%{%t%}: %[Value Menu%] %v"
   :button-prefix 'widget-push-button-prefix
   :button-suffix 'widget-push-button-suffix
-  :prompt-value 'widget-choice-prompt-value)
+  :prompt-value 'widget-choice-prompt-value
+  :inline-bubbles-p #'widget-choice-inline-bubbles-p)
 
 (defun widget-choice-prompt-value (widget prompt value _unbound)
   "Make a choice."
@@ -3948,6 +3978,20 @@ example:
     (if current
        (widget-prompt-value current prompt nil t)
       value)))
+
+(defun widget-choice-inline-bubbles-p (widget)
+  "Non-nil if the choice WIDGET has at least one choice that is inline.
+This is used when matching values, because a choice widget needs to
+match a value inline rather than just match it if at least one of its choices
+is inline."
+  (let ((args (widget-get widget :args))
+        cur found)
+    (while (and args (not found))
+      (setq cur (car args)
+            args (cdr args)
+            found (widget-get cur :inline)))
+    found))
+
 
 (define-widget 'radio 'radio-button-choice
   "A union of several sexp types."
diff --git a/src/Makefile.in b/src/Makefile.in
index 001f0c4..bd38b01 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -341,6 +341,10 @@ DUMPING=@DUMPING@
 CHECK_STRUCTS = @CHECK_STRUCTS@
 HAVE_PDUMPER = @HAVE_PDUMPER@
 
+## ARM Macs require that all code have a valid signature.  Since pump
+## invalidates the signature, we must re-sign to fix it.
+DO_CODESIGN=$(patsubst aarch64-apple-darwin%,yes,@configuration@)
+
 # 'make' verbosity.
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 
@@ -659,6 +663,9 @@ temacs$(EXEEXT): $(LIBXMENU) $(ALLOBJS) $(LIBEGNU_ARCHIVE) 
$(EMACSRES) \
          $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(W32_RES_LINK) $(LIBES)
 ifeq ($(HAVE_PDUMPER),yes)
        $(AM_V_at)$(MAKE_PDUMPER_FINGERPRINT) $@.tmp
+ifeq ($(DO_CODESIGN),yes)
+       codesign -s - -f $@.tmp
+endif
 endif
        $(AM_V_at)mv $@.tmp $@
        $(MKDIR_P) $(etc)
diff --git a/src/alloc.c b/src/alloc.c
index ff6681c..133b0e6 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6082,10 +6082,6 @@ garbage_collect (void)
   mark_fringe_data ();
 #endif
 
-#ifdef HAVE_MODULES
-  mark_modules ();
-#endif
-
   /* Everything is now marked, except for the data in font caches,
      undo lists, and finalizers.  The first two are compacted by
      removing an items which aren't reachable otherwise.  */
@@ -6179,10 +6175,17 @@ where each entry has the form (NAME SIZE USED FREE), 
where:
 - FREE is the number of those objects that are not live but that Emacs
   keeps around for future allocations (maybe because it does not know how
   to return them to the OS).
+
 However, if there was overflow in pure space, and Emacs was dumped
 using the 'unexec' method, `garbage-collect' returns nil, because
 real GC can't be done.
-See Info node `(elisp)Garbage Collection'.  */)
+
+Note that calling this function does not guarantee that absolutely all
+unreachable objects will be garbage-collected.  Emacs uses a
+mark-and-sweep garbage collector, but is conservative when it comes to
+collecting objects in some circumstances.
+
+For further details, see Info node `(elisp)Garbage Collection'.  */)
   (void)
 {
   if (garbage_collection_inhibited)
diff --git a/src/emacs-module.c b/src/emacs-module.c
index 23b8e86..5f97815 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -200,8 +200,6 @@ static AVOID module_abort (const char *, ...) 
ATTRIBUTE_FORMAT_PRINTF (1, 2);
 static emacs_env *initialize_environment (emacs_env *,
                                          struct emacs_env_private *);
 static void finalize_environment (emacs_env *);
-static void finalize_environment_unwind (void *);
-static void finalize_runtime_unwind (void *);
 static void module_handle_nonlocal_exit (emacs_env *, enum nonlocal_exit,
                                          Lisp_Object);
 static void module_non_local_exit_signal_1 (emacs_env *,
@@ -1089,10 +1087,6 @@ module_signal_or_throw (struct emacs_env_private *env)
     }
 }
 
-/* Live runtime and environment objects, for assertions.  */
-static Lisp_Object Vmodule_runtimes;
-static Lisp_Object Vmodule_environments;
-
 DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0,
        doc: /* Load module FILE.  */)
   (Lisp_Object file)
@@ -1137,9 +1131,9 @@ DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0,
   rt->private_members = &rt_priv;
   rt->get_environment = module_get_environment;
 
-  Vmodule_runtimes = Fcons (make_mint_ptr (rt), Vmodule_runtimes);
   ptrdiff_t count = SPECPDL_INDEX ();
-  record_unwind_protect_ptr (finalize_runtime_unwind, rt);
+  record_unwind_protect_module (SPECPDL_MODULE_RUNTIME, rt);
+  record_unwind_protect_module (SPECPDL_MODULE_ENVIRONMENT, rt_priv.env);
 
   int r = module_init (rt);
 
@@ -1167,7 +1161,7 @@ funcall_module (Lisp_Object function, ptrdiff_t nargs, 
Lisp_Object *arglist)
   struct emacs_env_private priv;
   emacs_env *env = initialize_environment (&pub, &priv);
   ptrdiff_t count = SPECPDL_INDEX ();
-  record_unwind_protect_ptr (finalize_environment_unwind, env);
+  record_unwind_protect_module (SPECPDL_MODULE_ENVIRONMENT, env);
 
   USE_SAFE_ALLOCA;
   emacs_value *args = nargs > 0 ? SAFE_ALLOCA (nargs * sizeof *args) : NULL;
@@ -1243,12 +1237,13 @@ module_assert_runtime (struct emacs_runtime *runtime)
   if (! module_assertions)
     return;
   ptrdiff_t count = 0;
-  for (Lisp_Object tail = Vmodule_runtimes; CONSP (tail); tail = XCDR (tail))
-    {
-      if (xmint_pointer (XCAR (tail)) == runtime)
-        return;
-      ++count;
-    }
+  for (const union specbinding *pdl = specpdl; pdl != specpdl_ptr; ++pdl)
+    if (pdl->kind == SPECPDL_MODULE_RUNTIME)
+      {
+        if (pdl->unwind_ptr.arg == runtime)
+          return;
+        ++count;
+      }
   module_abort ("Runtime pointer not found in list of %"pD"d runtimes",
                count);
 }
@@ -1259,13 +1254,13 @@ module_assert_env (emacs_env *env)
   if (! module_assertions)
     return;
   ptrdiff_t count = 0;
-  for (Lisp_Object tail = Vmodule_environments; CONSP (tail);
-       tail = XCDR (tail))
-    {
-      if (xmint_pointer (XCAR (tail)) == env)
-        return;
-      ++count;
-    }
+  for (const union specbinding *pdl = specpdl; pdl != specpdl_ptr; ++pdl)
+    if (pdl->kind == SPECPDL_MODULE_ENVIRONMENT)
+      {
+        if (pdl->unwind_ptr.arg == env)
+          return;
+        ++count;
+      }
   module_abort ("Environment pointer not found in list of %"pD"d environments",
                 count);
 }
@@ -1323,22 +1318,22 @@ value_to_lisp (emacs_value v)
          environments.  */
       ptrdiff_t num_environments = 0;
       ptrdiff_t num_values = 0;
-      for (Lisp_Object environments = Vmodule_environments;
-           CONSP (environments); environments = XCDR (environments))
-        {
-          emacs_env *env = xmint_pointer (XCAR (environments));
-          struct emacs_env_private *priv = env->private_members;
-          /* The value might be one of the nonlocal exit values.  Note
-             that we don't check whether a nonlocal exit is currently
-             pending, because the module might have cleared the flag
-             in the meantime.  */
-          if (&priv->non_local_exit_symbol == v
-              || &priv->non_local_exit_data == v)
-            goto ok;
-          if (value_storage_contains_p (&priv->storage, v, &num_values))
-            goto ok;
-          ++num_environments;
-        }
+      for (const union specbinding *pdl = specpdl; pdl != specpdl_ptr; ++pdl)
+        if (pdl->kind == SPECPDL_MODULE_ENVIRONMENT)
+          {
+            const emacs_env *env = pdl->unwind_ptr.arg;
+            struct emacs_env_private *priv = env->private_members;
+            /* The value might be one of the nonlocal exit values.  Note
+               that we don't check whether a nonlocal exit is currently
+               pending, because the module might have cleared the flag
+               in the meantime.  */
+            if (&priv->non_local_exit_symbol == v
+                || &priv->non_local_exit_data == v)
+              goto ok;
+            if (value_storage_contains_p (&priv->storage, v, &num_values))
+              goto ok;
+            ++num_environments;
+          }
       /* Also check global values.  */
       if (module_global_reference_p (v, &num_values))
         goto ok;
@@ -1421,18 +1416,14 @@ allocate_emacs_value (emacs_env *env, Lisp_Object obj)
 /* Mark all objects allocated from local environments so that they
    don't get garbage-collected.  */
 void
-mark_modules (void)
+mark_module_environment (void *ptr)
 {
-  for (Lisp_Object tem = Vmodule_environments; CONSP (tem); tem = XCDR (tem))
-    {
-      emacs_env *env = xmint_pointer (XCAR (tem));
-      struct emacs_env_private *priv = env->private_members;
-      for (struct emacs_value_frame *frame = &priv->storage.initial;
-          frame != NULL;
-          frame = frame->next)
-        for (int i = 0; i < frame->offset; ++i)
-          mark_object (frame->objects[i].v);
-    }
+  emacs_env *env = ptr;
+  struct emacs_env_private *priv = env->private_members;
+  for (struct emacs_value_frame *frame = &priv->storage.initial; frame != NULL;
+       frame = frame->next)
+    for (int i = 0; i < frame->offset; ++i)
+      mark_object (frame->objects[i].v);
 }
 
 
@@ -1495,7 +1486,6 @@ initialize_environment (emacs_env *env, struct 
emacs_env_private *priv)
   env->set_function_finalizer = module_set_function_finalizer;
   env->open_channel = module_open_channel;
   env->make_interactive = module_make_interactive;
-  Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments);
   return env;
 }
 
@@ -1505,22 +1495,18 @@ static void
 finalize_environment (emacs_env *env)
 {
   finalize_storage (&env->private_members->storage);
-  eassert (xmint_pointer (XCAR (Vmodule_environments)) == env);
-  Vmodule_environments = XCDR (Vmodule_environments);
 }
 
-static void
+void
 finalize_environment_unwind (void *env)
 {
   finalize_environment (env);
 }
 
-static void
+void
 finalize_runtime_unwind (void *raw_ert)
 {
   struct emacs_runtime *ert = raw_ert;
-  eassert (xmint_pointer (XCAR (Vmodule_runtimes)) == ert);
-  Vmodule_runtimes = XCDR (Vmodule_runtimes);
   finalize_environment (ert->private_members->env);
 }
 
@@ -1610,12 +1596,6 @@ syms_of_module (void)
                       DEFAULT_REHASH_SIZE, DEFAULT_REHASH_THRESHOLD,
                       Qnil, false);
 
-  staticpro (&Vmodule_runtimes);
-  Vmodule_runtimes = Qnil;
-
-  staticpro (&Vmodule_environments);
-  Vmodule_environments = Qnil;
-
   DEFSYM (Qmodule_load_failed, "module-load-failed");
   Fput (Qmodule_load_failed, Qerror_conditions,
        pure_list (Qmodule_load_failed, Qerror));
diff --git a/src/eval.c b/src/eval.c
index 09e12bd..aa1775b 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -690,6 +690,10 @@ default_toplevel_binding (Lisp_Object symbol)
        case SPECPDL_UNWIND_EXCURSION:
        case SPECPDL_UNWIND_VOID:
        case SPECPDL_BACKTRACE:
+#ifdef HAVE_MODULES
+        case SPECPDL_MODULE_RUNTIME:
+        case SPECPDL_MODULE_ENVIRONMENT:
+#endif
        case SPECPDL_LET_LOCAL:
          break;
 
@@ -700,6 +704,49 @@ default_toplevel_binding (Lisp_Object symbol)
   return binding;
 }
 
+/* Look for a lexical-binding of SYMBOL somewhere up the stack.
+   This will only find bindings created with interpreted code, since once
+   compiled names of lexical variables are basically gone anyway.  */
+static bool
+lexbound_p (Lisp_Object symbol)
+{
+  union specbinding *pdl = specpdl_ptr;
+  while (pdl > specpdl)
+    {
+      switch ((--pdl)->kind)
+       {
+       case SPECPDL_LET_DEFAULT:
+       case SPECPDL_LET:
+         if (EQ (specpdl_symbol (pdl), Qinternal_interpreter_environment))
+           {
+             Lisp_Object env = specpdl_old_value (pdl);
+             if (CONSP (env) && !NILP (Fassq (symbol, env)))
+               return true;
+           }
+         break;
+
+       case SPECPDL_UNWIND:
+       case SPECPDL_UNWIND_ARRAY:
+       case SPECPDL_UNWIND_PTR:
+       case SPECPDL_UNWIND_INT:
+       case SPECPDL_UNWIND_INTMAX:
+       case SPECPDL_UNWIND_EXCURSION:
+       case SPECPDL_UNWIND_VOID:
+       case SPECPDL_BACKTRACE:
+#ifdef HAVE_MODULES
+        case SPECPDL_MODULE_RUNTIME:
+        case SPECPDL_MODULE_ENVIRONMENT:
+#endif
+       case SPECPDL_LET_LOCAL:
+         break;
+
+       default:
+         emacs_abort ();
+       }
+    }
+  return false;
+}
+
 DEFUN ("default-toplevel-value", Fdefault_toplevel_value, 
Sdefault_toplevel_value, 1, 1, 0,
        doc: /* Return SYMBOL's toplevel default value.
 "Toplevel" means outside of any let binding.  */)
@@ -735,6 +782,15 @@ This is like `defvar' and `defconst' but without affecting 
the variable's
 value.  */)
   (Lisp_Object symbol, Lisp_Object doc)
 {
+  if (!XSYMBOL (symbol)->u.s.declared_special
+      && lexbound_p (symbol))
+    /* This test tries to catch the situation where we do
+       (let ((<foo-var> ...)) ...(<foo-function> ...)....)
+       and where the `foo` package only gets loaded when <foo-function>
+       is called, so the outer `let` incorrectly made the binding lexical
+       because the <foo-var> wasn't yet declared as dynamic at that point.  */
+    error ("Defining as dynamic an already lexical var");
+
   XSYMBOL (symbol)->u.s.declared_special = true;
   if (!NILP (doc))
     {
@@ -3512,6 +3568,15 @@ record_unwind_protect_void (void (*function) (void))
 }
 
 void
+record_unwind_protect_module (enum specbind_tag kind, void *ptr)
+{
+  specpdl_ptr->kind = kind;
+  specpdl_ptr->unwind_ptr.func = NULL;
+  specpdl_ptr->unwind_ptr.arg = ptr;
+  grow_specpdl ();
+}
+
+void
 rebind_for_thread_switch (void)
 {
   union specbinding *bind;
@@ -3561,6 +3626,14 @@ do_one_unbind (union specbinding *this_binding, bool 
unwinding,
       break;
     case SPECPDL_BACKTRACE:
       break;
+#ifdef HAVE_MODULES
+    case SPECPDL_MODULE_RUNTIME:
+      finalize_runtime_unwind (this_binding->unwind_ptr.arg);
+      break;
+    case SPECPDL_MODULE_ENVIRONMENT:
+      finalize_environment_unwind (this_binding->unwind_ptr.arg);
+      break;
+#endif
     case SPECPDL_LET:
       { /* If variable has a trivial value (no forwarding), and isn't
           trapped, we can just set it.  */
@@ -3891,6 +3964,10 @@ backtrace_eval_unrewind (int distance)
        case SPECPDL_UNWIND_INTMAX:
        case SPECPDL_UNWIND_VOID:
        case SPECPDL_BACKTRACE:
+#ifdef HAVE_MODULES
+        case SPECPDL_MODULE_RUNTIME:
+        case SPECPDL_MODULE_ENVIRONMENT:
+#endif
          break;
        case SPECPDL_LET:
          { /* If variable has a trivial value (no forwarding), we can
@@ -4026,6 +4103,10 @@ NFRAMES and BASE specify the activation frame to use, as 
in `backtrace-frame'.
          case SPECPDL_UNWIND_EXCURSION:
          case SPECPDL_UNWIND_VOID:
          case SPECPDL_BACKTRACE:
+#ifdef HAVE_MODULES
+          case SPECPDL_MODULE_RUNTIME:
+          case SPECPDL_MODULE_ENVIRONMENT:
+#endif
            break;
 
          default:
@@ -4072,6 +4153,14 @@ mark_specpdl (union specbinding *first, union 
specbinding *ptr)
          }
          break;
 
+#ifdef HAVE_MODULES
+        case SPECPDL_MODULE_RUNTIME:
+          break;
+        case SPECPDL_MODULE_ENVIRONMENT:
+          mark_module_environment (pdl->unwind_ptr.arg);
+          break;
+#endif
+
        case SPECPDL_LET_DEFAULT:
        case SPECPDL_LET_LOCAL:
          mark_object (specpdl_where (pdl));
diff --git a/src/font.c b/src/font.c
index 8dbf8cb..5f9db2e 100644
--- a/src/font.c
+++ b/src/font.c
@@ -3945,6 +3945,23 @@ VALUE must be a non-negative integer or a floating point 
number
 specifying the font size.  It specifies the font size in pixels (if
 VALUE is an integer), or in points (if VALUE is a float).
 
+`:dpi'
+
+VALUE must be a non-negative number that specifies the resolution
+(dot per inch) for which the font is designed.
+
+`:spacing'
+
+VALUE specifies the spacing of the font: mono, proportional, charcell,
+or dual.  It can be either a number (0 for proportional, 90 for dual,
+100 for mono, 110 for charcell) or a 1-letter symbol: `P', `D', `M',
+or `C' (lower-case variants are also accepted).
+
+`:avgwidth'
+
+VALUE must be a non-negative integer specifying the average width of
+the font in 1/10 pixel units.
+
 `:name'
 
 VALUE must be a string of XLFD-style or fontconfig-style font name.
diff --git a/src/frame.c b/src/frame.c
index 512aaf5..17ec455 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -1426,11 +1426,15 @@ do_switch_frame (Lisp_Object frame, int track, int 
for_deletion, Lisp_Object nor
       if (FRAMEP (gfocus))
        {
          focus = FRAME_FOCUS_FRAME (XFRAME (gfocus));
-         if ((FRAMEP (focus) && XFRAME (focus) == SELECTED_FRAME ())
+         if (FRAMEP (focus) && XFRAME (focus) == SELECTED_FRAME ())
              /* Redirect frame focus also when FRAME has its minibuffer
-                window on the selected frame (see Bug#24500).  */
+                window on the selected frame (see Bug#24500).
+
+                Don't do that: It causes redirection problem with a
+                separate minibuffer frame (Bug#24803) and problems
+                when updating the cursor on such frames.
              || (NILP (focus)
-                 && EQ (FRAME_MINIBUF_WINDOW (f), sf->selected_window)))
+                 && EQ (FRAME_MINIBUF_WINDOW (f), sf->selected_window)))  */
            Fredirect_frame_focus (gfocus, frame);
        }
     }
@@ -3630,7 +3634,11 @@ DEFUN ("frame-position", Fframe_position,
 FRAME must be a live frame and defaults to the selected one.  The return
 value is a cons (x, y) of the coordinates of the top left corner of
 FRAME's outer frame, in pixels relative to an origin (0, 0) of FRAME's
-display.  */)
+display.
+
+Note that the values returned are not guaranteed to be accurate: The
+values depend on the underlying window system, and some systems add a
+constant offset to the values.  */)
      (Lisp_Object frame)
 {
   register struct frame *f = decode_live_frame (frame);
diff --git a/src/lisp.h b/src/lisp.h
index b956f39..79de5d9 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3141,9 +3141,13 @@ enum specbind_tag {
   SPECPDL_UNWIND_PTR,          /* Likewise, on void *.  */
   SPECPDL_UNWIND_INT,          /* Likewise, on int.  */
   SPECPDL_UNWIND_INTMAX,       /* Likewise, on intmax_t.  */
-  SPECPDL_UNWIND_EXCURSION,    /* Likewise, on an execursion.  */
+  SPECPDL_UNWIND_EXCURSION,    /* Likewise, on an excursion.  */
   SPECPDL_UNWIND_VOID,         /* Likewise, with no arg.  */
   SPECPDL_BACKTRACE,           /* An element of the backtrace.  */
+#ifdef HAVE_MODULES
+  SPECPDL_MODULE_RUNTIME,       /* A live module runtime.  */
+  SPECPDL_MODULE_ENVIRONMENT,   /* A live module environment.  */
+#endif
   SPECPDL_LET,                 /* A plain and simple dynamic let-binding.  */
   /* Tags greater than SPECPDL_LET must be "subkinds" of LET.  */
   SPECPDL_LET_LOCAL,           /* A buffer-local let-binding.  */
@@ -4166,6 +4170,7 @@ extern void record_unwind_protect_intmax (void (*) 
(intmax_t), intmax_t);
 extern void record_unwind_protect_void (void (*) (void));
 extern void record_unwind_protect_excursion (void);
 extern void record_unwind_protect_nothing (void);
+extern void record_unwind_protect_module (enum specbind_tag, void *);
 extern void clear_unwind_protect (ptrdiff_t);
 extern void set_unwind_protect (ptrdiff_t, void (*) (Lisp_Object), 
Lisp_Object);
 extern void set_unwind_protect_ptr (ptrdiff_t, void (*) (void *), void *);
@@ -4236,7 +4241,9 @@ extern module_funcptr module_function_address
   (struct Lisp_Module_Function const *);
 extern void *module_function_data (const struct Lisp_Module_Function *);
 extern void module_finalize_function (const struct Lisp_Module_Function *);
-extern void mark_modules (void);
+extern void mark_module_environment (void *);
+extern void finalize_runtime_unwind (void *);
+extern void finalize_environment_unwind (void *);
 extern void init_module_assertions (bool);
 extern void syms_of_module (void);
 #endif
diff --git a/src/minibuf.c b/src/minibuf.c
index 464e301..fc3fd92 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -508,7 +508,10 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, 
Lisp_Object prompt,
   mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window));
   if (!EQ (mini_frame, selected_frame))
     record_unwind_protect (restore_window_configuration,
-                          Fcons (Qt,
+                          Fcons (/* Arrange for the frame later to be
+                                     switched back to the calling
+                                     frame. */
+                                  Qnil,
                                   Fcurrent_window_configuration (mini_frame)));
 
   /* If the minibuffer is on an iconified or invisible frame,
diff --git a/src/nsterm.m b/src/nsterm.m
index a9280eb..0729c96 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -7282,7 +7282,7 @@ not_in_argv (NSString *arg)
             old_title = t;
           }
         size_title = xmalloc (strlen (old_title) + 40);
-       esprintf (size_title, "%s  —  (%d x %d)", old_title, cols, rows);
+       esprintf (size_title, "%s  —  (%d × %d)", old_title, cols, rows);
         [window setTitle: [NSString stringWithUTF8String: size_title]];
         [window display];
         xfree (size_title);
diff --git a/src/pdumper.c b/src/pdumper.c
index e0f8f55..1a7aee6 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -2709,7 +2709,7 @@ dump_hash_table (struct dump_context *ctx,
 static dump_off
 dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer)
 {
-#if CHECK_STRUCTS && !defined HASH_buffer_5DC36DBD42
+#if CHECK_STRUCTS && !defined HASH_buffer_EE36B4292E
 # error "buffer changed. See CHECK_STRUCTS comment in config.h."
 #endif
   struct buffer munged_buffer = *in_buffer;
diff --git a/src/search.c b/src/search.c
index e7f9094..4eb634a 100644
--- a/src/search.c
+++ b/src/search.c
@@ -3031,6 +3031,23 @@ If optional arg RESEAT is non-nil, make markers on LIST 
point nowhere.  */)
   return Qnil;
 }
 
+DEFUN ("match-data--translate", Fmatch_data__translate, Smatch_data__translate,
+       1, 1, 0,
+       doc: /* Add N to all string positions in the match data.  Internal.  */)
+  (Lisp_Object n)
+{
+  CHECK_FIXNUM (n);
+  EMACS_INT delta = XFIXNUM (n);
+  if (EQ (last_thing_searched, Qt))   /* String match data only.  */
+    for (ptrdiff_t i = 0; i < search_regs.num_regs; i++)
+      if (search_regs.start[i] >= 0)
+        {
+          search_regs.start[i] = max (0, search_regs.start[i] + delta);
+          search_regs.end[i] = max (0, search_regs.end[i] + delta);
+        }
+  return Qnil;
+}
+
 /* Called from Flooking_at, Fstring_match, search_buffer, Fstore_match_data
    if asynchronous code (filter or sentinel) is running. */
 static void
@@ -3388,6 +3405,7 @@ is to bind it with `let' around a small expression.  */);
   defsubr (&Smatch_end);
   defsubr (&Smatch_data);
   defsubr (&Sset_match_data);
+  defsubr (&Smatch_data__translate);
   defsubr (&Sregexp_quote);
   defsubr (&Snewline_cache_check);
 
diff --git a/src/w32fns.c b/src/w32fns.c
index 7bb9689..a840f0e 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -4448,7 +4448,10 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM 
lParam)
          int size, i;
          W32Msg wmsg;
          HIMC context = get_ime_context_fn (hwnd);
-         wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
+         wmsg.dwModifiers =
+           w32_ignore_modifiers_on_IME_input
+           ? 0
+           : w32_get_key_modifiers (wParam, lParam);
          /* Get buffer size.  */
          size = get_composition_string_fn (context, GCS_RESULTSTR, NULL, 0);
          buffer = alloca (size);
@@ -10614,6 +10617,15 @@ tip frame.  */);
               doc: /* Non-nil means don't display the abort dialog when 
aborting.  */);
   w32_disable_abort_dialog = 0;
 
+  DEFVAR_BOOL ("w32-ignore-modifiers-on-IME-input",
+              w32_ignore_modifiers_on_IME_input,
+              doc: /* Whether to ignore modifier keys when processing input 
with IME.
+Some MS-Windows input methods use modifier keys such as Ctrl or Alt to input
+characters, in which case applying the modifiers will change the input.
+The default value of this variable is therefore t, to ignore modifier
+keys when IME input is received.  */);
+  w32_ignore_modifiers_on_IME_input = true;
+
 #if 0 /* TODO: Port to W32 */
   defsubr (&Sx_change_window_property);
   defsubr (&Sx_delete_window_property);
diff --git a/src/xdisp.c b/src/xdisp.c
index e49cc43..76ef420 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -1796,6 +1796,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int 
*x, int *y,
                     from a display vector, we need to consume all of
                     the glyphs from that display vector.  */
                  start_display (&it2, w, top);
+                 it2.glyph_row = NULL;
                  move_it_to (&it2, charpos - 1, -1, -1, -1, MOVE_TO_POS);
                  /* If we didn't get to CHARPOS - 1, there's some
                     replacing display property at that position, and
@@ -1919,6 +1920,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int 
*x, int *y,
                     of the display line where the display string
                     begins.  */
                  start_display (&it3, w, top);
+                 it3.glyph_row = NULL;
                  move_it_to (&it3, -1, 0, top_y, -1, MOVE_TO_X | MOVE_TO_Y);
                  /* If it3_moved stays false after the 'while' loop
                     below, that means we already were at a newline
diff --git a/test/lisp/custom-tests.el b/test/lisp/custom-tests.el
index 7691f16..232e3be 100644
--- a/test/lisp/custom-tests.el
+++ b/test/lisp/custom-tests.el
@@ -165,4 +165,42 @@
     (enable-theme 'custom--test)
     (should (equal settings (get 'custom--test 'theme-settings)))))
 
+(defcustom custom--test-local-option 'initial
+  "Buffer-local user option for testing."
+  :group 'emacs
+  :type '(choice (const initial) (const changed))
+  :local t)
+
+(defcustom custom--test-permanent-option 'initial
+  "Permanently local user option for testing."
+  :group 'emacs
+  :type '(choice (const initial) (const changed))
+  :local 'permanent)
+
+(ert-deftest custom-test-local-option ()
+  "Test :local user options."
+  ;; Initial default values.
+  (should (eq custom--test-local-option 'initial))
+  (should (eq custom--test-permanent-option 'initial))
+  (should (eq (default-value 'custom--test-local-option) 'initial))
+  (should (eq (default-value 'custom--test-permanent-option) 'initial))
+  (let ((obuf (current-buffer)))
+    (with-temp-buffer
+      ;; Changed buffer-local values.
+      (setq custom--test-local-option 'changed)
+      (setq custom--test-permanent-option 'changed)
+      (should (eq custom--test-local-option 'changed))
+      (should (eq custom--test-permanent-option 'changed))
+      (should (eq (default-value 'custom--test-local-option) 'initial))
+      (should (eq (default-value 'custom--test-permanent-option) 'initial))
+      (with-current-buffer obuf
+        (should (eq custom--test-local-option 'initial))
+        (should (eq custom--test-permanent-option 'initial)))
+      ;; Permanent variable remains unchanged.
+      (kill-all-local-variables)
+      (should (eq custom--test-local-option 'initial))
+      (should (eq custom--test-permanent-option 'changed))
+      (should (eq (default-value 'custom--test-local-option) 'initial))
+      (should (eq (default-value 'custom--test-permanent-option) 'initial)))))
+
 ;;; custom-tests.el ends here
diff --git a/test/lisp/emacs-lisp/ert-tests.el 
b/test/lisp/emacs-lisp/ert-tests.el
index 9618935..1f54c8d 100644
--- a/test/lisp/emacs-lisp/ert-tests.el
+++ b/test/lisp/emacs-lisp/ert-tests.el
@@ -801,6 +801,11 @@ This macro is used to test if macroexpansion in `should' 
works."
     (should (eql 0 (ert-stats-completed-unexpected stats)))
     (should (eql 1 (ert-stats-skipped stats)))))
 
+(ert-deftest ert-test-with-demoted-errors ()
+  "Check that ERT correctly handles `with-demoted-errors'."
+  :expected-result :failed  ;; FIXME!  Bug#11218
+  (should-not (with-demoted-errors (error "Foo"))))
+
 
 (provide 'ert-tests)
 
diff --git a/test/lisp/files-x-tests.el b/test/lisp/files-x-tests.el
index 9db1983..6b05e6a 100644
--- a/test/lisp/files-x-tests.el
+++ b/test/lisp/files-x-tests.el
@@ -274,7 +274,8 @@
         (should-not (local-variable-p 'remote-shell-file-name))
         (should-not (boundp 'remote-shell-file-name))))))
 
-(defvar tramp-connection-local-default-profile)
+(defvar tramp-connection-local-default-shell-variables)
+(defvar tramp-connection-local-default-system-variables)
 
 (ert-deftest files-x-test-with-connection-local-variables ()
   "Test setting connection-local variables."
@@ -335,7 +336,10 @@
           (append
            (nreverse (copy-tree files-x-test--variables3))
            (nreverse (copy-tree files-x-test--variables2))
-            (nreverse (copy-tree tramp-connection-local-default-profile)))))
+            (nreverse
+             (copy-tree tramp-connection-local-default-shell-variables))
+            (nreverse
+             (copy-tree tramp-connection-local-default-system-variables)))))
          ;; The variables exist also as local variables.
          (should (local-variable-p 'remote-shell-file-name))
          (should (local-variable-p 'remote-null-device))
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index 00d08ea..eeb838b 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -4466,7 +4466,9 @@ This tests also `make-symbolic-link', `file-truename' and 
`add-name-to-file'."
              (setq proc (start-file-process "test4" (current-buffer) nil))
              (should (processp proc))
              (should (equal (process-status proc) 'run))
-             (should (stringp (process-tty-name proc)))))
+             ;; On MS Windows, `process-tty-name' returns nil.
+             (unless (tramp--test-windows-nt-p)
+               (should (stringp (process-tty-name proc))))))
 
        ;; Cleanup.
        (ignore-errors (delete-process proc))))))
diff --git a/test/lisp/progmodes/compile-tests.el 
b/test/lisp/progmodes/compile-tests.el
index 0288cba..74d7c76 100644
--- a/test/lisp/progmodes/compile-tests.el
+++ b/test/lisp/progmodes/compile-tests.el
@@ -124,6 +124,8 @@
     ;; cucumber
     (cucumber "Scenario: undefined step  # features/cucumber.feature:3"
      29 nil 3 "features/cucumber.feature")
+    ;; This rule is actually handled by the `cucumber' pattern but when
+    ;; `omake' is included, then `gnu' matches it first.
     (gnu "      /home/gusev/.rvm/foo/bar.rb:500:in `_wrap_assertion'"
      1 nil 500 "/home/gusev/.rvm/foo/bar.rb")
     ;; edg-1 edg-2
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index c3dfd27..9573b98 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -484,5 +484,95 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350.";
 
   (should-error (string-replace "" "x" "abc")))
 
+(ert-deftest subr-replace-regexp-in-string ()
+  (should (equal (replace-regexp-in-string "a+" "xy" "abaabbabaaba")
+                 "xybxybbxybxybxy"))
+  ;; FIXEDCASE
+  (let ((case-fold-search t))
+    (should (equal (replace-regexp-in-string "a+" "xy" "ABAABBABAABA")
+                   "XYBXYBBXYBXYBXY"))
+    (should (equal (replace-regexp-in-string "a+" "xy" "ABAABBABAABA" t)
+                   "xyBxyBBxyBxyBxy"))
+    (should (equal (replace-regexp-in-string
+                    "a[bc]*" "xyz"
+                    "a A ab AB Ab aB abc ABC Abc AbC aBc")
+                   "xyz XYZ xyz XYZ Xyz xyz xyz XYZ Xyz Xyz xyz"))
+    (should (equal (replace-regexp-in-string
+                    "a[bc]*" "xyz"
+                    "a A ab AB Ab aB abc ABC Abc AbC aBc" t)
+                   "xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz")))
+  (let ((case-fold-search nil))
+    (should (equal (replace-regexp-in-string "a+" "xy" "ABAABBABAABA")
+                   "ABAABBABAABA")))
+  ;; group substitution
+  (should (equal (replace-regexp-in-string
+                  "a\\(b*\\)" "<\\1,\\&>" "babbcaabacbab")
+                 "b<bb,abb>c<,a><b,ab><,a>cb<b,ab>"))
+  (should (equal (replace-regexp-in-string
+                  "x\\(?2:..\\)\\(?1:..\\)\\(..\\)\\(..\\)\\(..\\)"
+                  "<\\3,\\5,\\4,\\1,\\2>" "yxabcdefghijkl")
+                 "y<ef,ij,gh,cd,ab>kl"))
+  ;; LITERAL
+  (should (equal (replace-regexp-in-string
+                  "a\\(b*\\)" "<\\1,\\&>" "babbcaabacbab" nil t)
+                 "b<\\1,\\&>c<\\1,\\&><\\1,\\&><\\1,\\&>cb<\\1,\\&>"))
+  (should (equal (replace-regexp-in-string
+                  "a" "\\\\,\\?" "aba")
+                 "\\,\\?b\\,\\?"))
+  (should (equal (replace-regexp-in-string
+                  "a" "\\\\,\\?" "aba" nil t)
+                 "\\\\,\\?b\\\\,\\?"))
+  ;; SUBEXP
+  (should (equal (replace-regexp-in-string
+                  "\\(a\\)\\(b*\\)c" "xy" "babbcdacd" nil nil 2)
+                 "baxycdaxycd"))
+  ;; START
+  (should (equal (replace-regexp-in-string
+                  "ab" "x" "abcabdabeabf" nil nil nil 4)
+                 "bdxexf"))
+  ;; An empty pattern matches once before every character.
+  (should (equal (replace-regexp-in-string "" "x" "abc")
+                 "xaxbxc"))
+  (should (equal (replace-regexp-in-string "y*" "x" "abc")
+                 "xaxbxc"))
+  ;; replacement function
+  (should (equal (replace-regexp-in-string
+                  "a\\(b*\\)c"
+                  (lambda (s)
+                    (format "<%s,%s,%s,%s,%s>"
+                            s
+                            (match-beginning 0) (match-end 0)
+                            (match-beginning 1) (match-end 1)))
+                  "babbcaacabc")
+                 "b<abbc,0,4,1,3>a<ac,0,2,1,1><abc,0,3,1,2>"))
+  ;; anchors (bug#15107, bug#44861)
+  (should (equal (replace-regexp-in-string "a\\B" "b" "a aaaa")
+                 "a bbba"))
+  (should (equal (replace-regexp-in-string "\\`\\|x" "z" "--xx--")
+                 "z--zz--")))
+
+(ert-deftest subr-tests--change-group-33341 ()
+  (with-temp-buffer
+    (buffer-enable-undo)
+    (insert "0\n")
+    (let ((g (prepare-change-group)))
+      (activate-change-group g)
+      (insert "b\n")
+      (insert "c\n")
+      (cancel-change-group g))
+    (should (equal (buffer-string) "0\n"))
+    (erase-buffer)
+    (setq buffer-undo-list nil)
+    (insert "0\n")
+    (let ((g (prepare-change-group)))
+      (activate-change-group g)
+      (insert "b\n")
+      (insert "c\n")
+      (accept-change-group g))
+    (should (equal (buffer-string) "0\nb\nc\n"))
+    (undo-boundary)
+    (undo)
+    (should (equal (buffer-string) ""))))
+
 (provide 'subr-tests)
 ;;; subr-tests.el ends here
diff --git a/test/lisp/wid-edit-tests.el b/test/lisp/wid-edit-tests.el
index 4508b68..1bd4297 100644
--- a/test/lisp/wid-edit-tests.el
+++ b/test/lisp/wid-edit-tests.el
@@ -148,4 +148,157 @@
       ;; Check that we effectively moved the item to the last position.
       (should (equal (widget-value lst) '("beg" "middle" "end"))))))
 
+(ert-deftest widget-test-choice-match-no-inline ()
+  "Test that a no-inline choice widget can match its values."
+  (let* ((choice '(choice (const nil) (const t) string function))
+         (widget (widget-convert choice)))
+    (should (widget-apply widget :match nil))
+    (should (widget-apply widget :match t))
+    (should (widget-apply widget :match ""))
+    (should (widget-apply widget :match 'ignore))))
+
+(ert-deftest widget-test-choice-match-all-inline ()
+  "Test that a choice widget with all inline members can match its values."
+  (let* ((lst '(list (choice (list :inline t symbol number)
+                             (list :inline t symbol regexp))))
+         (widget (widget-convert lst)))
+    (should-not (widget-apply widget :match nil))
+    (should (widget-apply widget :match '(:test 2)))
+    (should (widget-apply widget :match '(:test ".*")))
+    (should-not (widget-apply widget :match '(:test ignore)))))
+
+(ert-deftest widget-test-choice-match-some-inline ()
+  "Test that a choice widget with some inline members can match its values."
+  (let* ((lst '(list string
+                     (choice (const t)
+                             (list :inline t symbol number)
+                             (list :inline t symbol regexp))))
+         (widget (widget-convert lst)))
+    (should-not (widget-apply widget :match nil))
+    (should (widget-apply widget :match '("" t)))
+    (should (widget-apply widget :match '("" :test 2)))
+    (should (widget-apply widget :match '("" :test ".*")))
+    (should-not (widget-apply widget :match '(:test ignore)))))
+
+(ert-deftest widget-test-inline-p ()
+  "Test `widget-inline-p'.
+For widgets without an :inline t property, `widget-inline-p' has to return nil.
+But if the widget is a choice widget, it has to return nil if passed nil as
+the bubblep argument, or non-nil if one of the members of the choice widget has
+an :inline t property and we pass a non-nil bubblep argument.  If no members of
+the choice widget have an :inline t property, then `widget-inline-p' has to
+return nil, even with a non-nil bubblep argument."
+  (with-temp-buffer
+    (widget-insert "Testing.\n\n")
+    (let* ((widget (widget-create 'repeat
+                                  :value '(nil)
+                                  '(choice (const nil) (const t)
+                                           (list :inline t symbol number))
+                                  '(choice (const nil) (const t)
+                                           (list function string))))
+           (children (widget-get widget :children))
+           (child-1 (car children))
+           (child-2 (cadr children)))
+      (should-not (widget-inline-p widget))
+      (should-not (widget-inline-p child-1))
+      (should (widget-inline-p child-1 'bubble))
+      (should-not (widget-inline-p child-2))
+      (should-not (widget-inline-p child-2 'bubble)))))
+
+(ert-deftest widget-test-repeat-can-handle-choice ()
+  "Test that we can create a repeat widget with a choice correctly."
+  (with-temp-buffer
+    (widget-insert "Testing.\n\n")
+    (let* ((widget (widget-create 'repeat
+                                  :entry-format "%i %d %v"
+                                  :value '((:test 2))
+                                  '(choice (const nil) (const t)
+                                           (list symbol number))))
+           (child (car (widget-get widget :children))))
+      (widget-insert "\n")
+      (use-local-map widget-keymap)
+      (widget-setup)
+      (should child)
+      (should (equal (widget-value widget) '((:test 2)))))))
+
+(ert-deftest widget-test-repeat-can-handle-inlinable-choice ()
+  "Test that we can create a repeat widget with an inlinable choice correctly."
+  (with-temp-buffer
+    (widget-insert "Testing.\n\n")
+    (let* ((widget (widget-create 'repeat
+                                  :entry-format "%i %d %v"
+                                  :value '(:test 2)
+                                  '(choice (const nil) (const t)
+                                           (list :inline t symbol number))))
+           (child (widget-get widget :children)))
+      (widget-insert "\n")
+      (use-local-map widget-keymap)
+      (widget-setup)
+      (should child)
+      (should (equal (widget-value widget) '(:test 2))))))
+
+(ert-deftest widget-test-list-can-handle-choice ()
+  "Test that we can create a list widget with a choice correctly."
+  (with-temp-buffer
+    (widget-insert "Testing.\n\n")
+    (let* ((widget (widget-create 'list
+                                  :value '((1 "One"))
+                                  '(choice string
+                                           (list number string))))
+           (child (car (widget-get widget :children))))
+      (widget-insert "\n")
+      (use-local-map widget-keymap)
+      (widget-setup)
+      (should child)
+      (should (equal (widget-value widget) '((1 "One")))))))
+
+(ert-deftest widget-test-list-can-handle-inlinable-choice ()
+  "Test that we can create a list widget with an inlinable choice correctly."
+  (with-temp-buffer
+    (widget-insert "Testing.\n\n")
+    (let* ((widget (widget-create 'list
+                                  :value '(1 "One")
+                                  '(choice string
+                                           (list :inline t number string))))
+           (child (car (widget-get widget :children))))
+      (widget-insert "\n")
+      (use-local-map widget-keymap)
+      (widget-setup)
+      (should child)
+      (should (equal (widget-value widget) '(1 "One"))))))
+
+(ert-deftest widget-test-option-can-handle-choice ()
+  "Test that we can create a option widget with a choice correctly."
+  (with-temp-buffer
+    (widget-insert "Testing.\n\n")
+    (let* ((widget (widget-create 'repeat
+                                  :value '(("foo"))
+                                  '(list (option
+                                          (choice string
+                                                  (list :inline t
+                                                        number string))))))
+           (child (car (widget-get widget :children))))
+      (widget-insert "\n")
+      (use-local-map widget-keymap)
+      (widget-setup)
+      (should child)
+      (should (equal (widget-value widget) '(("foo")))))))
+
+(ert-deftest widget-test-option-can-handle-inlinable-choice ()
+  "Test that we can create a option widget with an inlinable choice correctly."
+  (with-temp-buffer
+    (widget-insert "Testing.\n\n")
+    (let* ((widget (widget-create 'repeat
+                                  :value '((1 "One"))
+                                  '(list (option
+                                          (choice string
+                                                  (list :inline t
+                                                        number string))))))
+           (child (car (widget-get widget :children))))
+      (widget-insert "\n")
+      (use-local-map widget-keymap)
+      (widget-setup)
+      (should child)
+      (should (equal (widget-value widget) '((1 "One")))))))
+
 ;;; wid-edit-tests.el ends here
diff --git a/test/src/emacs-module-resources/mod-test.c 
b/test/src/emacs-module-resources/mod-test.c
index 258a679..4196212 100644
--- a/test/src/emacs-module-resources/mod-test.c
+++ b/test/src/emacs-module-resources/mod-test.c
@@ -691,6 +691,14 @@ Fmod_test_identity (emacs_env *env, ptrdiff_t nargs, 
emacs_value *args,
   return args[0];
 }
 
+static emacs_value
+Fmod_test_funcall (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
+                   void *data)
+{
+  assert (0 < nargs);
+  return env->funcall (env, args[0], nargs - 1, args + 1);
+}
+
 /* Lisp utilities for easier readability (simple wrappers).  */
 
 /* Provide FEATURE to Emacs.  */
@@ -780,6 +788,8 @@ emacs_module_init (struct emacs_runtime *ert)
   DEFUN ("mod-test-function-finalizer-calls",
          Fmod_test_function_finalizer_calls, 0, 0, NULL, NULL);
   DEFUN ("mod-test-async-pipe", Fmod_test_async_pipe, 1, 1, NULL, NULL);
+  DEFUN ("mod-test-funcall", Fmod_test_funcall, 1, emacs_variadic_function,
+         NULL, NULL);
 
 #undef DEFUN
 
diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el
index fb4ed4a..99d4caf 100644
--- a/test/src/emacs-module-tests.el
+++ b/test/src/emacs-module-tests.el
@@ -506,4 +506,54 @@ See Bug#36226."
     (should (not (multibyte-string-p (mod-test-return-unibyte))))
     (should (equal result "foo\x00zot"))))
 
+(cl-defstruct (emacs-module-tests--variable
+               (:constructor nil)
+               (:constructor emacs-module-tests--make-variable
+                             (name
+                              &aux
+                              (mutex (make-mutex name))
+                              (condvar (make-condition-variable mutex name))))
+               (:copier nil))
+  "A variable that's protected by a mutex."
+  value
+  (mutex nil :read-only t :type mutex)
+  (condvar nil :read-only t :type condition-variable))
+
+(defun emacs-module-tests--wait-for-variable (variable desired)
+  (with-mutex (emacs-module-tests--variable-mutex variable)
+    (while (not (eq (emacs-module-tests--variable-value variable) desired))
+      (condition-wait (emacs-module-tests--variable-condvar variable)))))
+
+(defun emacs-module-tests--change-variable (variable new)
+  (with-mutex (emacs-module-tests--variable-mutex variable)
+    (setf (emacs-module-tests--variable-value variable) new)
+    (condition-notify (emacs-module-tests--variable-condvar variable) :all)))
+
+(ert-deftest emacs-module-tests/interleaved-threads ()
+  (let* ((state-1 (emacs-module-tests--make-variable "1"))
+         (state-2 (emacs-module-tests--make-variable "2"))
+         (thread-1
+          (make-thread
+           (lambda ()
+             (emacs-module-tests--change-variable state-1 'before-module)
+             (mod-test-funcall
+              (lambda ()
+                (emacs-module-tests--change-variable state-1 'in-module)
+                (emacs-module-tests--wait-for-variable state-2 'in-module)))
+             (emacs-module-tests--change-variable state-1 'after-module))
+           "thread 1"))
+         (thread-2
+          (make-thread
+           (lambda ()
+             (emacs-module-tests--change-variable state-2 'before-module)
+             (emacs-module-tests--wait-for-variable state-1 'in-module)
+             (mod-test-funcall
+              (lambda ()
+                (emacs-module-tests--change-variable state-2 'in-module)
+                (emacs-module-tests--wait-for-variable state-1 'after-module)))
+             (emacs-module-tests--change-variable state-2 'after-module))
+           "thread 2")))
+    (thread-join thread-1)
+    (thread-join thread-2)))
+
 ;;; emacs-module-tests.el ends here



reply via email to

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